CVE-2020-6116
An arbitrary code execution vulnerability exists in the rendering functionality of Nitro Software, Inc.’s Nitro Pro 13.13.2.242. When drawing the contents of a page using colors from an indexed colorspace, the application can miscalculate the size of a buffer when allocating space for its colors. When using this allocated buffer, the application can write outside its bounds and cause memory corruption which can lead to code execution. A specially crafted document must be loaded by a victim in order to trigger this vulnerability.
Nitro Pro 13.13.2.242
Nitro Pro 13.16.2.300
https://www.gonitro.com/nps/product-details/downloads
8.8 - CVSS:3.0/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H
CWE-680 - Integer Overflow to Buffer Overflow
Nitro Software, Inc. includes their flagship product, Nitro Pro as part of their Nitro Productivity Suite. Nitro Pro is Nitro Software’s PDF editor and flagship product. This product allows users to create and modify documents that follow the Portable Document Format (PDF) specification and other digital documents.
Nitro Software Inc. develops commercial software used to create, edit, sign, and secure Portable Document Format files and digital documents. This is supported by their Nitro Pro application as part of their Nitro Productivity Suite. The Nitro Pro application allows users to read, modify, and create documents that follow the Portable Document Format standard. When creating a page for a document, the creator is allowed to specify the colorspace to use when drawing the page’s different components. One of the available colorspaces is the “Indexed” colorspace which allows the creator to include an indexed color palette in the document in order to use for coloring the different parts of a page. When the application renders the page, it will allocate space for the indexed color palette and load colors into the allocated space. Due to an integer overflow, the application can miscalculate the size of the indexed palette resulting in an undersized buffer. Later when loading colors into this buffer, a buffer overflow will occur.
When the application is rendering a page, it must interpret a number of commands in order to build the contents of the page. When a colorspace is chosen by the contents of the page, the following function is executed. This function will first identify what type of colorspace was selected by the creator of the page. This is done by first checking the colorspace name agained the “/ICCBased” atom. First, the function will load the 64-bit number representing the “ICCBased” atom. If this is uninitialized, the application will pass the string to the ASAtomFromString
function at [1]. Afterwards at [2], the application will pass a string to the “ICCBased” atom and then compare it against the atom representing the selected ColorSpace. As this advisory involves the “/Indexed” colorspace, the application will continue on and check the next colorspace type. Next, the application will load the 64-bit integer representing the “Indexed” atom. If this has not been initialized yet, at [3] the application will pass the “Indexed” string to the ASAtomFromString
function in order to convert it to an atom.
npdf!PDBookmarkGetCosObj+0xbd74:
5af13314 8b0dc8604f5b mov ecx,dword ptr [npdf!CAPContent::`vftable'+0x1399d8 (5b4f60c8)] ; "ICCBased" atom
5af1331a 8b15cc604f5b mov edx,dword ptr [npdf!CAPContent::`vftable'+0x1399dc (5b4f60cc)] ; "ICCBased" atom
5af13320 8945fc mov dword ptr [ebp-4],eax
5af13323 8bc1 mov eax,ecx
5af13325 23c2 and eax,edx
5af13327 c68573ffffff01 mov byte ptr [ebp-8Dh],1
5af1332e c645fc01 mov byte ptr [ebp-4],1
5af13332 83f8ff cmp eax,0FFFFFFFFh
5af13335 751c jne npdf!PDBookmarkGetCosObj+0xbdb3 (5af13353)
...
5af13337 ff35c0604f5b push dword ptr [npdf!CAPContent::`vftable'+0x1399d0 (5b4f60c0)] ; "ICCBased" string
5af1333d e8de31f3ff call npdf!ASAtomFromString (5ae46520) ; [1] ASAtomFromString
5af13342 8bc8 mov ecx,eax
5af13344 8915cc604f5b mov dword ptr [npdf!CAPContent::`vftable'+0x1399dc (5b4f60cc)],edx ; Store atom
5af1334a 83c404 add esp,4
5af1334d 890dc8604f5b mov dword ptr [npdf!CAPContent::`vftable'+0x1399d8 (5b4f60c8)],ecx ; Store atom
...
5af13353 8b7e1c mov edi,dword ptr [esi+1Ch] ; Selected ColorSpace atom
5af13356 394e18 cmp dword ptr [esi+18h],ecx
5af13359 0f851f020000 jne npdf!PDBookmarkGetCosObj+0xbfde (5af1357e) ; [2] Branch to check of ColorSpace type
5af1335f 3bfa cmp edi,edx
5af13361 0f8517020000 jne npdf!PDBookmarkGetCosObj+0xbfde (5af1357e) ; [2] Branch to check of ColorSpace type
...
npdf!PDBookmarkGetCosObj+0xbfde:
5af1357e a1d8604f5b mov eax,dword ptr [npdf!CAPContent::`vftable'+0x1399e8 (5b4f60d8)] ; "Indexed" atom
5af13583 8b15dc604f5b mov edx,dword ptr [npdf!CAPContent::`vftable'+0x1399ec (5b4f60dc)] ; "Indexed" atom
5af13589 898574ffffff mov dword ptr [ebp-8Ch],eax
5af1358f 23c2 and eax,edx
5af13591 83f8ff cmp eax,0FFFFFFFFh
5af13594 751e jne npdf!PDBookmarkGetCosObj+0xc014 (5af135b4)
...
5af13596 ff35d0604f5b push dword ptr [npdf!CAPContent::`vftable'+0x1399e0 (5b4f60d0)] ; "Indexed" string
5af1359c e87f2ff3ff call npdf!ASAtomFromString (5ae46520) ; [3] ASAtomFromString
5af135a1 a3d8604f5b mov dword ptr [npdf!CAPContent::`vftable'+0x1399e8 (5b4f60d8)],eax
5af135a6 83c404 add esp,4
5af135a9 8915dc604f5b mov dword ptr [npdf!CAPContent::`vftable'+0x1399ec (5b4f60dc)],edx
5af135af 8b7e1c mov edi,dword ptr [esi+1Ch] ; load high 32-bits of selected colorspace
5af135b2 eb06 jmp npdf!PDBookmarkGetCosObj+0xc01a (5af135ba)
After loading the 64-bit integer for the “/Indexed” atom, the application will execute the following code. This code will first load the selected colorspace into the %ecx
register. This is done so that the selected atom can be compared against the “/Indexed” atom at [4]. After confirming the colorspace is of the “/Indexed” type, the application will then call CosObjGetType
to verify that the array for the colorspace is defined. At [6], the CosArrayGet
function is used to get the third element of the array. According to the PDF file format specification, the third element is named hival
and represents the maximum indexed of the described colorspace. After fetching the maximum index of the colorspace, at [7] the application will convert it to an integer.
npdf!PDBookmarkGetCosObj+0xc01a:
5af135ba 8b4e18 mov ecx,dword ptr [esi+18h] ; selected ColorSpace type
5af135bd 3bc8 cmp ecx,eax
5af135bf 0f85ba040000 jne npdf!PDBookmarkGetCosObj+0xc4df (5af13a7f) ; [4] Compare againsed "/Indexed" atom
5af135c5 3bfa cmp edi,edx
5af135c7 0f85b2040000 jne npdf!PDBookmarkGetCosObj+0xc4df (5af13a7f) ; [4] Compare againsed "/Indexed" atom
...
5af135cd ff7604 push dword ptr [esi+4] ; Array for ColorSpace
5af135d0 e8abe3f5ff call npdf!CosObjGetType (5ae71980) ; [5] CosObjGetType
5af135d5 83c404 add esp,4
5af135d8 84c0 test al,al
5af135da 7418 je npdf!PDBookmarkGetCosObj+0xc054 (5af135f4)
...
5af135dc 6a02 push 2
5af135de ff7604 push dword ptr [esi+4] ; Array for ColorSpace
5af135e1 e86a0ff6ff call npdf!CosArrayGet (5ae74550) ; [6] use CosArrayGet to get third element of array (hival)
5af135e6 83c408 add esp,8
...
5af135e9 50 push eax
5af135ea e87153f6ff call npdf!CosIntegerValue (5ae78960) ; [7] convert hival to integer
5af135ef 83c404 add esp,4
5af135f2 eb03 jmp npdf!PDBookmarkGetCosObj+0xc057 (5af135f7)
After converting hival
to an integer, at [8] the application will add 1 to it and then store it to a variable on the stack at [9]. After adjusting the index, the application will then multiply it by 3 at [10]. In order to ensure that at least 1 element is allocated, at [11] the application will add 3 and then check to see if the addition will overflow. Afterwards at [12], the application will pass the resulting calculation to malloc
. Although the application checks to see if the addition of 3 will result in an overflow, this is not sufficient as if the integer for hival
when multiplied by 3 at [10] overflows the overflow flag will not be set. As a result of the overflow, the allocation at [12] will be undersized. After allocating memory, at [13] this buffer will be stored on the stack and inside an object as a property.
npdf!PDBookmarkGetCosObj+0xc057:
5af135f7 40 inc eax ; [8] add 1 to the integer (hival)
5af135f8 898554ffffff mov dword ptr [ebp-0ACh],eax ; [9] Store for termination of a loop later
5af135fe 8d0c40 lea ecx,[eax+eax*2] ; [10] Multiply hival by 3
5af13601 33c0 xor eax,eax
5af13603 83c103 add ecx,3 ; [11] Add 3 to hival
5af13606 0f92c0 setb al ; [11] Check if addition resulted in overflow
5af13609 f7d8 neg eax
5af1360b 0bc1 or eax,ecx
5af1360d 50 push eax
5af1360e ff1580f6385b call dword ptr [npdf!CAPContent::Wrap+0x29de90 (5b38f680)] ; [12] malloc
5af13614 83c404 add esp,4
5af13617 8bf8 mov edi,eax ; [13] Store allocated buffer
5af13619 8b8554ffffff mov eax,dword ptr [ebp-0ACh]
5af1361f 894610 mov dword ptr [esi+10h],eax ; [13] Store buffer
5af13622 8b4624 mov eax,dword ptr [esi+24h]
Later after allocating a buffer for the indexed palette, the application will enter the following loop. This loop has a terminator at [14] which checks to see if the current palette index in %eax
is larger than the integer (hival
) that was returned from CosArrayGet
. After loading indexed colorspace data into a local variable, at [15] the application will load each 8-bit component from the current palette into a register, and then at [16] write it into the pointer in the %edi
register. Each iteration of this loop will then increment the pointer in the %edi
register by 3 in order to seek to the next index of the palette. As prior mentioned, this loop will continue to write into the undersized buffer using the %edi
register until the current indexed color in %eax
reached the length of the loop (hival
) that was read via CosArrayGet
. Due to the integer-overflow resulting in an undersized buffer being allocated, the length for the loop and the length for the buffer are out-of-sync. This loop will write outside the bounds of the buffer which will cause a heap-based buffer overflow. This can lead to code execution under the context of the application.
npdf!PDBookmarkGetCosObj+0xc425:
5af139c5 f20f100da845495b movsd xmm1,mmword ptr [npdf!CAPContent::`vftable'+0xd7eb8 (5b4945a8)]
5af139cd 898574ffffff mov dword ptr [ebp-8Ch],eax
5af139d3 3b8554ffffff cmp eax,dword ptr [ebp-0ACh] ; [14] Check against length
5af139d9 0f8d7d000000 jge npdf!PDBookmarkGetCosObj+0xc4bc (5af13a5c)
5af139df 33c9 xor ecx,ecx
5af139e1 394a20 cmp dword ptr [edx+20h],ecx
5af139e4 7e37 jle npdf!PDBookmarkGetCosObj+0xc47d (5af13a1d)
...
5af13a1d 8d45c0 lea eax,[ebp-40h]
5af13a20 50 push eax
5af13a21 8d8578ffffff lea eax,[ebp-88h]
5af13a27 50 push eax
5af13a28 e8f3461800 call npdf!PDEDefaultGState+0x2c0 (5b098120)
...
5af13a2d 8a45c0 mov al,byte ptr [ebp-40h] ; [15] Load byte from dword read by inner loop
5af13a30 83c408 add esp,8
5af13a33 8807 mov byte ptr [edi],al ; [16] Store byte to undersized buffer
5af13a35 8b45c0 mov eax,dword ptr [ebp-40h] ; [15] Load byte from dword read by inner loop
5af13a38 c1e808 shr eax,8
5af13a3b 884701 mov byte ptr [edi+1],al ; [16] Store byte to undersized buffer
5af13a3e 8b45c0 mov eax,dword ptr [ebp-40h] ; [15] Load byte from dword read by inner loop
5af13a41 c1e810 shr eax,10h
5af13a44 884702 mov byte ptr [edi+2],al ; [16] Store byte to undersized buffer
5af13a47 83c703 add edi,3 ; [17] Add 3 to buffer
5af13a4a 8b8574ffffff mov eax,dword ptr [ebp-8Ch]
5af13a50 8b9578ffffff mov edx,dword ptr [ebp-88h]
5af13a56 40 inc eax ; [17] Increment loop counter
5af13a57 e969ffffff jmp npdf!PDBookmarkGetCosObj+0xc425 (5af139c5)
When opening up the provided proof-of-concept in the application, the following crash will occur.
(2bbc.1bdc): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=0000006b ebx=0118f001 ecx=25c446e3 edx=006b0c0a esi=0c0b0cc8 edi=0c1d0ffe
eip=5af13a44 esp=0118eb4c ebp=0118ec0c iopl=0 nv up ei pl nz na po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00210202
npdf!PDBookmarkGetCosObj+0xc4a4:
5af13a44 884702 mov byte ptr [edi+2],al ds:0023:0c1d1000=??
Outputting the loop terminator shows that the loop will continue until the value of hival
is reached.
0:000> ? dwo(@ebp-ac)
Evaluate expression: 1431655766 = 55555556
Performing the same math that was used for the allocation shows that only 5 bytes were allocated for the buffer.
0:000> ? dwo(@ebp-ac)*3+3
Evaluate expression: 4294967301 = 00000001`00000005
The base addresses of the libraries in this report.
0:000> lm m npdf
Browse full module list
start end module name
5adc0000 5b807000 npdf (export symbols) npdf.dll
013e0000 01c61000 NitroPDF (deferred)
In the provided proof-of-concept, the “/Indexed” colorspace described by this advisory is stored in object 6. This colorspace uses the integer 1431655765 (0x55555555) for hival
which will result in the allocation for the buffer being of length 5. The commands for rendering the page are in object 7. These will load the specified colorspace by name (/IndexedColorSpace
), and then use a color to draw a rectangle that fills the first page. The usage of the colorspace whilst rendering the page is necessary for this vulnerability to trigger. The dimensions for the rectangle directly correspond to the MediaBox dimensions which are stored in object 5.
2020-05-13 - Vendor Disclosure
2020-09-01 - Vendor Patched
2020-09-15 - Public Release