CVE-2020-6074
An exploitable code execution vulnerability exists in the PDF parser of Nitro Pro 13.9.1.155. A specially crafted PDF document can cause a use-after-free which can lead to remote code execution. An attacker can provide a malicious file to trigger this vulnerability.
Nitro Pro 13.9.1.155
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
Nitro PDF allows users to save, read, sign and edit PDF files on their machines.
The modules analyzed for this vulnerability are:
Mapped memory image file: C:\Program Files\Nitro\Pro\13\NitroPDF.exe
Image path: C:\Program Files\Nitro\Pro\13\NitroPDF.exe
Image name: NitroPDF.exe
Browse all global symbols functions data
Timestamp: Thu Dec 19 05:46:43 2019 (5DFB6323)
CheckSum: 00932421
ImageSize: 00934000
File version: 13.9.1.155
Product version: 13.9.1.155
During the parsing of a PDF, a buffer is allocated for each page in the PDF. These pages are stored in a larger PDF object.
NitroPDF+0x98a5a
curr_page_number = 0;
result = PDDocGetNumPages(v2);
num_pages = result;
if ( !result )
{
num_pages = 1;
v3 = 1;
}
if ( num_pages > 0 )
{
do
{
v7 = operator new(0xB8ui64);
memset(v7, 0, 0xB8ui64);
*((_DWORD *)v7 + 0x27) = curr_page_number;
*((_BYTE *)v7 + 0x98) = v3;
sub_98850(v1, (__int64)v7, curr_page_number, 0);
// Saving the allocated page in the larger PDF object
result = (*(__int64 (__fastcall **)(__int64, void **))(*(_QWORD *)(v1 + 0x5A8) + 72i64))(v1 + 0x5A8, &v7);
++curr_page_number;
} while ( curr_page_number < num_pages );
}
In the case of nested pages, it is possible to free this page in the overarching PDF object.
NitroPDF+0x9c13d
if ( (_DWORD)curr_pages == 1 )
{
page_struct = (_BYTE *)sub_96E30(v10, 0i64, 1u);
if ( page_struct )
{
if ( page_struct[0x98] )
{
LODWORD(curr_pages) = 0;
// Freeing the original page allocated for the PDF
((void (__cdecl *)(void *, int))j_j_free)(page_struct, 0xB8);
*(_QWORD *)(v10 + 0x5B8) = *(_QWORD *)(v10 + 0x5B0);
}
}
}
With this page freed, a crafted PDF can cause an exception to occur. One way is to claim to have more pages in the PDF than given via the /Kids tag. An example of what causes this exception is below:
3 0 obj
<<
/Count 2
/Kids [ ]
>>
During the handling of this exception, a set of cleanup code is called for the main PDF object. In this code, various buffers are cleared, including the original buffer allocation for our page in the PDF.
NitroPDF+3154b6
.text:00000000003154B6 mov [rsp+38h+var_28], rdx
.text:00000000003154BB push rbp
.text:00000000003154BC sub rsp, 30h
.text:00000000003154C0 mov rbp, rdx
.text:00000000003154C3 mov rax, [rbp+78h]
.text:00000000003154C7 mov qword ptr [rax], 0
.text:00000000003154CE mov byte ptr [rax+98h], 1
.text:00000000003154D5 lea rax, unk_988FB
.text:00000000003154DC add rsp, 30h
.text:00000000003154E0 pop rbp
.text:00000000003154E1 retn
While the page was freed, it was never cleared from the original PDF object. A carefully crafted PDF could possibly setup the heap in a way to write an arbitrary null byte out of bounds which can lead to further memory corruption and possibly arbitrary code execution.
2020-02-17 - Vendor Disclosure
2020-05-18 - Public Release
Discovered by Cory Duplantis of Cisco Talos.