Microsoft Office PowerPoint 2010 - MSO/OART Heap Out-of-Bounds Access









Platform: Microsoft Office 2010 on Windows 7 x86
Class: Time of check time of use leading to memory corruption

The following crash was observed in Microsoft Office 2010 running under Windows 7 x86 with Application Verifier enabled. This crash is non-deterministic and will not reproduce in all instances but the crash demonstrated a high degree of reliability. 

Attached files:
910494862.ppt: fuzzed crashing file

File versions:
mso.dll: 14.0.7173.5000
oart.dll: 14.0.7169.5000
ppcore.dll: 14.0.7173.5000

(510.66c): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=1a6f0fb0 ebx=3c782fc4 ecx=1a53cfe0 edx=000004bf esi=1a53cfe0 edi=1a4d6fc0
eip=66acdf93 esp=0013d8b0 ebp=0013d8bc iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00210202
66acdf93 f6465804        test    byte ptr [esi+58h],4       ds:0023:1a53d038=??

0:000> uf 0x66acdf8b
66acdf8b 55              push    ebp
66acdf8c 8bec            mov     ebp,esp
66acdf8e 51              push    ecx
66acdf8f 51              push    ecx
66acdf90 56              push    esi
66acdf91 8bf1            mov     esi,ecx
=> 66acdf93 f6465804        test    byte ptr [esi+58h],4

Call Stack:

0:000> kb
ChildEBP RetAddr  Args to Child              
WARNING: Stack unwind information not available. Following frames may be wrong.
0013d8bc 66ba7720 00000000 1a9d6f98 66ad3d33 mso!Ordinal4899+0xd33
0013d948 67908f0d 1a996e30 1a9d6f98 0000001a mso!Ordinal4720+0x201
0013d980 67906400 0013d9fc 679063f4 0013d9fc oart!Ordinal7979+0x35
0013d994 67908f30 2cccaf58 0013d9fc 0013d9cc oart!Ordinal2490+0x10b
0013d9a4 677e2a14 0013d9fc 1a4d6fd8 1a984ff0 oart!Ordinal7979+0x58
0013d9cc 677e2999 1a4d6ff0 0013d9fc 0013da0c oart!Ordinal6+0xc4
0013d9dc 6788730f 0013d9fc 3a5fe1a5 1a554f8c oart!Ordinal6+0x49
0013da0c 68c8e465 3c782fc4 3a5ff871 68b7e504 oart!Ordinal1989+0xaa
0013da44 68c985dd 3a5fc635 0013e4b4 68b8661c ppcore!PPMain+0x9130c
0013e400 68d0540f 00000000 3c886ea0 00000001 ppcore!PPMain+0x9b484

In this crash the pointer being dereferenced in esi is being tested for a flag value. However, the pointer is referencing invalid memory generating an access violation. The esi value came from the ecx register which is presumably the this pointer. Previous chunk at esi-0x58 is valid memory but 0x58 is beyond that allocated size of that chunk:

0:000> !heap -p -a 19841038
    address 19841038 found in
    _DPH_HEAP_ROOT @ 11a1000
    in busy allocation (  DPH_HEAP_BLOCK:         UserAddr         UserSize -         VirtAddr         VirtSize)
                                197f1d9c:         19840fe0               20 -         19840000             2000
    70588e89 verifier!AVrfDebugPageHeapAllocate+0x00000229
    778c616e ntdll!RtlDebugAllocateHeap+0x00000030
    7788a08b ntdll!RtlpAllocateHeap+0x000000c4
    77855920 ntdll!RtlAllocateHeap+0x0000023a
    710ead1a vrfcore!VerifierSetAPIClassName+0x000000aa
    6d7b16ac vfbasics+0x000116ac
    60b20233 mso!Ordinal9052+0x0000713f
    67808744 oart!Ordinal2033+0x00000090
    678086ab oart!Ordinal6561+0x000000ac
    6781af9f oart!Ordinal5870+0x00000060

Looking at the calling function:

0:000> uf 0x66ba76ef
66ba76ef 56              push    esi
66ba76f0 8bf1            mov     esi,ecx
66ba76f2 e8a7ddfaff      call    mso!Ordinal8038+0x461 (66b5549e) ; first call
66ba76f7 85c0            test    eax,eax
66ba76f9 7427            je      mso!Ordinal4720+0x203 (66ba7722)

66ba76fb 8bce            mov     ecx,esi
66ba76fd e89cddfaff      call    mso!Ordinal8038+0x461 (66b5549e) ; second call
66ba7702 83781400        cmp     dword ptr [eax+14h],0
66ba7706 741a            je      mso!Ordinal4720+0x203 (66ba7722)

66ba7708 8bce            mov     ecx,esi
66ba770a e88fddfaff      call    mso!Ordinal8038+0x461 (66b5549e) ; third call
66ba770f 8b4014          mov     eax,dword ptr [eax+14h]
66ba7712 8b4810          mov     ecx,dword ptr [eax+10h] ; crashing ecx value
66ba7715 85c9            test    ecx,ecx
66ba7717 7413            je      mso!Ordinal4720+0x20d (66ba772c)

66ba7719 6a00            push    0
66ba771b e86b68f2ff      call    mso!Ordinal4899+0xd2b (66acdf8b) ; crashing function
66ba7720 5e              pop     esi
66ba7721 c3              ret

66ba7722 f6465804        test    byte ptr [esi+58h],4 ; same check as crashing function
66ba7726 7404            je      mso!Ordinal4720+0x20d (66ba772c)

66ba7728 8bce            mov     ecx,esi
66ba772a ebed            jmp     mso!Ordinal4720+0x1fa (66ba7719)

66ba772c b8ff0f0000      mov     eax,0FFFh
66ba7731 5e              pop     esi
66ba7732 c3              ret

Looking at the logic flow from this function we see at the very first call to mso!Ordinal8038+0x461 must return a non-null value or else the same check in the crashing function is performed in the calling function. With a non-null return this same function is called again only this time the value at [eax+0x14h] is checked to be non-null. If this second check passed then the we call the same function a third time! This time we follow the pointer at [[eax+0x14]+0x10] and check it to be non-null before passing it to the crashing function. Given the repeating calls to the same function and the non-determinism of the bug I suspect this is a time of check time of use bug on the object implementing these methods. 

Proof of Concept: