Microsoft Office PowerPoint 2010 - GDI 'GDI32!ConvertDxArray' Insufficient Bounds Check









Platform: GDI on Windows 7 x86 reachable from Microsoft Office 2010
Class: Out of bounds memory access

The following crash was observed in Microsoft Office 2010 running under Windows 7 x86 with Application Verifier enabled. 

Attached files:
2167705722.ppt: fuzzed crashing file

File versions:
gdi32.dll: 6.1.7601.23457
gdiplus.dll: 6.1.7601.23508
gfx.dll: 14.0.7104.5000
oart.dll: 14.0.7169.5000

(788.ca0): Access violation - code c0000005 (first chance)
eax=00000000 ebx=0747bc5c ecx=00000001 edx=16ab9fd8 esi=1c45dcb8 edi=223e3000
eip=77667a68 esp=1c45dc78 ebp=1c45dc84 iopl=0         nv up ei pl nz ac pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00010216
77667a68 8b07            mov     eax,dword ptr [edi]  ds:0023:223e3000=????????

0:014> kb
ChildEBP RetAddr  Args to Child              
1c45dc84 7765a2b3 000003a8 0747bc5c 223e2ff0 GDI32!ConvertDxArray+0x3c
1c45e6dc 776442e7 1f210a94 0000001b 00000093 GDI32!MF_ExtTextOut+0x3b4
1c45ec48 776405dc 1f210a94 0000001b 00000093 GDI32!ExtTextOutInternalA+0x156
1c45ec74 7764969c 1f210a94 0000001b 00000093 GDI32!ExtTextOutA+0x24
1c45ed5c 7764e40f 1f210a94 0ab42fc8 0747bc42 GDI32!PlayMetaFileRecord+0x1bc7
1c45ede0 7764e441 21464dc0 0000000c 00000000 GDI32!CommonEnumMetaFile+0x24d
1c45edf8 741fb1c0 1f210a94 2a260a92 7764438a GDI32!PlayMetaFile+0x1f
1c45ee60 741fb65b 2a260a92 43b405d9 46123597 GdiPlus!GetEmfFromWmfData+0x420
1c45ee84 741fb768 2a260a92 1c45eec8 00000000 GdiPlus!GpMetafile::InitWmf+0xb2
1c45eea0 741fea9f 2a260a92 1c45eec8 00000000 GdiPlus!GpMetafile::GpMetafile+0x3b
1c45eef8 741ff642 19a0cd28 1c45efbc 00000000 GdiPlus!GpMetafile::ConvertToEmfPlus+0x79
1c45ef1c 741d4fc2 19a0cd28 1c45efbc 00000004 GdiPlus!GpMetafile::ConvertToEmfPlus+0x1d
1c45ef58 6b388b58 19a0cd28 1999ef28 1c45efbc GdiPlus!GdipConvertToEmfPlus+0xbf
1c45efd4 6b36f2f4 19a0cd28 00000000 1fd76f56 gfx!Ordinal841+0x12250
1c45f004 678980c2 1c45f07c 1c45f024 1fd75519 gfx!Ordinal745+0x34
1c45f090 67897d68 1c45f0e8 07430f28 21408fe0 oart!Ordinal7931+0x6d0
1c45f104 677e340d 07430f28 1c45f124 67805b69 oart!Ordinal7931+0x376
1c45f110 67805b69 1c45f2c8 1c45f1b0 6b24cceb oart!Ordinal3235+0x14a

The function GDI32!ConvertDxArray is called with codepage 936 (ANSI/OEM Simplified Chinese [PRC, Singapore]; Chinese Simplified [GB2312]) a length of 4 (DWORDs) and a source contents containing 0x00000010 0x00000000 0x00000010 0x00000000. There are two paths in this function, one that operates on 4 byte boundaries and one that operates on 8 byte boundaries depending on the last argument where true indicates an 8-byte boundary and false indicates a 4-byte boundary. Both paths have the same issue. Pseudocode for one path in the function is:

  else if ( (unsigned int)current < result )
    cur_dest = (unsigned int *)dest;
    cur_src = (unsigned int *)src;
      dbcs_ret = IsDBCSLeadByteEx(CodePage, *current++);
      dbcs_flag = dbcs_ret == 0;
      tmp = *cur_src;
      *cur_dest = tmp;
      if ( !dbcs_flag )
        tmp = *cur_src; // crash here
        *cur_dest += tmp;
    while ( (unsigned int)current < end );

The issue here is that when dbcs_flag is false the 4 byte boundary version can actually process 8 bytes of the source buffer (cur_src is incremented twice) and the 8 byte version is capable of processing 16 bytes per iteration. The length checks in this function do not verify this behavior to be in bounds. However, the most likely exploitation scenario will be a memory disclosure because cur_dest is not written to out of bounds. The value in tmp is instead added to the contents of cur_dest. 

Proof of Concept: