Talos Vulnerability Report

TALOS-2024-2064

Adobe Acrobat Reader Font gvar per-tuple-variation-table Out-Of-Bounds Read Vulnerability

December 11, 2024
CVE Number

CVE-2024-49532

SUMMARY

An out-of-bounds read vulnerability exists in font handling code of Adobe Acrobat Reader 2024.002.21005. A specially crafted font file embedded into a PDF can trigger an out of bounds memory read which can lead to disclosure of sensitive information and aid further exploitation. An attacker needs to trick the user into opening the malicious file to trigger this vulnerability.

CONFIRMED VULNERABLE VERSIONS

The versions below were either tested or verified to be vulnerable by Talos or confirmed to be vulnerable by the vendor.

Adobe Acrobat Reader 2024.002.21005

PRODUCT URLS

Acrobat Reader - https://acrobat.adobe.com/us/en/acrobat/pdf-reader.html

CVSSv3 SCORE

6.5 - CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:N/A:N

CWE

CWE-125 - Out-of-bounds Read

DETAILS

Adobe Acrobat Reader is one of the most popular and feature-rich PDF readers on the market. It has a large user base and is usually a default PDF reader on systems. It also integrates into web browsers as a plugin for rendering PDFs.

Adobe Acrobat supports parsing of embedded font files in the PDF. This vulnerability is related to OpenType font format. An OpenType font file starts with a table directory (TableDirectory ) followed by one or more table record (TableRecord) entries. The structure of TableDirectory is as follows:

Offset Size   Name
------ ----- --------------------------------------
0x00    0x04  sfntVersion (0x00010000 or 0x4F54544F  )
0x04    0x02  numTables
0x06    0x02  searchRange
0x08    0x02  entrySelector
0x0c    0x02  rangeShift

If the value of the sfntVersion field is 0x00010000 or 0x74727565, the font contains TrueType data. The CFF data will be present if the value of sfntVersion is 0x4F54544F (‘OTTO). The numTables field specifies the number of TableRecord entries present in the font file. The structure of a TableRecord entry is as follows:

Offset Size   Name
------ ----- ----------------------------------
0x00    0x04  tableTag
0x04    0x04  tableChecksum
0x08    0x04  tableOffset
0x0C    0x04  tableLength

tableTag is the name of TableRecord. The tableOffset field specifies the offset of the table from the beginning of the file. The tableLength indicates the length of the table. The structure of each TableRecord depends on the type table, which is defined by the tableTag. This vulnerability occurs when the the value of the tableTag field is the string gvar, which indicates the table type is an glyph variations (gvar) table.

The gvar table contains a header followed by shared tuple records (sharedTuples) and glyph variation data tables (GlyphVariationDataTables). The structure of the gvar table header is as follows:

Offset Size   Name
------ ----- --------------------------------------
0x00    0x02             gvar_majorVersion
0x02    0x02             gvar_minorVersion
0x04    0x02             gvar_axisCount
0x06    0x02             sharedTupleCount
0x08    0x04             sharedTuplesOffset
0x0c    0x02             gvar_glyphCount (gc)
0x0e    0x02             gvar_flags (gc_size = 4 if flags else 2)
0x10    0x04             glyphVariationDataArrayOffset
0x14    gc+1 * gc_size   glyphVariationDataOffsets 

The gvar_axisCount field gives the number of variation axes for this font. gvar_glyphCount indicates the number of glyphs present in the font. glyphVariationDataArrayOffset defines the byte offset from the beginning of this table to GlyphVariationDataTables.

The glyphVariationDataOffsets is an array that contains 2-byte or 4-byte offsets. If gvar_flags is 0, the offsets in glyphVariationDataOffsets are 2 bytes each; otherwise, they are 4 bytes. Note that if the offset size is 2 bytes, the stored value represents half of the actual offset value. Therefore, when gvar_flags is 0, the true offset value is obtained by doubling the offset value. The number of offsets present in glyphVariationDataOffsets is gvar_glyphCount + 1.

GlyphVariationDataTables is an array that contains GlyphVariationData tables.

For simplicity, consider the scenario where gvar_flags is 0, gvar_glyphCount is 3, and the values of glyphVariationDataOffsets are as follows:

glyphVariationDataOffsets = [0x04,  0x08, 0x0A, 0x0F]

In this example, the offset from the beginning of this table to the first GlyphVariationData is glyphVariationDataArrayOffset+ 0x04 * 2 and the size of the first GlyphVariationData (namely GlyphVariationData_size) is 0x08 (0x08*2- 0x04*2= 0x08 ). Here, GlyphVariationData_size indicates the size of GlyphVariationData calculated by subtracting two consecutive offsets from the glyphVariationDataOffsets array.

The structure of the GlyphVariationData table is comprised of a header followed by serialized data. The structure of the GlyphVariationData header is as follows:

Offset Size       Name
------ -------- --------------------------------------
0x00    0x02                 tupleVariationCount (tc = tupleVariationCount & 0xfff )
0x02    0x02                 tvs_dataOffset
0x04    total_tvh_size       TupleVariationHeaders 

The low 12 bits of tupleVariationCount indicates the number of tuple variation tables for this glyph. tvs_dataOffset indicates the offset from the start of the GlyphVariationData table to the serialized data. The TupleVariationHeaders contains an array of TupleVariationHeader.

The total number of TupleVariationHeader present in TupleVariationHeaders is indicated by tc. Here, total_tvh_size is the sum of the size of each TupleVariationHeader like (total_tvh_size = tvh_size_1 + tvh_size_2 + ... + tvh_size_tc ). tvh_size_1 is the size of the first TupleVariationHeader, tvh_size_2 is the size of the second TupleVariationHeader, and so on.

The structure of TupleVariationHeader is as follows:

Offset Size   Name
------ ----- --------------------------------------
0x00    0x02             tvh_variationDataSize
0x02    0x02             tvh_tupleIndex

Note that TupleVariationHeader contains other optional fields which are omitted here for brevity.

tvh_variationDataSize indicates the size of a serialized data block. The size of TupleVariationHeader (tvh_size) is variable. It can be calculated using the following python pseudo code:

def get_TupleVariationHeader_size(tvh_tupleIndex, gvar_axisCount):
    size = 4
    if (tvh_tupleIndex & 0x8000) != 0:
        size += axisCount * 2
    if (tvh_tupleIndex & 0x4000) != 0:
        size += axisCount * 4
    return size

In our case, the vulnerable GlyphVariationData contains the following data:

d2e9afe0  00 02 00 0c 00 0d 20 01-00 07 20 00 03 02 2e 05  
d2e9aff0  05 81 40 01 50 81 00 22-01 00 04 80 40 00 c6 00 

Here, the value of tupleVariationCount is 0x02. Note that the low 12 bits of tupleVariationCount is 0 here. It means TupleVariationHeaders contains two TupleVariationHeader and the values of each TupleVariationHeader are as follows:

TupleVariationHeader_1 
    tvh_variationDataSize_1 = 0x0d
    tvh_tupleIndex_1 =  0x2001
    tvh_size_1    =  4

TupleVariationHeader_2
    tvh_variationDataSize_2 = 0x07
    tvh_tupleIndex_2 =  0x2000
    tvh_size_2    =  4

Here, total_tvh_size is 0x08 (tvh_size_1 + tvh_size_2). The value of tvh_size_1 and tvh_size_2 is calculated using the function get_TupleVariationHeader_size.

After the GlyphVariationData table header, a block of serialized data is present. A serialized data block begins with the optional shared point number data (shared-point-numbers) followed by the variation data for the tuple variation tables (per-tuple-variation-table). The optional shared-point-numbers data is present when (tupleVariationCount & 0x8000) is non-zero. Here, (tupleVariationCount & 0x8000) is zero, so shared-point-numbers is not present.

The per-tuple-variation-table data begins with the optional private-point-numbers if (tvh_tupleIndex & 0x2000) is non-zero. The private-point-numbers data is represented as packed point numbers.

The total length of the private-point-numbers data can be calculated using the following python pseudo-code:

def get_total_length_of_ppn(private_point_number):
    total_ppn_count = 0

    if private_point_number[0] & 0x80:
        total_ppn_count =  (private_point_number[1] | ((private_point_number[0] & 0x7F) << 8) ) + 2
    else:
        total_ppn_count =  private_point_number[0] + 1 

    return total_ppn_count

The vulnerability occurs when private-point-numbers data is present in the per-tuple-variation-table. The application reads the private-point-numbers and increments the buffer by total_ppn_count. However, when it calls a function to read the remaining data of the per-tuple-variation-table, it incorrectly passes tvh_variationDataSize instead of (tvh_variationDataSize - total_ppn_count), which leads to an out-of-bounds read condition.

Note that, this vulnerability is triggered with a valid font file because the application fails to account for the size of private-point-numbers when reading the per-tuple-variation-table.

In our Poc, the vulnerability occurs when reading the 0x09 GlyphVariationData table. We can observe the following in the debugger (with PageHeap enabled):

0:002> p
Time Travel Position: 9ECAC:10D6
eax=e2b04d54 ebx=e7976fb0 ecx=00000013 edx=c3860008 esi=7007caa0 edi=00000008
eip=701369fa esp=0040bfe0 ebp=0040c01c 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!CTCleanup+0xd11ea:
701369fa 8d3c7d14000000  lea     edi,[edi*2+14h]     ;<---------------------------- (1)
0:002> p
Time Travel Position: 9ECAC:10D7
eax=e2b04d54 ebx=e7976fb0 ecx=00000013 edx=c3860008 esi=7007caa0 edi=00000024
eip=70136a01 esp=0040bfe0 ebp=0040c01c 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!CTCleanup+0xd11f1:
70136a01 57              push    edi
0:002> p
Time Travel Position: 9ECAC:10D8
eax=e2b04d54 ebx=e7976fb0 ecx=00000013 edx=c3860008 esi=7007caa0 edi=00000024
eip=70136a02 esp=0040bfdc ebp=0040c01c 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!CTCleanup+0xd11f2:
70136a02 ff750c          push    dword ptr [ebp+0Ch]  ss:002b:0040c028=0040c048
0:002> p
Time Travel Position: 9ECAC:10D9
eax=e2b04d54 ebx=e7976fb0 ecx=00000013 edx=c3860008 esi=7007caa0 edi=00000024
eip=70136a05 esp=0040bfd8 ebp=0040c01c 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!CTCleanup+0xd11f5:
70136a05 8bce            mov     ecx,esi
0:002> p
Time Travel Position: 9ECAC:10DA
eax=e2b04d54 ebx=e7976fb0 ecx=7007caa0 edx=c3860008 esi=7007caa0 edi=00000024
eip=70136a07 esp=0040bfd8 ebp=0040c01c 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!CTCleanup+0xd11f7:
70136a07 53              push    ebx
0:002> p
Time Travel Position: 9ECAC:10DB
eax=e2b04d54 ebx=e7976fb0 ecx=7007caa0 edx=c3860008 esi=7007caa0 edi=00000024
eip=70136a08 esp=0040bfd4 ebp=0040c01c 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!CTCleanup+0xd11f8:
70136a08 ff15ac262a70    call    dword ptr [CoolType!CTGetVersion+0x14faac (702a26ac)] ds:002b:702a26ac={ntdll!LdrpValidateUserCallTarget (76ff8a10)}
0:002> 
Time Travel Position: 9ECAC:10E7
eax=0e00f954 ebx=e7976fb0 ecx=7007caa0 edx=00100000 esi=7007caa0 edi=00000024
eip=70136a0e esp=0040bfd4 ebp=0040c01c iopl=0         nv up ei pl zr na pe cy
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000247
CoolType!CTCleanup+0xd11fe:
70136a0e ffd6            call    esi {CoolType!CTCleanup+0x17290 (7007caa0)} ; <---------------- (2)
0:002> 
Time Travel Position: 9ECAC:10FE
eax=000009d6 ebx=e7976fb0 ecx=000000d6 edx=00000024 esi=7007caa0 edi=00000024
eip=70136a10 esp=0040bfd4 ebp=0040c01c 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!CTCleanup+0xd1200:
70136a10 8b730c          mov     esi,dword ptr [ebx+0Ch] ds:002b:e7976fbc=7007caa0 ; <---------------- (3)

[...]

0:002> 
Time Travel Position: 9ECAC:1107
eax=00000026 ebx=e7976fb0 ecx=7007caa0 edx=00000024 esi=7007caa0 edi=00000024
eip=70136a25 esp=0040bfd4 ebp=0040c01c iopl=0         nv up ei pl nz na pe nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000206
CoolType!CTCleanup+0xd1215:
70136a25 ff15ac262a70    call    dword ptr [CoolType!CTGetVersion+0x14faac (702a26ac)] ds:002b:702a26ac={ntdll!LdrpValidateUserCallTarget (76ff8a10)}
0:002> 
Time Travel Position: 9ECAC:1113
eax=0e00f954 ebx=e7976fb0 ecx=7007caa0 edx=00100000 esi=7007caa0 edi=00000024
eip=70136a2b esp=0040bfd4 ebp=0040c01c iopl=0         nv up ei pl zr na pe cy
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000247
CoolType!CTCleanup+0xd121b:
70136a2b ffd6            call    esi {CoolType!CTCleanup+0x17290 (7007caa0)}
0:002> 
Time Travel Position: 9ECAC:112A
eax=000009e6 ebx=e7976fb0 ecx=000000e6 edx=00000026 esi=7007caa0 edi=00000024
eip=70136a2d esp=0040bfd4 ebp=0040c01c 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!CTCleanup+0xd121d: 
70136a2d 8bf8            mov     edi,eax                                        ; <---------------- (4)
0:002> p
Time Travel Position: 9ECAC:112B
eax=000009e6 ebx=e7976fb0 ecx=000000e6 edx=00000026 esi=7007caa0 edi=000009e6
eip=70136a2f esp=0040bfd4 ebp=0040c01c 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!CTCleanup+0xd121f:
70136a2f 83c40c          add     esp,0Ch
0:002> p
Time Travel Position: 9ECAC:112C
eax=000009e6 ebx=e7976fb0 ecx=000000e6 edx=00000026 esi=7007caa0 edi=000009e6
eip=70136a32 esp=0040bfe0 ebp=0040c01c iopl=0         nv up ei pl nz ac po nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000212
CoolType!CTCleanup+0xd1222:
70136a32 03ff            add     edi,edi                                     ;<--------------------- (5)
0:002> 
Time Travel Position: 9ECAC:112D
eax=000009e6 ebx=e7976fb0 ecx=000000e6 edx=00000026 esi=7007caa0 edi=000013cc
eip=70136a34 esp=0040bfe0 ebp=0040c01c iopl=0         nv up ei pl nz na pe nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000206
CoolType!CTCleanup+0xd1224:
70136a34 2b7df8          sub     edi,dword ptr [ebp-8] ss:002b:0040c014=000013ac ;<----------------- (6)
0:002> 
Time Travel Position: 9ECAC:112E
eax=000009e6 ebx=e7976fb0 ecx=000000e6 edx=00000026 esi=7007caa0 edi=00000020
eip=70136a37 esp=0040bfe0 ebp=0040c01c 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!CTCleanup+0xd1227:
70136a37 897df4          mov     dword ptr [ebp-0Ch],edi ss:002b:0040c010=00000050
0:002> p
Time Travel Position: 9ECAC:112F
eax=000009e6 ebx=e7976fb0 ecx=000000e6 edx=00000026 esi=7007caa0 edi=00000020
eip=70136a3a esp=0040bfe0 ebp=0040c01c 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!CTCleanup+0xd122a:
70136a3a 0f846e010000    je      CoolType!CTCleanup+0xd139e (70136bae)   [br=0]
[...]
0:002> p
Time Travel Position: 9ECAC:114C
eax=000022ae ebx=e7976fb0 ecx=e7976fb0 edx=01010010 esi=70264880 edi=00000020
eip=70136a4f esp=0040bfe0 ebp=0040c01c iopl=0         nv up ei pl nz ac pe cy
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000217
CoolType!CTCleanup+0xd123f:
70136a4f 3bf8            cmp     edi,eax                                 ;<------------------(7)
0:002> p
Time Travel Position: 9ECAC:114D
eax=000022ae ebx=e7976fb0 ecx=e7976fb0 edx=01010010 esi=70264880 edi=00000020
eip=70136a51 esp=0040bfe0 ebp=0040c01c 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!CTCleanup+0xd1241:
70136a51 0f8757010000    ja      CoolType!CTCleanup+0xd139e (70136bae)   [br=0]

At (1), the value of edi serves as an index, determining which entry of the GlyphVariationData table will be accessed (the index values start at 0). The method, called at (2), reads the offsets from the glyphVariationDataOffsets. eax at (3) and (4) contains two consecutive offset values. Here, gvar_flags is 0, so the actual offset value is obtained by doubling the offset value, as shown at (5). GlyphVariationData_size is calculated at (6) and its value is 0x20. At (7), a check is performed to ensure GlyphVariationData_size is smaller than tableLength.

0:002> p
Time Travel Position: 9ECAC:1151
eax=00000020 ebx=e7976fb0 ecx=e7976fb0 edx=01010010 esi=703759fc edi=e2b04d54
eip=70136a60 esp=0040bfe0 ebp=0040c01c 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!CTCleanup+0xd1250:
70136a60 50              push    eax                                       ;<---------------------------- (8)
0:002> p
Time Travel Position: 9ECAC:1152
eax=00000020 ebx=e7976fb0 ecx=e7976fb0 edx=01010010 esi=703759fc edi=e2b04d54
eip=70136a61 esp=0040bfdc ebp=0040c01c 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!CTCleanup+0xd1251:
70136a61 894708          mov     dword ptr [edi+8],eax ds:002b:e2b04d5c=00000000
0:002> p
Time Travel Position: 9ECAC:1153
eax=00000020 ebx=e7976fb0 ecx=e7976fb0 edx=01010010 esi=703759fc edi=e2b04d54
eip=70136a64 esp=0040bfdc ebp=0040c01c 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!CTCleanup+0xd1254:
70136a64 56              push    esi
0:002> p
Time Travel Position: 9ECAC:1154
eax=00000020 ebx=e7976fb0 ecx=e7976fb0 edx=01010010 esi=703759fc edi=e2b04d54
eip=70136a65 esp=0040bfd8 ebp=0040c01c 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!CTCleanup+0xd1255:
70136a65 8b36            mov     esi,dword ptr [esi]  ds:002b:703759fc=70077eb0
0:002> p
Time Travel Position: 9ECAC:1155
eax=00000020 ebx=e7976fb0 ecx=e7976fb0 edx=01010010 esi=70077eb0 edi=e2b04d54
eip=70136a67 esp=0040bfd8 ebp=0040c01c 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!CTCleanup+0xd1257:
70136a67 8bce            mov     ecx,esi
0:002> p
Time Travel Position: 9ECAC:1156
eax=00000020 ebx=e7976fb0 ecx=70077eb0 edx=01010010 esi=70077eb0 edi=e2b04d54
eip=70136a69 esp=0040bfd8 ebp=0040c01c 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!CTCleanup+0xd1259:
70136a69 ff15ac262a70    call    dword ptr [CoolType!CTGetVersion+0x14faac (702a26ac)] ds:002b:702a26ac={ntdll!LdrpValidateUserCallTarget (76ff8a10)}
0:002> p
Time Travel Position: 9ECAC:1162
eax=0e00efd6 ebx=e7976fb0 ecx=70077eb0 edx=01440000 esi=70077eb0 edi=e2b04d54
eip=70136a6f esp=0040bfd8 ebp=0040c01c iopl=0         nv up ei pl zr na pe cy
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000247
CoolType!CTCleanup+0xd125f:
70136a6f ffd6            call    esi {CoolType!CTCleanup+0x126a0 (70077eb0)} ;<---------------------------- (9)
0:002> p
Time Travel Position: 9ECB0:EEF
eax=d2e9afe0 ebx=e7976fb0 ecx=00000020 edx=00000000 esi=70077eb0 edi=e2b04d54
eip=70136a71 esp=0040bfd8 ebp=0040c01c iopl=0         nv up ei ng nz na po nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000282
CoolType!CTCleanup+0xd1261:
70136a71 894704          mov     dword ptr [edi+4],eax ds:002b:e2b04d58=00000000
0:002> dd eax                                                                ;<---------------------------- (10)
d2e9afe0  c0c0c0c0 c0c0c0c0 c0c0c0c0 c0c0c0c0
d2e9aff0  c0c0c0c0 c0c0c0c0 c0c0c0c0 c0c0c0c0
d2e9b000  ???????? ???????? ???????? ????????

[...]

0:002> p
Time Travel Position: 9ECB0:F36
eax=c3866d50 ebx=e7976fb0 ecx=00000e4e edx=00014000 esi=70077f80 edi=e2b04d54
eip=70136ac9 esp=0040bfd8 ebp=0040c01c iopl=0         nv up ei ng nz na po nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000282
CoolType!CTCleanup+0xd12b9:
70136ac9 ff75f4          push    dword ptr [ebp-0Ch]  ss:002b:0040c010=00000020
0:002> p
Time Travel Position: 9ECB0:F37
eax=c3866d50 ebx=e7976fb0 ecx=00000e4e edx=00014000 esi=70077f80 edi=e2b04d54
eip=70136acc esp=0040bfd4 ebp=0040c01c iopl=0         nv up ei ng nz na po nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000282
CoolType!CTCleanup+0xd12bc:
70136acc ff7704          push    dword ptr [edi+4]    ds:002b:e2b04d58=d2e9afe0
0:002> p
Time Travel Position: 9ECB0:F38
eax=c3866d50 ebx=e7976fb0 ecx=00000e4e edx=00014000 esi=70077f80 edi=e2b04d54
eip=70136acf esp=0040bfd0 ebp=0040c01c iopl=0         nv up ei ng nz na po nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000282
CoolType!CTCleanup+0xd12bf:
70136acf e8ca120000      call    CoolType!CTCleanup+0xd258e (70137d9e)  ;<----------------------- (11)
0:002> p
Time Travel Position: 9ECB0:F72
eax=00000000 ebx=e7976fb0 ecx=00000000 edx=00000000 esi=70077f80 edi=e2b04d54
eip=70136ad4 esp=0040bfd0 ebp=0040c01c 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!CTCleanup+0xd12c4:
70136ad4 8b7324          mov     esi,dword ptr [ebx+24h] ds:002b:e7976fd4=70078fa0
0:002> db d2e9afe0                                                    ;<----------------------- (12)
d2e9afe0  00 02 00 0c 00 0d 20 01-00 07 20 00 03 02 2e 05  ...... ... .....
d2e9aff0  05 81 40 01 50 81 00 22-01 00 04 80 40 00 c6 00  ..@.P.."....@...
d2e9b000  ?? ?? ?? ?? ?? ?? ?? ??-?? ?? ?? ?? ?? ?? ?? ??  ????????????????

The malloc function is called at (9), and the size argument of malloc comes from eax at (8), which is equal to GlyphVariationData_size. This malloc creates the vulnerable buffer, and its value is examined at (10). The memcpy function is called at (11) to copy GlyphVariationData to the vulnerable buffer. The content of the vulnerable buffer can be observed at (11).

eax=00010000 ebx=00000035 ecx=00000006 edx=062449b8 esi=d2e9afec edi=00000020
eip=701146d2 esp=0040bebc ebp=0040bf58 iopl=0         nv up ei pl nz na pe nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000206
CoolType!CTCleanup+0xaeec2:
701146d2 8b45d8          mov     eax,dword ptr [ebp-28h] ss:002b:0040bf30=00000019 
0:002> p
Time Travel Position: 9ECC1:1674
eax=00000019 ebx=00000035 ecx=00000006 edx=062449b8 esi=d2e9afec edi=00000020
eip=701146d5 esp=0040bebc ebp=0040bf58 iopl=0         nv up ei pl nz na pe nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000206
CoolType!CTCleanup+0xaeec5:
701146d5 0fb7c0          movzx   eax,ax    ; <------------------------------------------- (13)
0:002> p
Time Travel Position: 9ECC1:1675
eax=00000019 ebx=00000035 ecx=00000006 edx=062449b8 esi=d2e9afec edi=00000020
eip=701146d8 esp=0040bebc ebp=0040bf58 iopl=0         nv up ei pl nz na pe nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000206
CoolType!CTCleanup+0xaeec8:
701146d8 3bc7            cmp     eax,edi
0:002> p
Time Travel Position: 9ECC1:1676
eax=00000019 ebx=00000035 ecx=00000006 edx=062449b8 esi=d2e9afec edi=00000020
eip=701146da esp=0040bebc ebp=0040bf58 iopl=0         nv up ei ng nz na pe cy
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000287
CoolType!CTCleanup+0xaeeca:
701146da 0f8d6f020000    jge     CoolType!CTCleanup+0xaf13f (7011494f)   [br=0]
0:002> p
Time Travel Position: 9ECC1:1677
eax=00000019 ebx=00000035 ecx=00000006 edx=062449b8 esi=d2e9afec edi=00000020
eip=701146e0 esp=0040bebc ebp=0040bf58 iopl=0         nv up ei ng nz na pe cy
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000287
CoolType!CTCleanup+0xaeed0:
701146e0 8b4dbc          mov     ecx,dword ptr [ebp-44h] ss:002b:0040bf14=d2e9afe0
0:002> p
Time Travel Position: 9ECC1:1678
eax=00000019 ebx=00000035 ecx=d2e9afe0 edx=062449b8 esi=d2e9afec edi=00000020
eip=701146e3 esp=0040bebc ebp=0040bf58 iopl=0         nv up ei ng nz na pe cy
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000287
CoolType!CTCleanup+0xaeed3:
701146e3 03c1            add     eax,ecx                            ; <-------------------------- (14)
0:002> p
Time Travel Position: 9ECC1:1679
eax=d2e9aff9 ebx=00000035 ecx=d2e9afe0 edx=062449b8 esi=d2e9afec edi=00000020
eip=701146e5 esp=0040bebc ebp=0040bf58 iopl=0         nv up ei ng nz na pe nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000286
CoolType!CTCleanup+0xaeed5:
701146e5 f745f000200000  test    dword ptr [ebp-10h],2000h ss:002b:0040bf48=00002000  ; <-------------------------- (15)
0:002> p
Time Travel Position: 9ECC1:167A
eax=d2e9aff9 ebx=00000035 ecx=d2e9afe0 edx=062449b8 esi=d2e9afec edi=00000020
eip=701146ec esp=0040bebc ebp=0040bf58 iopl=0         nv up ei pl nz na pe nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000206
CoolType!CTCleanup+0xaeedc:
701146ec 8945ec          mov     dword ptr [ebp-14h],eax ss:002b:0040bf44=d2e9aff8
0:002> db eax L7                                                    ; <-------------------------- (16)
d2e9aff9  00 04 80 40 00 c6 00                             ...@...
0:002> p
Time Travel Position: 9ECC1:167B
eax=d2e9aff9 ebx=00000035 ecx=d2e9afe0 edx=062449b8 esi=d2e9afec edi=00000020
eip=701146ef esp=0040bebc ebp=0040bf58 iopl=0         nv up ei pl nz na pe nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000206
CoolType!CTCleanup+0xaeedf:
701146ef 0f84a7000000    je      CoolType!CTCleanup+0xaef8c (7011479c)   [br=0]
0:002> p
Time Travel Position: 9ECC1:167C
eax=d2e9aff9 ebx=00000035 ecx=d2e9afe0 edx=062449b8 esi=d2e9afec edi=00000020
eip=701146f5 esp=0040bebc ebp=0040bf58 iopl=0         nv up ei pl nz na pe nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000206
CoolType!CTCleanup+0xaeee5:
701146f5 803800          cmp     byte ptr [eax],0           ds:002b:d2e9aff9=00  ; <-------------------------- (17)
0:002> p
Time Travel Position: 9ECC1:167D
eax=d2e9aff9 ebx=00000035 ecx=d2e9afe0 edx=062449b8 esi=d2e9afec edi=00000020
eip=701146f8 esp=0040bebc ebp=0040bf58 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!CTCleanup+0xaeee8:
701146f8 7570            jne     CoolType!CTCleanup+0xaef5a (7011476a)   [br=0]
0:002> p
Time Travel Position: 9ECC1:167E
eax=d2e9aff9 ebx=00000035 ecx=d2e9afe0 edx=062449b8 esi=d2e9afec edi=00000020
eip=701146fa esp=0040bebc ebp=0040bf58 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!CTCleanup+0xaeeea:
701146fa 33c9            xor     ecx,ecx
0:002> p
Time Travel Position: 9ECC1:167F
eax=d2e9aff9 ebx=00000035 ecx=00000000 edx=062449b8 esi=d2e9afec edi=00000020
eip=701146fc esp=0040bebc ebp=0040bf58 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!CTCleanup+0xaeeec:
701146fc c745cc01000000  mov     dword ptr [ebp-34h],1 ss:002b:0040bf24=00000038
0:002> p
Time Travel Position: 9ECC1:1680
eax=d2e9aff9 ebx=00000035 ecx=00000000 edx=062449b8 esi=d2e9afec edi=00000020
eip=70114703 esp=0040bebc ebp=0040bf58 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!CTCleanup+0xaeef3:
70114703 40              inc     eax                           ; <-------------------------- (18)
0:002> p
Time Travel Position: 9ECC1:1681
eax=d2e9affa ebx=00000035 ecx=00000000 edx=062449b8 esi=d2e9afec edi=00000020
eip=70114704 esp=0040bebc ebp=0040bf58 iopl=0         nv up ei ng nz na pe nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000286
CoolType!CTCleanup+0xaeef4:
70114704 8945ec          mov     dword ptr [ebp-14h],eax ss:002b:0040bf44=d2e9aff9
0:002> p
Time Travel Position: 9ECC1:1682
eax=d2e9affa ebx=00000035 ecx=00000000 edx=062449b8 esi=d2e9afec edi=00000020
eip=70114707 esp=0040bebc ebp=0040bf58 iopl=0         nv up ei ng nz na pe nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000286
CoolType!CTCleanup+0xaeef7:
70114707 e9a7000000      jmp     CoolType!CTCleanup+0xaefa3 (701147b3)
0:002> db eax L7                                                 ; <-------------------------- (19)
d2e9affa  04 80 40 00 c6 00 ??                             ..@...?
0:002> p
Time Travel Position: 9ECC1:1683
eax=d2e9affa ebx=00000035 ecx=00000000 edx=062449b8 esi=d2e9afec edi=00000020
eip=701147b3 esp=0040bebc ebp=0040bf58 iopl=0         nv up ei ng nz na pe nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000286
CoolType!CTCleanup+0xaefa3:
701147b3 8b7dd8          mov     edi,dword ptr [ebp-28h] ss:002b:0040bf30=00000019
[...]


Time Travel Position: 9ECC1:168D
eax=00000035 ebx=00000035 ecx=00000000 edx=00000007 esi=d2e9afec edi=00000020
eip=701147d2 esp=0040bebc ebp=0040bf58 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!CTCleanup+0xaefc2:
701147d2 85c9            test    ecx,ecx
0:002> p
Time Travel Position: 9ECC1:168E
eax=00000035 ebx=00000035 ecx=00000000 edx=00000007 esi=d2e9afec edi=00000020
eip=701147d4 esp=0040bebc ebp=0040bf58 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!CTCleanup+0xaefc4:
701147d4 0f45c1          cmovne  eax,ecx
0:002> p
Time Travel Position: 9ECC1:168F
eax=00000035 ebx=00000035 ecx=00000000 edx=00000007 esi=d2e9afec edi=00000020
eip=701147d7 esp=0040bebc ebp=0040bf58 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!CTCleanup+0xaefc7:
701147d7 8d4d88          lea     ecx,[ebp-78h]
0:002> p
Time Travel Position: 9ECC1:1690
eax=00000035 ebx=00000035 ecx=0040bee0 edx=00000007 esi=d2e9afec edi=00000020
eip=701147da esp=0040bebc ebp=0040bf58 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!CTCleanup+0xaefca:
701147da 51              push    ecx
0:002> p
Time Travel Position: 9ECC1:1691
eax=00000035 ebx=00000035 ecx=0040bee0 edx=00000007 esi=d2e9afec edi=00000020
eip=701147db esp=0040beb8 ebp=0040bf58 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!CTCleanup+0xaefcb:
701147db 52              push    edx                              ; <-------------------------- (20)
0:002> p
Time Travel Position: 9ECC1:1692
eax=00000035 ebx=00000035 ecx=0040bee0 edx=00000007 esi=d2e9afec edi=00000020
eip=701147dc esp=0040beb4 ebp=0040bf58 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!CTCleanup+0xaefcc:
701147dc 50              push    eax
0:002> p
Time Travel Position: 9ECC1:1693
eax=00000035 ebx=00000035 ecx=0040bee0 edx=00000007 esi=d2e9afec edi=00000020
eip=701147dd esp=0040beb0 ebp=0040bf58 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!CTCleanup+0xaefcd:
701147dd 894598          mov     dword ptr [ebp-68h],eax ss:002b:0040bef0=00000000
0:002> p
Time Travel Position: 9ECC1:1694
eax=00000035 ebx=00000035 ecx=0040bee0 edx=00000007 esi=d2e9afec edi=00000020
eip=701147e0 esp=0040beb0 ebp=0040bf58 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!CTCleanup+0xaefd0:
701147e0 8d45ec          lea     eax,[ebp-14h]
0:002> p
Time Travel Position: 9ECC1:1695
eax=0040bf44 ebx=00000035 ecx=0040bee0 edx=00000007 esi=d2e9afec edi=00000020
eip=701147e3 esp=0040beb0 ebp=0040bf58 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!CTCleanup+0xaefd3:
701147e3 57              push    edi
0:002> p
Time Travel Position: 9ECC1:1696
eax=0040bf44 ebx=00000035 ecx=0040bee0 edx=00000007 esi=d2e9afec edi=00000020
eip=701147e4 esp=0040beac ebp=0040bf58 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!CTCleanup+0xaefd4:
701147e4 50              push    eax
0:002> dd eax L8
0040bf44  d2e9affa 00000000 00000020 00000006
0040bf54  00000007 0040c01c 700b1a53 3f442db4
0:002> p
Time Travel Position: 9ECC1:1697
eax=0040bf44 ebx=00000035 ecx=0040bee0 edx=00000007 esi=d2e9afec edi=00000020
eip=701147e5 esp=0040bea8 ebp=0040bf58 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!CTCleanup+0xaefd5:
701147e5 ff75d4          push    dword ptr [ebp-2Ch]  ss:002b:0040bf2c=e14570c8
0:002> p
Time Travel Position: 9ECC1:1698
eax=0040bf44 ebx=00000035 ecx=0040bee0 edx=00000007 esi=d2e9afec edi=00000020
eip=701147e8 esp=0040bea4 ebp=0040bf58 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!CTCleanup+0xaefd8:
701147e8 e883320000      call    CoolType!CTCleanup+0xb2260 (70117a70)   ; <-------------------------- (21)

The above code shows the processing of the serialized data of the second tuple variation table. At (13), the register eax contains the offset value calculated by adding tvs_dataOffset and tvh_variationDataSize of the first table. At (14), the offset is added to the vulnerable buffer to obtain the serialized data block of the second tuple variation table . A check is performed at (15) to determine whether per-tuple-variation-table contains the private-point-numbers data. In this case, per-tuple-variation-table contains private-point-numbers.

At (16) , you can observe the content of per-tuple-variation-table of the second tuple variation table. At (17), the value of ` private_point_number[0] is 0, so the value of total_ppn_count is 1. The vulnerable buffer is increase by total_ppn_count at (18). At (19), the remaining content of per-tuple-variation-table` is examined.

Next, a function is called at (21) to read the remaining fields of per-tuple-variation-table and the size value of this function comes from the eax register at (20). As noted earlier, the total size of of the second per-tuple-variation-table (tvh_variationDataSize_2) is 0x07, but after subtracting the size of private-point-numbers from it, the new size value is 6. The incorrect size value is passed to the function without taking into account the already parsed private-point-numbers. A crash occurs when the function uses this incorrect size to read the remaining fields of the per-tuple-variation-table. This can be observed at the time of the crash.

0:002> g
(2a04.2208): Access violation - code c0000005 (first/second chance not available)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
Time Travel Position: 9ECC2:0
eax=00000007 ebx=0040bf44 ecx=d2e9b000 edx=00000005 esi=00000005 edi=0040bee0
eip=70117ab8 esp=0040be8c ebp=0040be9c 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!CTCleanup+0xb22a8:
70117ab8 0fb601          movzx   eax,byte ptr [ecx]         ds:002b:d2e9b000=??
0:002> u
CoolType!CTCleanup+0xb22a8:
70117ab8 0fb601          movzx   eax,byte ptr [ecx]
70117abb 8945fc          mov     dword ptr [ebp-4],eax
70117abe 8d4101          lea     eax,[ecx+1]
70117ac1 8b4dfc          mov     ecx,dword ptr [ebp-4]
70117ac4 8bf1            mov     esi,ecx
70117ac6 8903            mov     dword ptr [ebx],eax
70117ac8 83e63f          and     esi,3Fh
70117acb 8b07            mov     eax,dword ptr [edi]
0:002> kb
 # ChildEBP RetAddr      Args to Child              
WARNING: Stack unwind information not available. Following frames may be wrong.
00 0040be9c 701147ed     e14570c8 0040bf44 00000020 CoolType!CTCleanup+0xb22a8
01 0040bf58 700b1a53     3f442db4 e144f2c8 e2b04bf8 CoolType!CTCleanup+0xaefdd
02 0040c01c 700afb28     e2b04bf8 3f442db4 e2b04d68 CoolType!CTCleanup+0x4c243
03 0040c10c 700aefa3     3f442c18 3f442d88 3f442d48 CoolType!CTCleanup+0x4a318
04 0040c170 700aedce     3f442c18 3f442d88 3f442d48 CoolType!CTCleanup+0x49793
05 0040c1cc 700a7238     3f442c18 3f442d88 3f442d48 CoolType!CTCleanup+0x495be
06 0040c240 700a7077     0040c390 0040c2c0 00000001 CoolType!CTCleanup+0x41a28
07 0040c25c 700a3097     0040c390 0040c2c0 e769bed0 CoolType!CTCleanup+0x41867
08 0040c3e4 700a2922     e769bed0 70384a18 0040c5d0 CoolType!CTCleanup+0x3d887
09 0040c610 7009b49b     0040d0d0 0040c69c 00000000 CoolType!CTCleanup+0x3d112
0a 0040d11c 7009928a     00000008 00000000 00000000 CoolType!CTCleanup+0x35c8b
0b 0040d1f0 70097d7d     a0d1eda8 00000032 0040d2c4 CoolType!CTCleanup+0x33a7a
0c 0040db50 70097418     e73588fc 0040db84 3d2271fc CoolType!CTCleanup+0x3256d
0d 0040deac 7009733c     e73588fc e73588e4 3d2271b8 CoolType!CTCleanup+0x31c08
0e 0040dee8 703f03d1     b9f1cf30 e73588fc e73588e4 CoolType!CTCleanup+0x31b2c
0f 0040defc 703e5b3e     e73588e4 703e5aa0 8f517570 AGM!AGMTerminate+0xa2f1
10 0040df10 703e0087     8f51757c 70860a50 00000001 AGM!AGMInitialize+0x1c37e
11 0040df34 703ef1c8     0040df60 0040df7c 0040df9c AGM!AGMInitialize+0x168c7
12 0040df48 703ef1f4     00000001 2f9e04fa 00000000 AGM!AGMTerminate+0x90e8
13 0040e004 7417ab2f     08401000 3f800000 00000000 AGM!AGMTerminate+0x9114
14 0040e01c 00000000     00000000 00000000 e734aff0 verifier!AVrfDebugPageHeapFree+0xef

Using this vulnerability, it is possible to read arbitrary memory of the process. Because of complex interactions between PDF reader and font subcomponents, especially in the presence of a JavaScript engine, it is possible that sensitive contents of arbitrary memory could be disclosed, which could aid in further exploitation and exploit mitigation bypass.

TIMELINE

2024-09-03 - Vendor Disclosure
2024-12-10 - Vendor Patch Release
2024-12-11 - Public Release

Credit

Discovered by KPC of Cisco Talos.