|
Microsoft GdiPlus.dll EMF GpFont::SetData Stack Overflow
Write up by redsand@blacksecurity.org Credits to mIKEJONES for providing the .EMF Crash
An integer overflow has been found within the Microsoft Windows gdiplus.dll [0x4ED67060]
This vulnerability currently allows us to write 64 bytes of either NULL's to the stack or UNICODE characters we control, inadvertently overwriting our stacks return addresses as well as the stack canary. We’ve decided to call this the Microsoft GdiPlus EMF GpFont.SetData integer overflow.
One known vulnerable version of GdiPlus.dll is: x86_Microsoft.Widnows.GdiPlus_6595b64144ccf1df_1.0.2600.5581_x-ww_dfbc4fc4
Below is our confirmed call stack after the stack overflow:
GdiPlus!GpFont::SetData+0xf6 GdiPlus!MetafilePlayer::AddObject+0x8f GdiPlus!ObjectEPR::Play+0x1a GdiPlus!GdipPlayMetafileRecordCallback+0x35 GdiPlus!MetafilePlayer::EnumerateEmfPlusRecords+0x66 GdiPlus!EnumEmfWithDownLevel+0x52 gdi32!bInternalPlayEMF+0x6b0 GdiPlus!MetafilePlayer::EnumerateEmfRecords+0xd7 GdiPlus!GpGraphics::EnumEmfPlusDual+0x27d GdiPlus!GpMetafile::EnumerateForPlayback+0x686 GdiPlus!GpMetafile::Play+0x26 GdiPlus!GpGraphics::DrawImage+0x263
We decided to pull this up using a fancy version of OllyDbg and begin digging around. With that said, this is what we found:
You will note at address 0x4ECFFA33 MOV ESI,DWORD PTR DS:[EAX+14] We control the value loaded into ESI. For this purpose we use a value greater than 0xFE000000. This integer can be located at the offset 0xC8 (200 bytes) within the given example.
Next you will see we are able to control the value loaded into ECX. For the ESI value of 0xFFFFFFFF (largest unsigned integer) we load the value of 0x16 into our register (for example: if value is 0xEEEEEEEE, our value becomes 0xDDDDDDF4 and becomes greater than 0x16 and is marked as an invalid image) 4ECFFA36 LEA ECX, DWORD PTR DS:[ESI+ESI+18]
This value is increased by 0x18 (24) bytes: 4ECFFA3A ADD EAX, 18
Then compared with a value offset of EBP, which is located on our stack. For this purpose the value stored in ECX is 0x16: 4ECFFA3D CMP DWORD PTR SS:[EBP+C],ECX
This means the jmp is not taken 4ECFFA40 JB gdiplus.4ECFFAC9
Our controlled value is compared with the hex value 0x20 (32) 4ECFFA46 CMP ESI,20
And this jump will also never be taken. 4ECFFA49 JBE SHORT gdiplus.4ECFFA4E
From here, we begin to setup our stack for the __stdcall calling approach to our function gdiplus.4ED67060
4ECFFA4B PUSH 20 4ECFFA4D POP ESI 4ECFFA4E PUSH ESI 4ECFFA4F PUSH EAX 4ECFFA50 LEA EAX,DWORD PTR SS:[EBP-44] 4ECFFA53 PUSH EAX
This is the function in which the stack overwrite actually occurs. 4ECFFA54 CALL gdiplus.4ED67060
Let’s step into our function gdiplus.4ED67060:
First we have a loop setup to go through our Unicode Font String (ARIAL) and copy it (letter by letter) to another location on the stack controlled by EDI. This approach would be the natural code execution process however after further review, we're able to determine we can utilize the length of the font name (ARIAL) and extend it to 32 total WORD bytes (since this is Unicode, that's 64 bytes total). This will eat up our allocated memory and will halt the need to zero out the rest of the buffer. Stack corruption occurs either here or below.
4ED67069 XOR ESI,ESI 4ED6706B TEST ECX,ECX 4ED6706D JBE SHORT gdiplus.4ED6709C 4ED6706F MOV EDX,DWORD PTR SS:[EBP+C] 4ED67072 PUSH EDI 4ED67073 MOV EDI,DWORD PTR SS:[EBP+8] 4ED67076 MOV AX,WORD PTR DS:[EDX] 4ED67079 TEST AX,AX 4ED6707C JE SHORT gdiplus.4ED6708A 4ED6707E MOV WORD PTR DS:[EDI],AX 4ED67081 INC EDI 4ED67082 INC EDI 4ED67083 INC EDX 4ED67084 INC EDX 4ED67085 INC ESI 4ED67086 CMP ESI,ECX 4ED67088 JB SHORT gdiplus.4ED67076
Once our string copy has been completed, we check to see if our wide character string length of our font (ARIAL) is less than 0x20 (32) characters
4ED6708A CMP ESI,ECX 4ED6708C JNB SHORT gdiplus.4ED6709B
The length of our string (ESI) is subtracted into 0x20 (32) to get the remainder (0x1B if using just the font name ARIAL) and is left in the register ECX 4ED6708E SUB ECX,ESI
Here we clear our value for eax to make it zero 4ED67090 XOR EAX,EAX
Then we shift the value of our remainder 0x1B (27) right one position which leaves us with 0x0D (13) 4ED67092 SHR ECX,1
This is where our overflow occurs; EDI points to our stack just past the location where we copied our font string on the stack earlier.
4ED67094 REP STOS DWORD PTR ES:[EDI]
This is what our stack looks like at that location when we begin overwriting it with zeros. 0007E344 |7C900000 ..�| ntdll.7C900000 0007E348 |7C9101C0 À‘| ntdll.7C9101C0 0007E34C |FFFFFFFF ÿÿÿÿ 0007E350 |7C9101BB »‘| RETURN to ntdll.7C9101BB from ntdll.7C90E8E6 0007E354 |4EC5221F "ÅN RETURN to gdiplus.4EC5221F from ntdll.RtlAllocateHeap
The rest below does not concern us as there is little we can leverage from this.
4ED67096 ADC ECX,ECX 4ED67098 REP STOS WORD PTR ES:[EDI] 4ED6709B POP EDI 4ED6709C POP ESI 4ED6709D POP EBP 4ED6709E RETN 0C
Our call stack doesn't begin to be corrupted until several functions later when our nulls come up to be pop'd into EIP from a RETN from ESP.
When we return execution back to 0x4ECFFA86, our valid returns on the stack have been used, leaving us with a corrupted stack scenario.
Below is a snapshot of our stack before the overflow occurs:
0007E2B0 |003B0041 A.;. 0007E2B4 |0007E0AC ¬à . 0007E2B8 |00000000 .... 0007E2BC |0007ED40 @í . 0007E2C0 |7C90E900 .é�| ntdll.7C90E900 0007E2C4 |7C9101C0 À‘| ntdll.7C9101C0 0007E2C8 |FFFFFFFF ÿÿÿÿ 0007E2CC |7C9101BB »‘| RETURN to ntdll.7C9101BB from ntdll.7C90E8E6 0007E2D0 |4EC5221F "ÅN RETURN to gdiplus.4EC5221F from ntdll.RtlAllocateHeap 0007E2D4 |003B0000 ..;. 0007E2D8 |40000060 `..@ 0007E2DC |0000001C ... 0007E2E0 |0007E2F0 ðâ . 0007E2E4 |4EC9DD15 ÝÉN RETURN to gdiplus.4EC9DD15 from gdiplus.4EC52209 0007E2E8 |0000001C ... 0007E2EC |00000006 ... 0007E2F0 |00009C45 Eœ.. 0007E2F4 ]0007E318 ã . 0007E2F8 |4EC9E783 ƒçÉN RETURN to gdiplus.4EC9E783
(Again, but more details):
0007E294 B90105D5 Õ¹ 0007E298 00000000 .... 0007E29C 00000000 .... 0007E2A0 00000000 .... 0007E2A4 003B4108 A;. 0007E2A8 018E0F08 Ž 0007E2AC 003B5108 Q;. 0007E2B0 003B0000 ..;. 0007E2B4 0007E0AC ¬à . 0007E2B8 00000000 .... 0007E2BC 0007ED40 @í . 0007E2C0 7C90E900 .é�| ntdll.7C90E900 0007E2C4 7C9101C0 À‘| ntdll.7C9101C0 0007E2C8 FFFFFFFF ÿÿÿÿ 0007E2CC 7C9101BB »‘| RETURN to ntdll.7C9101BB from ntdll.7C90E8E6 0007E2D0 4EC5221F "ÅN RETURN to gdiplus.4EC5221F from ntdll.RtlAllocateHeap 0007E2D4 003B0000 ..;. 0007E2D8 40000060 `..@ 0007E2DC 0000001C ... 0007E2E0 0007E2F0 ðâ . 0007E2E4 4EC9DD15 ÝÉN RETURN to gdiplus.4EC9DD15 from gdiplus.4EC52209 0007E2E8 0000001C ... 0007E2EC 00000006 ... 0007E2F0 000091F7 ÷‘..
Below is a snapshot of our stack after the overflow occurs:
ESP+4 > 003B4108 A;. ESP+8 > 018E0F08 Ž ESP+C > 003B5108 Q;. ESP+10 > 001D0B0B . ESP+14 > 001D0B0B . ESP+18 > 001D0B0B . ESP+1C > 001D0B0B . ESP+20 > 001D0B0B . ESP+24 > 001D0B0B . ESP+28 > 001D0B0B . ESP+2C > 001D0B0B . ESP+30 > 001D0B0B . ESP+34 > 001D0B0B . ESP+38 > 001D0B0B . ESP+3C > 001D0B0B . ESP+40 > 001D0B0B . ESP+44 > 001D0B0B . ESP+48 > 001D0B0B . ESP+4C > 001D0B0B . ESP+50 > 00000000 .... ESP+54 >/0007E318 ã . ESP+58 >|4EC9E783 ƒçÉN RETURN to gdiplus.4EC9E783
After our function has completed, an internal gdiplus function looks for stack corruption. At 0x4ECFDA78, GdiPlus sets our unhandled exception filter to zero (clears it) and then calls our unhandled exception filter, then immediately terminates the process. This is the standard process for handling detected stack overflows.
CALL gdiplus.4ECFD994 Cookie Security Check
Thanks,
- redsand
Example .EMF File can be found here
http://milw0rm.com/sploits/2009-voltage-exploit.emf
|