Zscaler Blog

Get the latest Zscaler blog updates in your inbox

Subscribe
Security Research

Analysis of Adobe Acrobat Pro DC Solid Framework Out-of-Bounds Read Vulnerability (CVE-2021-40729)

KAI LU, BRETT STONE-GROSS
January 31, 2022 - 11 min read

Summary

In October 2021, Adobe released a security update for vulnerabilities in Adobe Acrobat and Reader. Among these vulnerabilities is an out-of-bounds read (CVE-2021-40729) that was discovered by Zscaler’s ThreatLabz. In this blog, we present our analysis of this vulnerability in the Adobe Acrobat Pro DC Solid Framework. Adobe uses the Solid Framework for the conversion of PDF files to Microsoft Office files. Foxit’s PDF Editor is also impacted by this vulnerability since it also uses the Solid Framework for the conversion of PDF files to other file formats.

 

Vulnerability Description

CVE-2021-40729 is an out-of-bounds read vulnerability that could potentially lead to disclosure of sensitive memory. An attacker could leverage this vulnerability to bypass mitigations such as ASLR. Exploitation of this issue requires user interaction in that a victim must open a malicious PDF file.

 

Known Affected Software Configurations

Acrobat DC Continuous 21.007.20095 and earlier versions in Windows
Acrobat DC Continuous 21.007.20096 and earlier versions in macOS
Acrobat 2020 Classic 2020 20.004.30015 and earlier versions in Windows & macOS
Acrobat 2017 Classic 2017 17.011.30202 and earlier versions in Windows & macOS
Foxit PDF Editor 11.2.0.53415 and all previous 11.x versions, 10.1.6.37749 and earlier

 

Proof of Concept

The vulnerability can be triggered by opening a specially crafted PDF file and exporting it to a Microsoft Word document. Zscaler ThreatLabz created a PoC file that will cause the following crash.

(1734.15d4): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=a7acaff8 ebx=a7fc4fa0 ecx=00000001 edx=80000090 esi=809f8220 edi=04afbb7c
eip=80a82943 esp=04afbb00 ebp=04afbb18 iopl=0         nv up ei ng nz ac po cy
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00210293
PDFLibTool!CPdfColorSpace::SetCurrentColorIndirect+0xb3:
80a82943 f20f100cc8      movsd   xmm1,mmword ptr [eax+ecx*8] ds:002b:a7acb000=????????????????
0:000> dc eax
a7acaff8  6f23f199 3ff0cd9a ???????? ????????  ..#o...?????????
a7acb008  ???????? ???????? ???????? ????????  ????????????????
a7acb018  ???????? ???????? ???????? ????????  ????????????????
a7acb028  ???????? ???????? ???????? ????????  ????????????????
a7acb038  ???????? ???????? ???????? ????????  ????????????????
a7acb048  ???????? ???????? ???????? ????????  ????????????????
a7acb058  ???????? ???????? ???????? ????????  ????????????????
a7acb068  ???????? ???????? ???????? ????????  ????????????????
0:000> !heap -p -a a7acaff8
    address a7acaff8 found in
    _DPH_HEAP_ROOT @ 9501000
    in busy allocation (  DPH_HEAP_BLOCK:         UserAddr         UserSize -         VirtAddr         VirtSize)
                                a7993bc8:         a7acaff8                8 -         a7aca000             2000
    672ba8b0 verifier!AVrfDebugPageHeapAllocate+0x00000240
    7714f10e ntdll!RtlDebugAllocateHeap+0x00000039
    770b70f0 ntdll!RtlpAllocateHeap+0x000000f0
    770b6e3c ntdll!RtlpAllocateHeapInternal+0x0000104c
    770b5dde ntdll!RtlAllocateHeap+0x0000003e
    75840166 ucrtbase!_malloc_base+0x00000026
    80d11770 PDFLibTool!CxManagedImage::Set+0x000c27b0
    80a09dcd PDFLibTool!SolidWatermark::CSolidImageWatermarkOptBase::getWatermarkFileName+0x0000095d
    80b1e9ed PDFLibTool!CPdfPageContentProcessor::ProcessGeneralCommand+0x00001d6d
    80b1c398 PDFLibTool!CPdfPageContentProcessor::ProcessCommandStreamMultiThread+0x000005a8
    80b1bdce PDFLibTool!CPdfPageContentProcessor::ProcessCommandStream+0x0000002e
    848d7fad SecurePdfSDK!CLayoutPage::PerTextContent+0x0000004d
    848ffbf4 SecurePdfSDK!CPdfOCRPageRenderTools::IsPageScanned+0x00000174
    83f89e31 PdfFlt+0x00009e31
    83fa634d PdfFlt+0x0002634d
    8453a8dd PdfFlt+0x005ba8dd
    80b17800 PDFLibTool!CPdfDocumentImplEx::RenderPDFDocument+0x00000410
    8453c8b8 PdfFlt+0x005bc8b8
    84541c4b PdfFlt+0x005c1c4b
0:000> kb # ChildEBP RetAddr  Args to Child             
WARNING: Stack unwind information not available. Following frames may be wrong.
00 04afbb18 80b225ed 04afbb7c a7acaff8 00000000 PDFLibTool!CPdfColorSpace::SetCurrentColorIndirect+0xb3
01 04afbbf8 80b1ea41 00000001 a7acaff8 00000000 PDFLibTool!CPdfPageContentProcessor::SetCurrentColor+0x1cd
02 04afbd98 80b1c398 04afc1b8 80b1c398 04afbe9c PDFLibTool!CPdfPageContentProcessor::ProcessGeneralCommand+0x1dc1
03 04afc0cc 80b1bdce a38defb8 a329efe8 04afe918 PDFLibTool!CPdfPageContentProcessor::ProcessCommandStreamMultiThread+0x5a8
04 04afc0e4 848d7fad a38defb8 88a9b2b0 a329efe8 PDFLibTool!CPdfPageContentProcessor::ProcessCommandStream+0x2e
05 04afc114 848ffbf4 88a9b7a8 6c818f20 83f87840 SecurePdfSDK!CLayoutPage::PerTextContent+0x4d
06 04afc40c 83f89e31 6a550cf0 a329efe8 04afc474 SecurePdfSDK!CPdfOCRPageRenderTools::IsPageScanned+0x174
07 04afc8d8 83fa634d 6c818f20 4e1b0412 83fa62c0 PdfFlt+0x9e31
08 04afc92c 8453a8dd 6c818f20 4e1b0156 84539dd0 PdfFlt+0x2634d
09 04afcc68 80b17800 6babbb4c 04afe918 6fdf4f40 PdfFlt+0x5ba8dd
0a 04afcd90 8453c8b8 95236fe0 4e1b00de 04afe918 PDFLibTool!CPdfDocumentImplEx::RenderPDFDocument+0x410
0b 04afcde0 84541c4b 95236fe0 04afe990 73f2aff0 PdfFlt+0x5bc8b8
0c 04afce00 84541d1a 4e1b030a 04afe990 6a550cc0 PdfFlt+0x5c1c4b
0d 00000000 00000000 00000000 00000000 00000000 PdfFlt+0x5c1d1a

Test Environment

Adobe Acrobat Pro DC, Product version: 21.7.20095.60881 
PDFLibTool.dll, Product version: 10.0.12082.1
Solid Framework (x86), Product version: 10.0.12082.1

 

Technical Analysis

The out-of-bounds read vulnerability CVE-2021-40729 exists in Adobe Acrobat Pro DC’s third-party library Solid Framework, which is located in the directory C:\Program Files (x86)\Adobe\Acrobat DC\Acrobat\plug_ins\SaveAsNonPDF\Solid. Figure 1 shows a comparison between a properly structured PDF file with a minimized PoC file that triggers this vulnerability.

Image

Figure 1. Comparison between a proper PDF file and the minimized PoC file that triggers CVE-2021-40729

Figure 2 shows the single modified byte that triggers the vulnerability located in obj 4.

Image

Figure 2. Hexdump of the obj 4 buffer including the modified byte that triggers the vulnerability

The structure of the minimized PoC file is shown below in Figure 3.

Image

Figure 3. The structure of the minimized PoC file

In Figure 3, the obj 2 uses obj 6 as its resource and obj 4 as its content. obj 6 references obj 8 as its colorspace. obj 8 is an ICCBased family color space. ICCBased color spaces can have 1, 3, or 4 components (defined by the N dictionary value). So they often use Gray (1 component), RGB (3 components), or CMYK (4 components). obj 25 contains the ICC profile in its stream. The parameter “/N 3” indicates the number of color components in the color space described by the ICC profile data. In this PoC PDF file, it uses RGB color spaces. Therefore, when the Solid Framework processes the SC (set color) operator, three operands are required.

The description of the SC operator is listed in Figure 4.

Image

Figure 4. The SC (set color) operator definition

The stream data in obj 4 has been compressed using the deflate algorithm. (For more information on deflate, please refer to https://tools.ietf.org/html/rfc1951). Zlib is a C library that implements the deflate algorithm. Adobe Acrobat also uses Zlib to decompress the deflate-compressed data, we can use the Python script shown below to decompress the data in obj 4.

 

Image

Figure 5. Python Zlib decompression code


The uncompressed stream content in obj 4 contains a stream of graphics operators. Many of these streams contain invalid operators and abnormal operands including “1.050196108SC” as highlighted in Figure 6.

Image

Figure 6. Uncompressed stream content in obj 4 in the minimized PoC file


Normal stream data for the SC operator is shown in Figure 7.

Image

Figure 7. Uncompressed stream content in obj 4 in a normal PDF file


The parsing and processing of these abnormal operands for the SC operator are directly related to this vulnerability. In the section of “Proof of Concept”, the methods CPdfPageContentProcessor::ProcessCommandStreamMultiThread  and CPdfPageContentProcessor::ProcessGeneralCommand in the stack backtrace stand out.

The method CPdfPageContentProcessor::ProcessCommandStreamMultiThread(std::streambuf &) is responsible for processing the command stream of graphic operators and operands. First, it calls the function sub_101288B0() that starts a thread to parse the stream content. The thread calls the function sub_1012AB70(). Finally, in the function sub_1012AB70() it parses each command from the stream content. The call flow graph is shown in Figure 8.

Image

Figure 8. The control flows for parsing the PDF stream content

The following breakpoint in Figure 9 can be set in order to analyze the memory layout for each command.

Image

Figure 9. Breakpoint to analyze the memory layout of the CPdfCmdContext structure


After the method CPdfCommandStreamBase::GetNextCmd(CPdfCmdContext &) is called, a breakpoint can be set on the following instruction to analyze the memory layout of the CPdfCmdContext structure.

bu PDFLibTool!CPdfPageContentProcessor::~CPdfPageContentProcessor+0x75e ".echo get cmd: ; dd ebp-0x3c L1; .echo Vector<CPdfLexem>; db poi(ebp-0x38) L(poi(ebp-0x34)-poi(ebp-0x38)); gc; "

Example output when this breakpoint is hit is shown in Figure 10 with the std::Vector<CPdfLexem> graphic operator objects. Each CPdfLexem object consists of 0x28 bytes that store the operator and its operands.

Image

Figure 10. Memory dump for a CPdfCmdContext structure

A conditional breakpoint can be set after the method CPdfCommandStreamBase::GetNextCmd(CPdfCmdContext &) finishes parsing the stream content “0.5968767 m.5285492” followed by the stream content “1.050196108SC” to further understand how the invalid data is processed.

bu PDFLibTool!CPdfPageContentProcessor::~CPdfPageContentProcessor+0x75e ".echo get cmd: ; dd ebp-0x3c L1; .echo Vector<CPdfLexem>; db poi(ebp-0x38) L(poi(ebp-0x34)-poi(ebp-0x38)); .if(poi(poi(ebp-0x38)+0x20)=0x2a47d227) { .echo 0.5968767 m.5285492 hit; } .else {gc;}"

In Figure 11, the memory layout is shown with the members of a CPdfCmdContext structure.

Image

Figure 11. The members of a CPdfCmdContext structure 


After the call to CPdfCommandStreamBase::GetNextCmd(CPdfCmdContext &) the memory layout of std::Vector<CPdfLexem> object contains the stream content “1.050196108SC” as seen in Figure 12. This std::Vector<CPdfLexem> object includes two CPdfLexem objects: the first comes from a single operand, and the second comes from the operator SC.

Image

Figure 12. Memory layout  of std::Vector<CPdfLexem> for the stream content “1.050196108SC”.


The memory layout of the type double 1.050196108 is correct (0x3ff0cd9a6f23f199) as shown in Figure 13 using an online conversion tool.

Image

Figure 13. The 1.050196108 double value converted to a hex value

The method CPdfPageContentProcessor::ProcessCommandStreamMultiThread(std::streambuf &), obtains all commands via parsing the stream content for graphic operators and operands. Then, it calls CPdfPageContentProcessor::ProcessGeneralCommand(GrStateCommands const &,char const *,std::vector<CPdfLexem> &,int) to process these commands one by one in a loop.
Thereby, another conditional breakpoint can be set on the method call CPdfPageContentProcessor::ProcessGeneralCommand(GrStateCommands const &,char const *,std::vector<CPdfLexem> &,int).

bu PDFLibTool!CPdfPageContentProcessor::ProcessGeneralCommand ".if(poi(poi(esp+4))=0x23) {.printf \"stream 0x23 \\n\"; .printf  \"CPdfPageContentProcessor::ProcessGeneralCommand hit\\n\"; dps esp L7; db poi(esp+8); dd poi(esp+4) L1; }; .else{ gc;}"

This function takes 4 parameters. The 1st parameter stores the command number, the 2nd parameter stores the graphic operator, the 3rd parameter is a reference to a std::vector<CPdfLexem> object, and the 4th parameter is the number of operands in this graphic operator. 

The following output shows when this breakpoint is hit the second time.

stream 0x23
CPdfPageContentProcessor::ProcessGeneralCommand hit
04efb85c  84fac398 PDFLibTool!CPdfPageContentProcessor::ProcessCommandStreamMultiThread+0x5a8
04efb860  04efb954     ;1st parameter
04efb864  aaab4fdc     ;2nd parameter
04efb868  04efb958     ;3rd parameter
04efb86c  00000001     ;4th parameter
04efb870  39045405
04efb874  50caaff8
aaab4fdc  53 43 00 85 ff ff ff ff-b4 fd c5 a4 40 11 f6 84  SC..........@...
aaab4fec  02 00 00 00 0f 00 00 00-00 00 00 00 00 00 00 00  ................
aaab4ffc  00 00 00 00 ?? ?? ?? ??-?? ?? ?? ?? ?? ?? ?? ??  ....????????????
aaab500c  ?? ?? ?? ?? ?? ?? ?? ??-?? ?? ?? ?? ?? ?? ?? ??  ????????????????
aaab501c  ?? ?? ?? ?? ?? ?? ?? ??-?? ?? ?? ?? ?? ?? ?? ??  ????????????????
aaab502c  ?? ?? ?? ?? ?? ?? ?? ??-?? ?? ?? ?? ?? ?? ?? ??  ????????????????
aaab503c  ?? ?? ?? ?? ?? ?? ?? ??-?? ?? ?? ?? ?? ?? ?? ??  ????????????????
aaab504c  ?? ?? ?? ?? ?? ?? ?? ??-?? ?? ?? ?? ?? ?? ?? ??  ????????????????
04efb954  00000023
eax=1103d938 ebx=04efbc70 ecx=04efbc70 edx=55000054 esi=881ec9c0 edi=564c6fb8
eip=84facc80 esp=04efb85c ebp=04efbb84 iopl=0         nv up ei pl zr na pe cy
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00200247
PDFLibTool!CPdfPageContentProcessor::ProcessGeneralCommand:
84facc80 53              push    ebx
0:000> db poi(04efb958)
aaab4fb0  04 00 00 00 00 f0 1a 85-ff ff ff ff b4 fd c5 a4  ................
aaab4fc0  40 11 f6 84 00 00 00 00-0f 00 00 00 00 00 00 00  @...............
aaab4fd0  99 f1 23 6f 9a cd f0 3f-07 00 00 00 53 43 00 85  ..#o...?....SC..
aaab4fe0  ff ff ff ff b4 fd c5 a4-40 11 f6 84 02 00 00 00  ........@.......
aaab4ff0  0f 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
aaab5000  ?? ?? ?? ?? ?? ?? ?? ??-?? ?? ?? ?? ?? ?? ?? ??  ????????????????
aaab5010  ?? ?? ?? ?? ?? ?? ?? ??-?? ?? ?? ?? ?? ?? ?? ??  ????????????????
aaab5020  ?? ?? ?? ?? ?? ?? ?? ??-?? ?? ?? ?? ?? ?? ?? ??  ????????????????
0:000> !heap -p -a aaab4fb0
   address aaab4fb0 found in
    _DPH_HEAP_ROOT @ 87a1000
    in busy allocation (  DPH_HEAP_BLOCK:         UserAddr         UserSize -         VirtAddr         VirtSize)
                                aa9929f4:         aaab4fb0               50 -         aaab4000             2000
    707ba8b0 verifier!AVrfDebugPageHeapAllocate+0x00000240
    7714f10e ntdll!RtlDebugAllocateHeap+0x00000039
    770b70f0 ntdll!RtlpAllocateHeap+0x000000f0
    770b6e3c ntdll!RtlpAllocateHeapInternal+0x0000104c
    770b5dde ntdll!RtlAllocateHeap+0x0000003e
    75840166 ucrtbase!_malloc_base+0x00000026
    851a1770 PDFLibTool!CxManagedImage::Set+0x000c27b0
    84f1df85 PDFLibTool!UnicodeHelper::unicodeStringToPdfUnicodeString+0x00002b25
    84f9cb50 PDFLibTool!CPdfCommandStreamBase::GetNextCmd+0x00000240
    84faabbe PDFLibTool!CPdfPageContentProcessor::~CPdfPageContentProcessor+0x0000075e
    84fa8b08 PDFLibTool!CPdfDocumentImplEx::SetupPageData+0x00000728
    75854f9f ucrtbase!thread_start<unsigned int (__stdcall*)(void *),1>+0x0000003f
    76f8fa29 KERNEL32!BaseThreadInitThunk+0x00000019
    770d7a9e ntdll!__RtlUserThreadStart+0x0000002f
    770d7a6e ntdll!_RtlUserThreadStart+0x0000001b
    00000000 +0x00000000

In this example, the command number is 0x23. This causes the code to enter the following switch case shown in Figure 14 in the function CPdfPageContentProcessor::ProcessGeneralCommand.

Image

Figure 14. The switch case for command 0x23 in CPdfPageContentProcessor::ProcessGeneralCommand

This switch code block reads 8 bytes at the offset 0x20 in the std::vector<CPdfLexem> object. The value of the 8 bytes represents the type double 1.050196108. Then it creates a heap buffer with a size of 8 bytes. Next, it copies these 8 bytes from the std::vector<CPdfLexem> object to the newly created heap buffer. Next, it makes an indirect call to the method CPdfPageContentProcessor::SetCurrentColor(int,double *,char const *,int). When it enters into this function CPdfPageContentProcessor::SetCurrentColor, the second parameter is a pointer to a double with the value 1.050196108 as shown in Figure 15.

Image

Figure 15. The parameters of CPdfPageContentProcessor::SetCurrentColor

In the method CPdfPageContentProcessor::SetCurrentColor, the code calls the method CPdfColorSpace::SetCurrentColorIndirect(CGSColor &,double const *,int,std::string const &). This first parameter is a CGSColor object. The second parameter comes from the 2nd parameter of CPdfPageContentProcessor::SetCurrentColor. The register ECX is the this pointer for a CPdfColorSpace object whose size is 0x60 bytes. The 4 bytes at offset 0x08 in the CPdfColorSpace object represents the number of color components in the color space described by the ICC profile data. In Figure 16, the 4 bytes at offset 0x08 in the CPdfColorSpace object is equal to 0x3. As previously described in Figure 3 and Figure 4, “/N 3” indicates the number of color components in the color space, which explains why the value at offset 0x8 in the CPdfColorSpace object is 0x3.

Image

Figure 16. Inspect the parameters of CPdfColorSpace::SetCurrentColorIndirect

The function CPdfPageContentProcessor::SetCurrentColor calls the method CPdfColorSpace::SetCurrentColorIndirect. This method tries to read 3 required color components from its second parameter in a loop. However, in the PoC the second parameter stores only one color component. When it tries to read the second color component from the heap buffer, it causes an out-of-bounds read crash as shown in Figure 17.

Image

Figure 17. Read color components from a heap buffer


This concludes the analysis of this out-of-bounds read vulnerability. In short, due to abnormal SC operands in obj 4 when Adobe Acrobat parses “1.050196108SC”, it converts this stream content to one double type as the only operand of the SC operator. One double type takes 8 bytes. obj 25 declared that in color spaces it required 3 color components (i.e., three operands). Therefore, it tries to read 3 double type values in the heap buffer when it handles the graphic operator SC. This heap buffer only consists of 8 bytes, which subsequently causes an out-of-bounds read crash.

 

Mitigation

All users of Adobe Acrobat and Reader are encouraged to upgrade to the latest version of this software. Zscaler’s Advanced Threat Protection and Advanced Cloud Sandbox can protect customers against this vulnerability.

 

References

https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-40729
https://helpx.adobe.com/security/products/acrobat/apsb21-104.html
https://www.foxit.com/support/security-bulletins.html
https://blog.idrsolutions.com/2011/04/understanding-the-pdf-file-format-%E2%80%93-iccbased-colorspaces/
https://gregstoll.com/~gregstoll/floattohex/

 

form submtited
Thank you for reading

Was this post useful?

Get the latest Zscaler blog updates in your inbox

By submitting the form, you are agreeing to our privacy policy.