FortiGuard Labs Threat Research

Analysis of CVE-2016-4203 - Adobe Acrobat and Reader CoolType Handling Heap Overflow Vulnerability

By Kai Lu | July 20, 2016

Summary

Recently, Adobe patched some security vulnerabilities in Adobe Acrobat and Reader. One of them is a heap buffer overflow vulnerability (CVE-2016-4203) I recently discovered. In this blog, we want to share our analysis of this vulnerability.

Proof of Concept

This vulnerability can be reproduced by opening the PoC file “poc_minimized.pdf” with Adobe Reader DC. When opened, AcroRd32.exe crashes, and the crash information is shown below:

(8de0.6bc4): Access violation - code c0000005 (first chance)

First chance exceptions are reported before any exception handling.

This exception may be expected and handled.

eax=097eeeed ebx=00000015 ecx=097ef6e0 edx=0000cc6c esi=2952d000 edi=00000024

eip=0959a23c esp=2911e5f8 ebp=2911e608 iopl=0         nv up ei pl nz na po nc

cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00010202

CoolType!CTInit+0x45ef1:

0959a23c 0fb606          movzx   eax,byte ptr [esi]         ds:002b:2952d000=??

 

0:022> kb

 # ChildEBP RetAddr  Args to Child             

WARNING: Stack unwind information not available. Following frames may be wrong.

00 2911e608 09598db1 097eee15 097ef564 097ef44c CoolType!CTInit+0x45ef1

01 2911e674 095939a0 17e9cc38 17e9cd98 17e9cd58 CoolType!CTInit+0x44a66

02 2911e6d0 095935c3 17e9cc38 17e9cd98 17e9cd58 CoolType!CTInit+0x3f655

03 2911e720 0958d9a3 17e9cc38 17e9cd98 17e9cd58 CoolType!CTInit+0x3f278

04 2911e784 0958d79c 00000001 2911e7d8 00000001 CoolType!CTInit+0x39658

05 2911e79c 095a6cb0 2911e8a8 2911e7d8 1a99cf68 CoolType!CTInit+0x39451

06 2911e8fc 095a6996 1a99cf68 097f61a8 2911ea88 CoolType!CTInit+0x52965

07 2911eac4 095a614e 2911ecb8 00000000 097f6480 CoolType!CTInit+0x5264b

08 2911eb90 095a506f 773dfa00 00000000 00000001 CoolType!CTInit+0x51e03

09 2911ef58 095a468a 00000025 20f02fec 00001088 CoolType!CTInit+0x50d24

0a 2911ef98 095a3691 20f02fe0 00000002 2911f028 CoolType!CTInit+0x5033f

0b 2911f100 095a32c7 2911f518 2911f8ac 0000044a CoolType!CTInit+0x4f346

0c 2911f150 0908a44c 145aae2c 2911f518 2911f8ac CoolType!CTInit+0x4ef7c

0d 2911f258 0906bab0 2911f34c 00000000 21d6629c AGM!AGMInitialize+0x51bf0

0e 2911f268 0906b98f 00000000 f7510c27 20d6cf70 AGM!AGMInitialize+0x33254

0f 2911f280 0906ba9c 00000081 21cb6d00 21cb6d00 AGM!AGMInitialize+0x33133

10 2911f9ac 0906182e 090004ca 00001fa0 2911fa01 AGM!AGMInitialize+0x33240

11 2911f9bc 09080a4d 21d76fe8 00000053 00000000 AGM!AGMInitialize+0x28fd2

12 2911fa01 00000000 c8000000 5021ca6f 0027c3d2 AGM!AGMInitialize+0x481f1

 

0:022> !heap -p -a esi

    address 2952d000 found in

    _DPH_HEAP_ROOT @ 3d01000

    in busy allocation (  DPH_HEAP_BLOCK:         UserAddr         UserSize -         VirtAddr         VirtSize)

                                                       293c3924:         2952cf40               c0 -         2952c000             2000

    6a749abc verifier!AVrfDebugPageHeapAllocate+0x0000023c

    7749d836 ntdll!RtlDebugAllocateHeap+0x0000003c

    773ffb40 ntdll!RtlpAllocateHeap+0x000000f0

    773fdecb ntdll!RtlpAllocateHeapInternal+0x0000027b

    773fdc2e ntdll!RtlAllocateHeap+0x0000002e

    6854ed63 MSVCR120!malloc+0x00000049 [f:\dd\vctools\crt\crtw32\heap\malloc.c @ 92]

    095550fc CoolType!CTInit+0x00000db1

    095589db CoolType!CTInit+0x00004690

    …

 

Analysis

This vulnerability is an instance of a heap buffer overflow vulnerability. Let’s look into this specially crafted PDF file first. The comparison between the normal PDF file and the minimized PoC file is shown below.

Figure 1. The PoC File vs The Original PDF File

Figure 2. Parsing of The PoC File with 010 Editor

From Figures 1 and 2 we can see the only difference between the original PDF file and the PoC file is a single byte at offset 0x30e3, located in obj 17. The object structure is shown below.

17 0 obj

<

/Length 14790

/Filter [/FlateDecode]

/DecodeParms [null]

>> 

stream

endstream

endobj

 

The question is, what type of data does this object store? We found that the following object in the PDF file references obj 17:

8 0 obj

<

/FontFile2 17 0 R

/FontName /AAAAAA+ComicSansMS

/Flags 32

/ItalicAngle 0

/Ascent 1102

/Descent -312

/CapHeight 0

/StemV 0

/Type /FontDescriptor

/CIDSet 19 0 R

>> 

endobj

 

From the field “/FontFile2 17 0 R”, we know that obj 17 stores a font file.  The data of the font file is encoded with FlateDecode in obj 17. For more information on deflate, please refer to https://tools.ietf.org/html/rfc1951.

 

Zlib is a C library which implements the deflate algorithm. We can use it to decompress the data in obj 17. The decompressed data of obj 17 in the PoC file is stored in the file output.dat, which has a length of 0x4650. The decompressed data of obj 17 in the original PDF file is stored in the file output_original.dat which also has a length of 0x4650. The following is the comparison between output.dat and output_original.dat:

Figure 3. The PoC Font File (output.dat) vs The Original Font File (output_original.dat)

We can see that about one hundred bytes are different.  The next question, then, is to figure out what data triggers this vulnerability. Some debugging skills are needed. Let’s go back to Windbg first.

0:022> r

eax=097eeeed ebx=00000015 ecx=097ef6e0 edx=0000cc6c esi=2952d000 edi=00000024

eip=0959a23c esp=2911e5f8 ebp=2911e608 iopl=0         nv up ei pl nz na po nc

cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00010202

CoolType!CTInit+0x45ef1:

0959a23c 0fb606          movzx   eax,byte ptr [esi]         ds:002b:2952d000=??

 

0:022> !heap -p -a esi

    address 2952d000 found in

    _DPH_HEAP_ROOT @ 3d01000

    in busy allocation (  DPH_HEAP_BLOCK:         UserAddr         UserSize -         VirtAddr         VirtSize)

                                                       293c3924:         2952cf40               c0 -         2952c000             2000

    6a749abc verifier!AVrfDebugPageHeapAllocate+0x0000023c

    7749d836 ntdll!RtlDebugAllocateHeap+0x0000003c

    773ffb40 ntdll!RtlpAllocateHeap+0x000000f0

    773fdecb ntdll!RtlpAllocateHeapInternal+0x0000027b

    773fdc2e ntdll!RtlAllocateHeap+0x0000002e

    6854ed63 MSVCR120!malloc+0x00000049 [f:\dd\vctools\crt\crtw32\heap\malloc.c @ 92]

    095550fc CoolType!CTInit+0x00000db1

    095589db CoolType!CTInit+0x00004690

    …

 

0:022> db 2952cf40 Lc0

2952cf40  00 02 00 b3 ff fb 01 bf-05 da 00 0b 00 23 00 55  .............#.U

2952cf50  40 36 06 38 00 1e 12 1e-06 20 25 30 25 50 25 c0  @6.8..... %0%P%.

2952cf60  25 04 80 25 90 25 a0 25-03 09 38 03 18 15 1b 18  %..%.%.%..8.....

2952cf70  38 0f 21 1f 25 2f 25 3f-25 7f 25 04 1f 0c bf 0c  8.!.%/%?%.%.....

2952cf80  02 50 0c 7f 0c 02 0c 25-10 d6 5d 71 5d 7d c4 c4  .P.....%..]q]}..

2952cf90  18 fd 7d c4 c4 18 10 7d-d4 18 ed 5d 71 00 3f 2f  ..}....}...]q.?/

2952cfa0  10 d6 ed 31 30 01 22 26-35 34 36 33 32 1f 00 ed  ...10."&54632...

2952cfb0  02 03 14 16 00 ed 06 23-22 26 35 34 26 35 34 12  .......#"&54&54.

2952cfc0  35 34 36 33 32 1f 00 ed-02 01 4a 30 46 46 30 30  54632.....J0FF00

2952cfd0  45 45 03 07 36 2c 2b 37-07 14 37 2b 2b 37 14 04  EE..6,+7..7++7..

2952cfe0  f2 44 30 30 44 44 30 30-44 fc d4 3c ef 3c 2c 38  .D00DD00D..<.<,8

2952cff0  38 2c 3c ef 3c 5e 01 19-5e 2d 38 38 2d 5e fe e7  8,<.<^..^-88-^..

 

From the above output, an out of bounds memory access is triggered when handling a heap buffer with size 0xc0 and starting at 0x2952cf40.  Next, we search some data from this heap buffer, like |00 02 00 b3| in output.dat.

Figure 4. The Result of Data Searching in output.dat

We find it at offset 0x2d0b, and it only appears once. We then use TTFTemplate.bt in 010 Editor to parse the font file, and find |00 02 00 B3| is in the ‘glyp’ table. Next, we extract the data with size 0xc0 in the ‘glyp’ table from output.dat and output_original.dat respectively, and save them as glyph_poc.dat and glyph_original.dat. The following is the comparison between them.

Figure 5. The Comparison Between glyph_poc.dat And glyph_original.dat

As you can see, there are three differences between them. The 'glyf' table contains the data that defines the appearance of the glyphs in the font. This includes specification of the points that describe the contours that make up a glyph outline, and the instructions that grid-fit that glyph. The 'glyf' table supports the definition of simple glyphs and compound glyphs, that is, glyphs that are made up of other glyphs. The number of glyphs in the font is restricted only by the value stated in the 'head' table. The order in which glyphs are placed in a font is arbitrary.

Each glyph begins with the following header:

Obviously, glyph_poc.dat stores a simple glyph because the first two bytes 0x0002 is greater than zero.

The structure of a simple glyph is shown below:

Based on the specification of the ‘glyph’ table above, we try to parse it by marking different colors as follows.

Figure 6. Parsing of The Simple glyph

Then we trace how Adobe Reader handles the simple glyph. We set the following breakpoint in Windbg.

bu CoolType!CTInit+0x44a61 " .if((poi(poi(poi(esp+0xc))) & 0x0`ffffffff) = 0x0`b3000200) { .printf \"hit:\\n\";} .else {gc}"

 

When the breakpoint is hit, we get the following debug information:

0:021> g

(8de0.6bc4): C++ EH exception - code e06d7363 (first chance)

hit:

eax=097ef6e0 ebx=097ef6d0 ecx=097eeda0 edx=2911e6fc esi=097ef758 edi=097ef756

eip=09598dac esp=2911e610 ebp=2911e674 iopl=0         nv up ei pl zr na pe nc

cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000246

CoolType!CTInit+0x44a61:

09598dac e8f1110000      call    CoolType!CTInit+0x45c57 (09599fa2)

 

0:022> dds esp

2911e610  097eee00 CoolType!CTGetVersion+0x1dd1c8    -->param1

2911e614  097ef510 CoolType!CTGetVersion+0x1dd8d8    -->param2

2911e618  097ef3bc CoolType!CTGetVersion+0x1dd784    -->param3

2911e61c  097ef6e0 CoolType!CTGetVersion+0x1ddaa8    -->param4

2911e620  17e9cd98                                                                  -->param5

2911e624  00000001                                                                -->param6

2911e628  00000002                                                                  -->param7

2911e62c  097eee58 CoolType!CTGetVersion+0x1dd220

2911e630  097eee60 CoolType!CTGetVersion+0x1dd228

2911e634  097ef756 CoolType!CTGetVersion+0x1ddb1e

2911e638  097ef758 CoolType!CTGetVersion+0x1ddb20

2911e63c  2911e6c4

2911e640  2911e6c8

2911e644  097ef6d0 CoolType!CTGetVersion+0x1dda98

2911e648  17e9cdc4

2911e64c  17e9cf84

2911e650  00000015

 

0:022> dd 097ef6e0 L4

097ef6e0  2952cf40 2952cf4a 2952d000 00000000

 

0:022> db 2952cf40  lc0

2952cf40  00 02 00 b3 ff fb 01 bf-05 da 00 0b 00 23 00 55  .............#.U

2952cf50  40 36 06 38 00 1e 12 1e-06 20 25 30 25 50 25 c0  @6.8..... %0%P%.

2952cf60  25 04 80 25 90 25 a0 25-03 09 38 03 18 15 1b 18  %..%.%.%..8.....

2952cf70  38 0f 21 1f 25 2f 25 3f-25 7f 25 04 1f 0c bf 0c  8.!.%/%?%.%.....

2952cf80  02 50 0c 7f 0c 02 0c 25-10 d6 5d 71 5d 7d c4 c4  .P.....%..]q]}..

2952cf90  18 fd 7d c4 c4 18 10 7d-d4 18 ed 5d 71 00 3f 2f  ..}....}...]q.?/

2952cfa0  10 d6 ed 31 30 01 22 26-35 34 36 33 32 1f 00 ed  ...10."&54632...

2952cfb0  02 03 14 16 00 ed 06 23-22 26 35 34 26 35 34 12  .......#"&54&54.

2952cfc0  35 34 36 33 32 1f 00 ed-02 01 4a 30 46 46 30 30  54632.....J0FF00

2952cfd0  45 45 03 07 36 2c 2b 37-07 14 37 2b 2b 37 14 04  EE..6,+7..7++7..

2952cfe0  f2 44 30 30 44 44 30 30-44 fc d4 3c ef 3c 2c 38  .D00DD00D..<.<,8

2952cff0  38 2c 3c ef 3c 5e 01 19-5e 2d 38 38 2d 5e fe e7  8,<.<^..^-88-^..

 

0:022> !heap -p -a 2952cf40 

    address 2952cf40 found in

    _DPH_HEAP_ROOT @ 3d01000

    in busy allocation (  DPH_HEAP_BLOCK:         UserAddr         UserSize -         VirtAddr         VirtSize)

                                                       293c3924:         2952cf40               c0 -         2952c000             2000

    6a749abc verifier!AVrfDebugPageHeapAllocate+0x0000023c

    7749d836 ntdll!RtlDebugAllocateHeap+0x0000003c

    773ffb40 ntdll!RtlpAllocateHeap+0x000000f0

    773fdecb ntdll!RtlpAllocateHeapInternal+0x0000027b

    773fdc2e ntdll!RtlAllocateHeap+0x0000002e

    6854ed63 MSVCR120!malloc+0x00000049 [f:\dd\vctools\crt\crtw32\heap\malloc.c @ 92]

    095550fc CoolType!CTInit+0x00000db1

    095589db CoolType!CTInit+0x00004690

    ...

 

From the above output, the heap buffer at address 0x2952cf40 with size 0xc0 stores the simple glyph. It matches the data in glyph_poc.dat. The address 0x2952cf4a points to endPtsOfContours[2], and the address 0x2952d000 points to the end of the simple glyph.

The following are the values of some fields in the simple glyph.

endPtsOfContours [0]:   00 0b  

endPtsOfContours [1]:  00 23

instructionLength: 00 55 

instructions[n]:   40 36 06 38 00 1e 12 1e-06 20 25 30 25 50 25 c0 

                              25 04 80 25 90 25 a0 25-03 09 38 03 18 15 1b 18

                              38 0f 21 1f 25 2f 25 3f-25 7f 25 04 1f 0c bf 0c

                             02 50 0c 7f 0c 02 0c 25-10 d6 5d 71 5d 7d c4 c4 

                            18 fd 7d c4 c4 18 10 7d-d4 18 ed 5d 71 00 3f 2f 

                            10 d6 ed 31 30

Flags[]: ……
xCoordinates[ ]
: …….
yCoordinates[ ]: …….

 

We continued to trace how the ‘flags’ field in the simple glyph are handled. In IDA Pro, the following is the code branch of handling the ‘flags’ field. Its length is 0x24(endPtsOfContours [1] + 1=0x23+0x1=0x24).

Figure 7. The Branch of Handling the ‘flags’ Field

In Windbg, we get the following debug info:

0:022> bu 0959a132

 

0:022> g

Breakpoint 1 hit

eax=0000004d ebx=097eee00 ecx=00000000 edx=00000024 esi=2952cfa5 edi=00000000

eip=0959a132 esp=2911e5f8 ebp=2911e608 iopl=0         nv up ei ng nz ac pe cy

cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000297

CoolType!CTInit+0x45de7:

0959a132 8bca            mov     ecx,edx

 

0:022> db esi

2952cfa5  01 22 26 35 34 36 33 32-1f 00 ed 02 03 14 16 00  ."&54632........

2952cfb5  ed 06 23 22 26 35 34 26-35 34 12 35 34 36 33 32  ..#"&54&54.54632

2952cfc5  1f 00 ed 02 01 4a 30 46-46 30 30 45 45 03 07 36  .....J0FF00EE..6

2952cfd5  2c 2b 37 07 14 37 2b 2b-37 14 04 f2 44 30 30 44  ,+7..7++7...D00D

2952cfe5  44 30 30 44 fc d4 3c ef-3c 2c 38 38 2c 3c ef 3c  D00D..<.<,88,<.<

2952cff5  5e 01 19 5e 2d 38 38 2d-5e fe e7 ?? ?? ?? ?? ??  ^..^-88-^..?????

2952d005  ?? ?? ?? ?? ?? ?? ?? ??-?? ?? ?? ?? ?? ?? ?? ??  ????????????????

2952d015  ?? ?? ?? ?? ?? ?? ?? ??-?? ?? ?? ?? ?? ?? ?? ??  ????????????????

 

0:022> db ebx

097eee00  01 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................

097eee10  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................

097eee20  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................

097eee30  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................

097eee40  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................

097eee50  00 00 00 00 00 00 00 00-00 00 0c 00 00 00 00 00  ................

097eee60  0b 00 23 00 00 00 00 00-00 00 00 00 00 00 00 00  ..#.............

097eee70  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................

 

The buffer at address 0x097eee00 is used to store the result of parsing the ‘flags’ field.

The following C code in function sub_8049FA2 from IDA Pro is used to handle the ‘flags’ field:

//parse the Flags field which length is 0x24, the result is stored in param a1.  v20 points to the offset of data in the glyph object.

do 

      {

        if ( v14 )

        {

          v28 -= v14;

          v54 = v28;

          if ( v28 < 0 )

            return 5133;

          if ( v14 )

          {

            LOBYTE(v50) = *(v27 - 1);

            memset(v27, v50, v14);

            v27 += v14;

            do

              --v14;

            while ( v14 );

            v28 = v54;

          }

        }

        else

        {

          v29 = *(_BYTE *)v20;

          *v27 = *(_BYTE *)v20;

          if ( v29 & 8 )

          {

            if ( ++v20 > *(_DWORD *)(a4 + 8) )

              return 5133;

            v14 = *(_BYTE *)v20;

          }

          ++v20;

          ++v27;

          --v28;

          if ( v20 > *(_DWORD *)(a4 + 8) )

            return 5133;

        }

      }

      while ( v28 > 0 );

      if ( v14 )

        return 5121;

      v30 = v55;

      v31 = 0;

      v32 = (char *)a1;  // a1 stores the result of parsing Flags field. |01 22 26 35 34 36 33 32 1f ed ed ed 03 14 16 00 ed ed ed ed ed ed ed 23 22 26 35 34 26 35 34 12 35 34 36 33|

      v33 = 0;

      v51 = a1;

      v48 = 0;

 

After the parsing of the ‘flags’ field is finished, we see the following debug info:

0:022> t

eax=097ef6e0 ebx=097eee24 ecx=00000000 edx=00000006 esi=2952cfc4 edi=00060000

eip=0959a1ab esp=2911e5f8 ebp=2911e608 iopl=0         nv up ei pl zr na pe nc

cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000246

CoolType!CTInit+0x45e60:

0959a1ab 8b7d30          mov     edi,dword ptr [ebp+30h] ss:002b:2911e638=00000024                       

 

0:022> db ebx-24 L30

097eee00  01 22 26 35 34 36 33 32-1f ed ed ed 03 14 16 00  ."&54632........

097eee10  ed ed ed ed ed ed ed 23-22 26 35 34 26 35 34 12  .......#"&54&54.

097eee20  35 34 36 33 00 00 00 00-00 00 00 00 00 00 00 00  5463............               

 

0:022> db esi

2952cfc4  32 1f 00 ed 02 01 4a 30-46 46 30 30 45 45 03 07  2.....J0FF00EE..

2952cfd4  36 2c 2b 37 07 14 37 2b-2b 37 14 04 f2 44 30 30  6,+7..7++7...D00

2952cfe4  44 44 30 30 44 fc d4 3c-ef 3c 2c 38 38 2c 3c ef  DD00D..<.<,88,<.

2952cff4  3c 5e 01 19 5e 2d 38 38-2d 5e fe e7 ?? ?? ?? ??  <^..^-88-^..????

2952d004  ?? ?? ?? ?? ?? ?? ?? ??-?? ?? ?? ?? ?? ?? ?? ??  ????????????????

2952d014  ?? ?? ?? ?? ?? ?? ?? ??-?? ?? ?? ?? ?? ?? ?? ??  ????????????????

2952d024  ?? ?? ?? ?? ?? ?? ?? ??-?? ?? ?? ?? ?? ?? ?? ??  ????????????????

2952d034  ?? ?? ?? ?? ?? ?? ?? ??-?? ?? ?? ?? ?? ?? ?? ??  ????????????????

 

From Figure 5, we can see that three different bytes are located in the ‘flags’ field.  This leads to a very different result. 

The result is stored in a buffer at address 0x097eee00, and its size is 0x24. Then the buffer is used to parse the xCoordinates[ ] and yCoordinates[ ] fields.  The following C code in function sub_8049FA2 from IDA Pro is used to handle the xCoordinates[ ] field:

v48 = 0;

      if ( v55 > 0 )  //handling xCoordinates, the result is stored in param a3. 

        {

        v34 = 0;

        do

        {

          v35 = *v32;

          if ( v35 & 2 )

          {

            v36 = (v35 & 0x10) == 0;

            v37 = *(_BYTE *)v20;

            if ( v36 )

              v34 -= v37;

            else

              v34 += v37;

            ++v20;

          }

          else if ( !(v35 & 0x10) )

          {

            v38 = *(_BYTE *)(v20 + 1);

            v34 += _byteswap_ushort(*(_WORD *)v20);

            v33 = v48;

            v20 += 2;

          }

          *(_DWORD *)a3 = v34;

          a3 += 4;

          v32 = (char *)v51 + 1;

          v51 = (char *)v51 + 1;

          v30 = v55;

          if ( v20 > *(_DWORD *)(a4 + 8) )

            return 5133;

          v48 = ++v33;

        }

        while ( v33 < v55 );

      }

 

After handling ‘xCoordinates’ is finished, we find the following debug info:

0:022> t

eax=097eee24 ebx=00000000 ecx=00000024 edx=0000efeb esi=2952cfeb edi=00000024

eip=0959a224 esp=2911e5f8 ebp=2911e608 iopl=0         nv up ei pl zr na pe nc

cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000246

CoolType!CTInit+0x45ed9:

0959a224 895d28          mov     dword ptr [ebp+28h],ebx ss:002b:2911e630=097eee24

 

0:022> db esi

2952cfeb  3c ef 3c 2c 38 38 2c 3c-ef 3c 5e 01 19 5e 2d 38  <.<,88,<.<^..^-8

2952cffb  38 2d 5e fe e7 ?? ?? ??-?? ?? ?? ?? ?? ?? ?? ??  8-^..???????????

2952d00b  ?? ?? ?? ?? ?? ?? ?? ??-?? ?? ?? ?? ?? ?? ?? ??  ????????????????

2952d01b  ?? ?? ?? ?? ?? ?? ?? ??-?? ?? ?? ?? ?? ?? ?? ??  ????????????????

 

0:022> dd 097ef3bc L40

097ef3bc  0000321f 0000321f 00003132 00003132

097ef3cc  00003132 00003134 00003135 0000317f

097ef3dc  000031af 000077f5 ffffa825 ffffed6a

097ef3ec  ffffed67 ffffed67 ffffed6e 0000239a

097ef3fc  00004ed1 000055e5 ffff8d10 ffffb847

097ef40c  ffffcc4b ffffbe8f ffffeebf ffffee7b

097ef41c  ffffee37 ffffee07 ffffee07 ffffee07

097ef42c  ffffedd7 ffffedd7 ffffedd7 ffffee1b

097ef43c  ffffee1b ffffee1b ffffef17 ffffefeb

097ef44c  00000000 00000000 00000000 00000000

097ef45c  00000000 00000000 00000000 00000000

097ef46c  00000000 00000000 00000000 00000000

097ef47c  00000000 00000000 00000000 00000000

097ef48c  00000000 00000000 00000000 00000000

097ef49c  00000000 00000000 00000000 00000000

097ef4ac  00000000 00000000 00000000 00000000

 

Next, the following C code in function sub_8049FA2 from IDA Pro is used to handle the yCoordinates[ ] field:

if ( v30 > 0 )   // handle yCoordinates, the result is stored in param a2. 

      {    //v20 points to the following buffer.

                  2952cfeb  3c ef 3c 2c 38 38 2c 3c-ef 3c 5e 01 19 5e 2d 38  <.<,88,<.<^..^-8

                  2952cffb  38 2d 5e fe e7 ?? ?? ??-?? ?? ?? ?? ?? ?? ?? ??  8-^..???????????

 

        v39 = (char *)a1; // a1 stores the result of parsing the Flags field. |01 22 26 35 34 36 33 32 1f ed ed ed 03 14 16 00 ed ed ed ed ed ed ed 23 22 26 35 34 26 35 34 12 35 34 36 33|

 

        v40 = 0;

        v41 = a4;

        do

        {

          v42 = *v39;

          if ( v42 & 4 )

          {

            v36 = (v42 & 0x20) == 0;

            v43 = *(_BYTE *)v20;   //crash here!

            if ( v36 )

              v40 -= v43;

            else

              v40 += v43;

            ++v20;

          }

          else if ( !(v42 & 0x20) )

          {

            v44 = *(_BYTE *)(v20 + 1);

            v40 += _byteswap_ushort(*(_WORD *)v20);

            v41 = a4;

            v20 += 2;

          }

          v45 = (_DWORD *)a2;

          a2 += 4;

          *v45 = v40;

          *(_BYTE *)a1 &= 1u;

          v39 = (char *)a1 + 1;

          a1 = (char *)a1 + 1;

          if ( v20 > *(_DWORD *)(v41 + 8) )

            return 5133;

        }

        while ( ++v31 < v55 );  //v55 is 0x24

      }

 

When the crash occurs, we see the following debug info, shown below:

0:022> t

eax=097eeeed ebx=00000015 ecx=097ef6e0 edx=0000cc6c esi=2952d000 edi=00000024

eip=0959a23c esp=2911e5f8 ebp=2911e608 iopl=0         nv up ei pl nz na po nc

cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000202

CoolType!CTInit+0x45ef1:

0959a23c 0fb606          movzx   eax,byte ptr [esi]         ds:002b:2952d000=??

 

0:022> t

(8de0.6bc4): Access violation - code c0000005 (first chance)

First chance exceptions are reported before any exception handling.

This exception may be expected and handled.

eax=097eeeed ebx=00000015 ecx=097ef6e0 edx=0000cc6c esi=2952d000 edi=00000024

eip=0959a23c esp=2911e5f8 ebp=2911e608 iopl=0         nv up ei pl nz na po nc

cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00010202

CoolType!CTInit+0x45ef1:

0959a23c 0fb606          movzx   eax,byte ptr [esi]         ds:002b:2952d000=??

 

0:022> dd 097ef510

097ef510  00003cef 00003cef 00003d2b 00003d57

097ef520  00003d8f 00003dc7 00003dc7 00003dc7

097ef530  00003d9b 00003dd7 00003ec6 00003f02

097ef540  ffff9d03 ffff9cea ffff9c8c ffffc9c4

097ef550  ffffc9fc ffffca29 ffffca87 ffffcb85

097ef560  ffffcc6c 00000000 00000000 00000000

097ef570  00000000 00000000 00000000 00000000

097ef580  00000000 00000000 00000000 00000000

 

0:022> db esi L10

2952d000  ?? ?? ?? ?? ?? ?? ?? ??-?? ?? ?? ?? ?? ?? ?? ??  ????????????????

 

Because ebx is smaller than edi, the loop condition is still true and the program continues to parse the yCoordinates[ ] field. While esi points to the end of the simple glyph, it causes an out of bounds memory access.

In summary, this vulnerability is a typical heap buffer overflow vulnerability. In particular, the vulnerability is caused by a crafted PDF file which includes an invalid font file. It causes an out of bounds memory access, due to improper bounds checking when manipulating an array pointer. Attackers can exploit the vulnerability by using the out of bounds access for unintended reads, writes, or frees – potentially leading to code corruption, control-flow hijack, or information leak attack.

Mitigation

All users of Adobe Acrobat and Reader are encouraged to upgrade to the latest version of these software. Additionally, organizations that have deployed Fortinet IPS solutions are already protected from this vulnerability with the signature Adobe.Acrobat.Reader.CoolType.TTF.Memory.Corruption.