A specifically crafted jpeg2000 file embedded in a PDF file can lead to a heap corruption when opening a PDF document in NitroPDF 126.96.36.1992. With careful memory manipulation, this can lead to arbitrary code execution. In order to trigger this vulnerability, the victim would need to open the malicious file.
8.8 - CVSS:3.0/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H
CWE-122: Heap Based Buffer Overflow
A potential remote code execution vulnerability exists in the PDF parsing functionality of Nitro Pro. A specially crafted PDF file can cause a vulnerability resulting in potential code execution.
While parsing an embedded jpeg2000 file with an unusually large
ssizDepth value in
siz block of the code stream, an incorrectly sized allocation is made. Later, while further processing the file, improper checks lead to a write access of out of bounds memory on the heap. This can lead to following crash:
(a0e8.9a74): Access violation - code c0000005 (first chance) First chance exceptions are reported before any exception handling. This exception may be expected and handled. *** ERROR: Symbol file could not be found. Defaulted to export symbols for C:\Program Files\Nitro\Pro\12\npdf.dll - npdf!CosCryptGetVersion+0x8961: 00007fff`a27df4e1 418800 mov byte ptr [r8],al ds:000001e0`0766f000=?? 0:000> !heap -p -a r8 address 000001e00766f000 found in _DPH_HEAP_ROOT @ 1e050631000 in busy allocation ( DPH_HEAP_BLOCK: UserAddr UserSize - VirtAddr VirtSize) 1e0068c2958: 1e00766a720 48d8 - 1e00766a000 6000 00007fffdcc8f4bf ntdll!RtlDebugAllocateHeap+0x000000000000003f 00007fffdcc3b530 ntdll!RtlpAllocateHeap+0x000000000008f760 00007fffdcba9725 ntdll!RtlpAllocateHeapInternal+0x00000000000005e5 00007fffc8a86a57 MSVCR120!malloc+0x000000000000005b [f:\dd\vctools\crt\crtw32\heap\malloc.c @ 92] 00007fffc8a86967 MSVCR120!operator new+0x000000000000001f [f:\dd\vctools\crt\crtw32\heap\new.cpp @ 59] 00007fffa27e090c npdf!CosCryptGetVersion+0x0000000000009d8c 00007fffa27e1a77 npdf!CosCryptGetVersion+0x000000000000aef7 00007fffa27e37be npdf!CosCryptGetVersion+0x000000000000cc3e 00007fffa27e2fed npdf!CosCryptGetVersion+0x000000000000c46d 00007fffa27e4cfa npdf!CosCryptGetVersion+0x000000000000e17a 00007fffa27e4b9d npdf!CosCryptGetVersion+0x000000000000e01d 00007fffa27e4912 npdf!CosCryptGetVersion+0x000000000000dd92 00007fffa271c0b8 npdf!CosStreamSetStm+0x0000000000000468 00007fffa271bb09 npdf!CosStreamOpenStm+0x0000000000000009 00007fffa26787a5 npdf!PDTextIsSpaceBetween+0x00000000000fa305 00007fffa25fb38d npdf!PDTextIsSpaceBetween+0x000000000007ceed 00007fffa2685584 npdf!PDTextIsSpaceBetween+0x00000000001070e4 00007fffa255ae10 npdf!init_npdf_optional_features+0x0000000000007be0 00007fffa255d9d6 npdf!init_npdf_optional_features+0x000000000000a7a6
As can be seen from the above PageHeap output, we are accessing out of bounds memory. The size of the allocated chunk is 0x48d8. The size of the calculation happens in function
sub_180420480, specifically in the following code:
.text:000000018042089D mov rax, [rbp+20A0h+var_1900] .text:00000001804208A4 mov ecx, [rax+0Ch] .text:00000001804208A7 imul ecx, edx .text:00000001804208AA imul ecx, [rdi+8] .text:00000001804208AE add ecx, 7 .text:00000001804208B1 shr ecx, 3 .text:00000001804208B4 mov rax, [rbp+20A0h+var_10C0] .text:00000001804208BB imul ecx, [rax] .text:00000001804208BE test ecx, ecx .text:00000001804208C0 jle loc_180420
In the above code, the initial value in
rax+0xc actually comes from
sub_18048DF20, where it is calculated based on
xrsiz values from the
siz block. The whole equation is roughly as follows:
buffer_size = (((((xsiz / xrsiz)) * (ssizdepth) * 3 ) + 7 ) >> 3 ) * 3
The above value is subsequently sign-extended before being used in an allocation function.
The relevant part in our sample testcase that triggers this vulnerability is as follows:
<siz> <lsiz>47</lsiz> <rsiz>ISO/IEC 15444-1</rsiz> <xsiz>16506</xsiz> <ysiz>148</ysiz> <xOsiz>0</xOsiz> <yOsiz>0</yOsiz> <xTsiz>2</xTsiz> <yTsiz>42</yTsiz> <xTOsiz>0</xTOsiz> <yTOsiz>0</yTOsiz> <numberOfTiles>33012</numberOfTiles> <csiz>3</csiz> <ssizSign>unsigned</ssizSign> <ssizDepth>74</ssizDepth> <xRsiz>74</xRsiz> <yRsiz>72</yRsiz> <ssizSign>unsigned</ssizSign> <ssizDepth>73</ssizDepth> <xRsiz>72</xRsiz> <yRsiz>72</yRsiz> <ssizSign>unsigned</ssizSign> <ssizDepth>73</ssizDepth> <xRsiz>72</xRsiz> <yRsiz>72</yRsiz> </siz>
The actual overflow happens in function
sub_18041F3D0 which has three nested loops guarded by different counters. Depending on the values, index into the allocated buffer can be incremented more than once per innermost loop and eventually leads to out of bounds access on the heap, overwriting adjacent memory.
By carefully controlling other image size values in the codestream, memory region of precise size can be allocated and by controlling the content of the image this overflow can be abused to overwrite adjacent heap memory which can ultimately lead to arbitrary code execution.
2019-05-07 - Vendor disclosure
2019-07-02 - 60 day follow up
2019-07-29 - 2nd follow up (90 days approaching notice)
2019-08-06 - 3rd follow up
2019-08-07 - Vendor acknowledged & advised prior emails went to spam folder; Talos issued copy of report
2019-09-03 - Talos granted disclosure extension to 2019-09-10
2019-09-05 - Vendor advised issues will be addressed in a future release (timeline unknown)
2019-10-09 - Public Disclosure
Discovered by Aleksandar Nikolic of Cisco Talos.