Source: https://bugs.chromium.org/p/project-zero/issues/detail?id=950
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
mso!Ordinal4899+0xd33:
66acdf93 f6465804 test byte ptr [esi+58h],4 ds:0023:1a53d038=??
0:000> uf 0x66acdf8b
mso!Ordinal4899+0xd2b:
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
mso!Ordinal4720+0x1d0:
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)
mso!Ordinal4720+0x1dc:
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)
mso!Ordinal4720+0x1e9:
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)
mso!Ordinal4720+0x1fa:
66ba7719 6a00 push 0
66ba771b e86b68f2ff call mso!Ordinal4899+0xd2b (66acdf8b) ; crashing function
66ba7720 5e pop esi
66ba7721 c3 ret
mso!Ordinal4720+0x203:
66ba7722 f6465804 test byte ptr [esi+58h],4 ; same check as crashing function
66ba7726 7404 je mso!Ordinal4720+0x20d (66ba772c)
mso!Ordinal4720+0x209:
66ba7728 8bce mov ecx,esi
66ba772a ebed jmp mso!Ordinal4720+0x1fa (66ba7719)
mso!Ordinal4720+0x20d:
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:
https://gitlab.com/exploit-database/exploitdb-bin-sploits/-/raw/main/bin-sploits/41418.zip