Talos Vulnerability Report


Nitro PRO PDF nested pages remote code execution vulnerability

May 18, 2020
CVE Number



An exploitable code execution vulnerability exists in the PDF parser of Nitro Pro 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.

Tested Versions

Nitro Pro

Product URLs


CVSSv3 Score

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:
Product version:

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.

        curr_page_number = 0;
        result = PDDocGetNumPages(v2);
        num_pages = result;
        if ( !result )
            num_pages = 1;
            v3 = 1;
        if ( num_pages > 0 )
                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);
            } while ( curr_page_number < num_pages );

In the case of nested pages, it is possible to free this page in the overarching PDF object.

    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.


.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.