Talos Vulnerability Report


Adobe Acrobat Reader Javascript event.richValue use-after-free vulnerability

January 11, 2022
CVE Number



A use-after-free vulnerability exists in the way certain events are handled in Adobe Acrobat Reader 21.007.20091. A specially-crafted javascript code can exploit a use-after-free vulnerability which can lead to arbitrary code execution. User would need to open a malicious file to trigger the vulnerability.

Tested Versions

Adobe Acrobat Reader 21.007.20091

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-416 - Use After Free


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.

There exists a vulnerability in the way Adobe Reader DC handles event object from inside event handlers, which can lead to a use-after-free vulnerability.

The following line inside the Keystroke event handler installed for a text field triggers the vulnerability.

event.richValue = app.activeDocs[0];

The text field present on the page has fields richText and defaultValue, set with arbitrary strings.

When resetForm(true) is called on app.activeDocs[0], the Keystroke action is triggered and the installed event handler is executed.

Interpreting value assignment to the property richValue inside an event handler results in calling function sub_20A93600 in AcroForm.api. Following the call graph, it eventually calls sub_20AD1C86, which, based on assumption, checks the type of the object given as an argument and calls function in EScript.api by passing a function pointer (sub_20AD2160).

In the context of the poc, the type is Doc, which is the type of assigning value app.activeDocs[0].

.text:20AD20AE                 lea     eax, [ebp+pExceptionObject]
.text:20AD20B1                 push    eax             ; wchar_t *
.text:20AD20B2                 mov     eax, data_21470978
.text:20AD20B7                 push    offset sub_20AD2160 ; wchar_t *
.text:20AD20BC                 mov     esi, [eax+0D0h]
.text:20AD20C2 loc_20AD20C2:                           ; CODE XREF: ObjectTypeHandle+421↑j
.text:20AD20C2                 push    ebx             ; wchar_t *
.text:20AD20C3                 mov     ecx, esi
.text:20AD20C5                 call    ds:___guard_check_icall_fptr
.text:20AD20CB                 call    esi             ; Calls a function defined in EScript.api
.text:20AD20CD                 add     esp, 0Ch

The EScript.api function sub_1005D780 visits all the properties defined in that Doc object and calls sub_20AD2160 each time. sub_20AD2160 calls sub_20AD62BB, which returns the pointer to offset +0x18 of a 0x48-sized heap memory. The pointer returned from this function at the time of handling the “event” property of Doc object is later accessed after being freed.

sub_20AA69DF is the actual function that returns an allocated memory block of size 0x48.

.text:20AA69DF                 push    4
.text:20AA69E1                 mov     eax, offset loc_20F6C0C2
.text:20AA69E6                 call    __EH_prolog3
.text:20AA69EB                 mov     esi, ecx
.text:20AA69ED                 mov     [ebp+var_10], esi
.text:20AA69F0                 mov     eax, [ebp+arg_0]
.text:20AA69F3                 mov     [esi], eax
.text:20AA69F5                 and     [ebp+var_4], 0
.text:20AA69F9                 and     dword ptr [esi+4], 0
.text:20AA69FD                 push    48h ; 'H'              ; Allocation size
.text:20AA69FF                 call    sub_2085EE36    ; Returns allocated memory address
.text:20AA6A04                 mov     [esi+4], eax
.text:20AA6A07                 mov     eax, esi
.text:20AA6A09                 pop     ecx
.text:20AA6A0A                 call    __EH_epilog3
.text:20AA6A0F                 retn    4

Here is the stack trace when allocation happens.

0:000> k
 # ChildEBP RetAddr      
00 04afa3e4 6df4ee50     ucrtbase!_malloc_base
WARNING: Stack unwind information not available. Following frames may be wrong.
01 04afa3f0 6e196a04     AcroForm+0x5ee50
02 04afa41c 6e1c5186     AcroForm!hb_ot_tags_to_script_and_language+0x76164
03 04afa434 6e1c56ea     AcroForm!DllUnregisterServer+0x99f6
04 04afa478 6e1c62f5     AcroForm!DllUnregisterServer+0x9f5a
05 04afa4b4 6e1c2190     AcroForm!DllUnregisterServer+0xab65
06 04afa4d0 6dc7d8b2     AcroForm!DllUnregisterServer+0x6a00
07 04afa524 6dc7d793     EScript!mozilla::HashBytes+0x4bb62
08 04afa53c 6f897d33     EScript!mozilla::HashBytes+0x4ba43
09 04afa554 6e1c20cd     AcroRd32!DllCanUnloadNow+0x9d913
0a 04afa59c 6e1c237f     AcroForm!DllUnregisterServer+0x693d
0b 04afa5c4 6e1c222a     AcroForm!DllUnregisterServer+0x6bef
0c 04afa604 6e18378b     AcroForm!DllUnregisterServer+0x6a9a
0d 04afa628 6dc5f71b     AcroForm!hb_ot_tags_to_script_and_language+0x62eeb

Continuing execution of sub_20AD2160, it then calls sub_20AD22A9 which eventually calls sub_20AD1C86 again with the type of object it handles being Event. Repeating what was done before with the Doc object, it triggers function sub_20A92E80 , which is a function that is called when reading value of richValue property defined in the Event object, before executing the usual sub_20AD2160.

sub_20A92E80 frees the object mentioned earlier with the stack trace like the following.

0:000> k
 # ChildEBP RetAddr      
00 04af9ed4 6df4f9ea     ucrtbase!free+0x13
WARNING: Stack unwind information not available. Following frames may be wrong.
01 04af9ee0 6e1c5635     AcroForm+0x5f9ea
02 04af9f04 6e1c558f     AcroForm!DllUnregisterServer+0x9ea5
03 04af9f1c 6e1c5584     AcroForm!DllUnregisterServer+0x9dff
04 04af9f34 6e1c5584     AcroForm!DllUnregisterServer+0x9df4
05 04af9f4c 6e1c5584     AcroForm!DllUnregisterServer+0x9df4
06 04af9f64 6e1c5584     AcroForm!DllUnregisterServer+0x9df4
07 04af9f7c 6e1c5584     AcroForm!DllUnregisterServer+0x9df4
08 04af9f94 6e1c5584     AcroForm!DllUnregisterServer+0x9df4
09 04af9fac 6e1c5584     AcroForm!DllUnregisterServer+0x9df4
0a 04af9fc4 6e1c5584     AcroForm!DllUnregisterServer+0x9df4
0b 04af9fdc 6e1c5584     AcroForm!DllUnregisterServer+0x9df4
0c 04af9ff4 6e1c5584     AcroForm!DllUnregisterServer+0x9df4
0d 04afa00c 6e1c5584     AcroForm!DllUnregisterServer+0x9df4
0e 04afa024 6e1c5584     AcroForm!DllUnregisterServer+0x9df4
0f 04afa03c 6e1c5584     AcroForm!DllUnregisterServer+0x9df4
10 04afa054 6e1c554b     AcroForm!DllUnregisterServer+0x9df4
11 04afa078 6e1c71e7     AcroForm!DllUnregisterServer+0x9dbb
12 04afa0cc 6e1c6c89     AcroForm!DllUnregisterServer+0xba57
13 04afa0d8 6e1c6d9d     AcroForm!DllUnregisterServer+0xb4f9
14 04afa10c 6e1c12de     AcroForm!DllUnregisterServer+0xb60d
15 04afa140 6e1c1320     AcroForm!DllUnregisterServer+0x5b4e
16 04afa16c 6e182f1e     AcroForm!DllUnregisterServer+0x5b90
17 04afa1a4 6dc5e0f6     AcroForm!hb_ot_tags_to_script_and_language+0x6267e

When it executes sub_20AD2160 after, edi register stores a dangling pointer.

.text:20AD2160 sub_20AD2160 proc near                  ; DATA XREF: ObjectTypeHandle+431↑o
.text:20AD2160                                         ; .rdata:20FEF197↓o
.text:20AD2160 arg_4           = dword ptr  0Ch
.text:20AD2160 arg_8           = dword ptr  10h
.text:20AD2160 arg_C           = dword ptr  14h
.text:20AD2160                 push    ebp
.text:20AD2161                 mov     ebp, esp
.text:20AD2163                 mov     eax, [ebp+arg_C]
.text:20AD2166                 push    esi             ; uintptr_t
.text:20AD2167                 push    edi             ; unsigned int
.text:20AD2168                 push    0               ; wchar_t *
.text:20AD216A                 mov     edi, [eax]      ; edi is a dangling pointer
.text:20AD216C                 mov     eax, dword_214724C4
.text:20AD2171                 push    [ebp+arg_8]     ; wchar_t *
.text:20AD2174                 push    [ebp+arg_4] ; wchar_t *
.text:20AD2177                 mov     esi, [eax+14h]
.text:20AD217A                 mov     ecx, esi
.text:20AD217C                 call    ds:___guard_check_icall_fptr
.text:20AD2182                 call    esi
.text:20AD2184                 pop     ecx
.text:20AD2185                 movzx   eax, ax
.text:20AD2188                 mov     ecx, edi
.text:20AD218A                 push    eax             ; wchar_t *
.text:20AD218B                 call    sub_20AD62BB    ; calls this function
.text:20AD2190                 push    eax             ; int
.text:20AD2191                 call    sub_20AD22A9
.text:20AD2196                 add     esp, 0Ch
.text:20AD2199                 xor     eax, eax
.text:20AD219B                 inc     eax
.text:20AD219C                 pop     edi
.text:20AD219D                 pop     esi
.text:20AD219E                 pop     ebp
.text:20AD219E Property__sub_20AD2160 endp

The pointer stored in edi is copied to ecx register and sub_20AD62BB is called. Here use-after-free happens.

.text:20AD62BB                 push    10h             ; wchar_t *
.text:20AD62BD                 mov     eax, offset loc_20F709F6
.text:20AD62C2                 call    __EH_prolog3
.text:20AD62C7                 mov     edi, ecx
.text:20AD62C9                 mov     eax, [edi]      ; use-after-free
.text:20AD62CB                 sub     eax, 13h
.text:20AD62CE                 jz      short is_13h

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.


2021-10-14 - Vendor Disclosure
2022-01-11 - Public Release


Discovered by Jaewon Min and Aleksandar Nikolic of Cisco Talos.