Talos Vulnerability Report


Aspose.PDF for C++ Remote Code Execution Vulnerability

September 17, 2019
CVE Number



An exploitable Use-After-Free vulnerability exists in the way FunctionType 0 PDF elements are processed in Aspose.PDF for C++. A specially crafted PDF can cause a dangling heap pointer, resulting in a use-after-free . An attacker can send a malicious PDF to trigger this vulnerability.

Tested Versions

Aspose.PDF for C++ 19.2

Product URLs


CVSSv3 Score

8.8 - CVSS:3.0/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H


CWE-416: Use After Free


Aspose provides a series of APIs for manipulating or converting a large family of document formats. Aspose.PDF is a library used for editing, writing, and rendering PDFs. Aspose.PDF provides a number of different language bindings to allow modification or creation of PDFs from a number of different developer environments.

PDFs have a known huge feature set. One of these features are Function Objects. From the PDF v1.7 Specification, "PDF is not a programming language, and a PDF file is not a program. However, PDF does provide several types of function objects (PDF 1.2) that represent parameterized classes of functions, including mathematical formulas and sampled representations with arbitrary resolution. Functions are used in various ways in PDF, including device-dependent rasterization information for high-quality printing halftone spot functions and transfer functions), color transform functions for certain color spaces, and specification of colors as a function of position for smooth shadings."

The module used for this research is described below:

00007ffb`55a80000 00007ffb`5adc6000   Aspose_PDF_vc141x64 C (export symbols)       Aspose.PDF_vc141x64.dll
    Loaded symbol image file: Aspose.PDF_vc141x64.dll
    Image path: C:\Aspose.PDF_for_C++_19.2\example\x64\Release\Aspose.PDF_vc141x64.dll
    Image name: Aspose.PDF_vc141x64.dll
    Browse all global symbols  functions  data
    Timestamp:        Thu Feb 21 10:05:05 2019 (5C6ECC31)
    CheckSum:         00000000
    ImageSize:        05346000

A traditional example use case of a FunctionType looks like the following object:

12 0 obj
    /Length 64
    /FunctionType 0
    /Size [ 10 ]
    /Decode [ 0 1 0 1 ]
    /Range [ 0 255 0 255 ]
    /BitsPerSample 8
    /Domain [ 0 1 ]

While parsing a FunctionType, various heap objects are created. One such is an object of size 0x78 which is allocated and then immediately initialized.

crashing_obj = (CrashingObj *)malloc_wrapper(0x78);
if (crashing_obj == (CrashingObj *)0x0) {
    // Failed to allocate
else {
    crashing_obj->field_0x8 = 0x25640f8;
    crashing_obj->field_0x70 = 0x2564108;
    Object((Object *)&crashing_obj->field_0x38);
    crashing_obj->field_0x68 = 0x2552f90;
    *(undefined8 *)((longlong)&crashing_obj->field_0x70 + (longlong)*(int *)(crashing_obj->field_0x70 + 4)) = 0x2552fa0;
    crashing_obj->field_0x0 = (CrashingObj *)0x25535a0;
    // Further initialization

Below are all of the allocations and frees involving this object.

Action     Address            Size       TimeStart       Result
Alloc    - 0x1e772ee0000    - 0x78     - 46E4BE:7B     -        
Free     - 0x1e772ee0000    -          - 47A6A2:47     - 0x1    

Above we see the address being allocated at TimeStamp 46E4BE:7B and being freed at 47A6A2:47. Taking a look at the reads and writes of this particular object shows the various usages of the fields in the object. For example, we see the initial writing of uninitialized data with 0xc0c0c0c0c0c0c0c0 [0]. We can also further cross-reference the TimeStamps seeing that all memory access time stamps are within range of the allocation and freed timestamps.

    EventType         TimeStart       AccessType       RIP                 Address            Size       Value
- MemoryAccess    - 46E4D9:22     - Write          - 0x7ffbc31e2ff8    - 0x1e77f6def80    - 0x10     - 0xc0c0c0c0c0c0c0c0   [0]
- MemoryAccess    - 46E4DB:4E     - Write          - 0x7ffb578c222a    - 0x1e77f6def88    - 0x8      - 0x7ffb57fe40f8        
- MemoryAccess    - 46E4DB:64     - Write          - 0x7ffb578c2269    - 0x1e77f6def80    - 0x8      - 0x7ffb57fd35a0        
- MemoryAccess    - 46E4DB:65     - Read           - 0x7ffb578c226c    - 0x1e77f6def88    - 0x8      - 0x7ffb57fe40f8        
- MemoryAccess    - 46E4DB:85     - Read           - 0x7ffb578c2307    - 0x1e77f6def88    - 0x8      - 0x7ffb57fe40f8    
- MemoryAccess    - 46E4DB:91     - Read           - 0x7ffb578c2339    - 0x1e77f6def88    - 0x8      - 0x7ffb57fe40f8    

Looking at the time of the crash of the proof of concept shows that the Address being decremented is the heap address that was freed prior at 47A6A2:47.

Time Travel Position: 1233615:0 // [1]
00007ffb`55c4ed5b f00fc14110      lock xadd dword ptr [rcx+10h],eax ds:000001e7`7f6def88=57fe40f8

rax=00000000ffffffff rbx=000001e77f6f6fc0 rcx=000001e77f6def78

Seeing that the TimeStamp is past the time of the free [1], but seeing the address being decremented was the address in the prior free, shows that we have a danling pointer left after the free resulting in a Use-After-Free vulnerability.

By carefully controling allocations after the object is freed, and attacker can place arbitrary objects in it's place which can lead to further memory corruption and can ultimately result in arbitrary code execution.

Crash Information

rax=00000000ffffffff rbx=000001e77f6f6fc0 rcx=000001e77f6def78
rdx=000001e772ee0000 rsi=000001e77f75cf60 rdi=000001e77f6f0fc0
rip=00007ffb55c4ed5b rsp=0000007b1e34eb40 rbp=0000000000000001
r8=0000000000000000  r9=0000000000000001 r10=00000000ffffffef
r11=0000007b1e34ead0 r12=0000000000000001 r13=0000000000000000
r14=0000000000000000 r15=000001e77bc50f00
iopl=0         nv up ei pl zr na po nc
cs=0033  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000246
00007ffb`55c4ed5b f00fc14110      lock xadd dword ptr [rcx+10h],eax ds:000001e7`7f6def88=57fe40f8

!analyze -v
BUCKET_ID:  APPLICATION_FAULT_INVALID_POINTER_WRITE_AVRF_Aspose_PDF_vc141x64!System::Collections::Generic::Dictionary_System::String,System::String_::begin+8b


2019-04-17 - Vendor disclosure
2019-08-24 - Vendor acknowledged & advised issues under review
2019-09-16 - Vendor patched
2019-09-17 - Public release


Discovered by Cory Duplantis and Aleksandar Nikolic Cisco Talos.