NULL pointer dereference in Windows GDI bParseWin32MetaFile

TL;DR

A denial of service vulnerability exists when Windows GDI improperly handles objects in memory. Converting a specially crafted EMF file to a WMF may trigger a read access violation due to a NULL pointer dereference and could allow denial of service.

Description

It seems that calling Metafile::EmfToWmfBits() method on a specially crafted EMF file may lead to memory corruption triggered by bGetNextRecord() called by the bParseWin32Metafile() function. The below is the relevant excerpt of the crash analysis from WinDbg:

 1FAULTING_IP:
 2gdiplus!bGetNextRecord+34e6f
 3747ae398 8b4104          mov     eax,dword ptr [ecx+4]
 4
 5EXCEPTION_RECORD:  (.exr -1)
 6ExceptionAddress: 747ae398 (gdiplus!bGetNextRecord+0x00034e6f)
 7   ExceptionCode: c0000005 (Access violation)
 8   ExceptionFlags: 00000000
 9NumberParameters: 2
10   Parameter[0]: 00000000
11   Parameter[1]: 00000004
12Attempt to read from address 00000004

The Metafile::EmfToWmfBits() method converts an enhanced-format metafile to a Windows Metafile Format (WMF) metafile and stores the converted records in a specified buffer.

1Metafile::EmfToWmfBits(hEmf, 0, NULL, MM_TEXT, EmfToWmfBitsFlagsDefault);

Calling this method on the provided EMF file in MM_TEXT mapping mode and the EmfToWmfBitsFlagsDefault options for the conversion will trigger a reproducable read access violation due to a NULL pointer dereference.

PoC

The below PoC code can demonstrate the problem:

1HENHMETAFILE hEmf = GetEnhMetaFileW(L"PoC.emf");
2Metafile::EmfToWmfBits(hEmf, 0, NULL, MM_TEXT, EmfToWmfBitsFlagsDefault);
3
4DeleteEnhMetaFile(hEmf);

Upon compiling it and starting with the attached file in the current working directory, the expected crash is generated in bGetNextRecord():

 1(2214.30f8): Access violation - code c0000005 (first chance)
 2First chance exceptions are reported before any exception handling.
 3This exception may be expected and handled.
 4eax=00000001 ebx=00000001 ecx=00000000 edx=00effa58 esi=07f53e10 edi=00000000
 5eip=747ae398 esp=00effa3c ebp=00effa5c iopl=0         nv up ei pl zr na pe nc
 6cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00010246
 7gdiplus!bGetNextRecord+0x34e6f:
 8747ae398 8b4104          mov     eax,dword ptr [ecx+4] ds:002b:00000004=????????
 9
100:000> kb
11 # ChildEBP RetAddr  Args to Child              
1200 00effa5c 747792ba 07f51130 00001000 00000074 gdiplus!bGetNextRecord+0x34e6f
1301 00effa74 747791e9 00000000 00000000 00000001 gdiplus!GdipConvertEmfToWmf+0x91
1402 00effab0 74778ed3 00000000 00000001 00000000 gdiplus!GdipGetWinMetaFileBitsEx+0x160
1503 00effb60 74778e64 00000000 00000001 00000000 gdiplus!ConvertEmfToPlaceableWmf+0x5f
1604 00effb78 00371807 d7462c87 00000000 00000000 gdiplus!GdipEmfToWmfBits+0x24
1705 00effc60 003718d6 d7462c87 00000000 00000000 ParseWin32Metafile!Gdiplus::Metafile::EmfToWmfBits+0x37 [C:\Program Files (x86)\Windows Kits\10\Include\10.0.18362.0\um\GdiplusMetafile.h @ 380] 
1806 00effd54 00371fe3 00000001 07f45fa0 07ecbf20 ParseWin32Metafile!main+0x46 

An access violation exception happened at 0x4 while attempting to read memory at 0x4 using a NULL pointer. The bug has been reproduced on a fully patched Windows 10 64-bit with a 32-bit PoC program loading version 10.0.18362.959 (x86) of gdi32full.dll, but the 64-bit build of gdi32full.dll also seems to be affected.

Timeline

⬅️ 2020-07-23: Reported issue to MSRC.
➡️ 2020-07-23: MSRC opened case 60123.
➡️ 2020-08-03: MSRC indicated that this is a moderate issue and won’t be fixed.
➡️ 2020-08-25: Coordinated public release of advisory.

References

Last updated:
Tags: DoS, EMF