CVE-2021-44710
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.
Adobe Acrobat Reader 21.007.20091
Acrobat Reader - 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 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
.text:20AD2160 arg_4 = dword ptr 0Ch
.text:20AD2160 arg_8 = dword ptr 10h
.text:20AD2160 arg_C = dword ptr 14h
.text:20AD2160
.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.