Talos Vulnerability Report

TALOS-2025-2136

Adobe Acrobat Reader Font numGlyphs Out-Of-Bounds Read Vulnerability

March 12, 2025
CVE Number

CVE-2025-27164

SUMMARY

An out-of-bounds read vulnerability exists in the Font functionality of Adobe Acrobat Reader 2024.005.20320. A specially crafted font file embedded into a PDF can trigger this vulnerability which can lead to disclosure of sensitive information. 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.005.20320

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 a font file contains CFF2 and maxp (Maximum Profile) tables. The values of the tableTag field are the strings maxp and CFF2 for maxp and CFF2 tables, respectively.

The maxp table provides information about the maximum values for various parameters in the font. The structure of a maxp table is as follows:

Offset Size         Name
------ --------    --------------------------------------
0x00    0x04        Version16Dot16                        
0x04    0x02        numGlyphs 
0x06    0x02        maxPoints
0x08    0x02        maxContours

This is not the complete structure of the maxp table, but for this vulnerability, the numGlyphs field is important. numGlyphs indicates the number of glyphs in the font.

CFF2 stands for Compact Font Format version 2 table. It is used to store and represent glyph outlines and other related data for OpenType fonts. A CFF2 table starts with a table header, followed by Top DICT, Global Subr INDEX, VariationStore and so on. The structure of the CFF2 table header is as follows:

Offset Size   Name
------ ----- --------------------------------------
0x00    0x01  cff2MajorVersion
0x01    0x01  cfff2MinorVersion
0x02    0x01  cff2HeaderSize
0x03    0x02  topDictLength

Here, the topDictLength field indicates the length of the Top DICT data in bytes. The Top DICT data is a dictionary data comprising key-value pairs, where the key is a 1- or 2-byte operator and the dictionary value is encoded as a variable-size numeric operand. The important thing to note here is that in Top DICT data, an operator is preceded by the operand(s) that specify its value. The following table indicates various types of operators Top DICT data may contain:

Operator Name   Operator Value    Operand type and meaning
----------------------------------------------------------------------------------------------------------
CharStrings    0x11               number and it gives CharStrings INDEX offset, from start of the CFF2 table.
vstore         0x18               number and it VariationStore structure offset, from start of the CFF2 table.
FDArray        0x0C 0x24          number and it Font DICT (FD) INDEX offset, from start of the CFF2 table.
FDSelect       0x0C 0x25          number and it CharStrings INDEX offset, from start of the CFF2 table. 
FontMatrix     0x0c 0x07          array and default value is (0.001 0 0 0.001 0 0)	

For this vulnerability, CharStrings operator is important. The value of CharStrings operator in byte is 0x11. The operand gives CharStrings INDEX offset, from the start of the CFF2 table. The operand value is encoded, and it can be decoded using the following pseudo python code:

 # usage: decode_integer(b"\x1d\x00\x00\x00\x8a")
 def decode_integer(data):
    first_byte = data[0]
    if 32 <= first_byte <= 246:
        return first_byte - 139
    elif 247 <= first_byte <= 250:
        return (first_byte - 247) * 256 + data[1] + 108
    elif 251 <= first_byte <= 254:
        return -(first_byte - 251) * 256 - data[1] - 108
    elif first_byte == 28:
        return data[1] * 256 + data[2]
    elif first_byte == 29:
        return (data[1] << 24) + (data[2] << 16) + (data[3] << 8) + data[4]

In our case, the Top DICT data contains three operators, and the content of the Top DICT data is as follows:

1D 00 00 00 7A 0C 24  ==>  (operator: FDArray, operand after encoding: 0x7A) 
1D 00 00 00 8A 11     ==>  (operator: CharStrings, operand after encoding: 0x8A)
1D 00 00 00 1C 18     ==>  (operator: vstore, operand after encoding: 0x1C)

The second entry of the Top DICT data shows that it contains CharStrings Index at offset 0x8A from the start of the CFF2 table. The CharStrings Index contains a header, followed by object data. The structure of CharStrings Index header is as follows:

 Offset Size                                     Name
-----------------------------------------------------------------------
0x00    0x04                                     CharStringsCount
0x04    0x01                                     CharStringsOffSize
0x05    CharStringsCount+1 * CharStringsOffSize  CharStringsOffset

CharStringsCount defines the number of object stored in INDEX . CharStringsOffset is an array, and the size of the array is the (CharStringsCount+1) * CharStringsOffSize bytes.

This vulnerability occurs when the value of the numGlyphs field in the maxp table is greater than CharStringsCount. In this case, the value of numGlyphs is 0x5039 and the value of CharStringsCount is 0x39. We can observe the following in the debugger (with PageHeap enabled):

0:000> p
Time Travel Position: 13FA40:FAA
eax=5ba2cff0 ebx=5ba2cfe8 ecx=5ba2cff0 edx=00000000 esi=5ba2cff0 edi=00000039
eip=6ee2837b esp=04efc40c ebp=04efc420 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+0x4cfb:
6ee2837b 8bc7            mov     eax,edi
0:000> p
Time Travel Position: 13FA40:FAB
eax=00000039 ebx=5ba2cfe8 ecx=5ba2cff0 edx=00000000 esi=5ba2cff0 edi=00000039
eip=6ee2837d esp=04efc40c ebp=04efc420 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+0x4cfd:
6ee2837d c1e003          shl     eax,3     ; <------------------------------- (1)
:000> p
Time Travel Position: 13FA40:FAC
eax=000001c8 ebx=5ba2cfe8 ecx=5ba2cff0 edx=00000000 esi=5ba2cff0 edi=00000039
eip=6ee28380 esp=04efc40c ebp=04efc420 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+0x4d00:
6ee28380 50              push    eax
0:000> p
Time Travel Position: 13FA40:FAD
eax=000001c8 ebx=5ba2cfe8 ecx=5ba2cff0 edx=00000000 esi=5ba2cff0 edi=00000039
eip=6ee28381 esp=04efc408 ebp=04efc420 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+0x4d01:
6ee28381 e850d8ffff      call    CoolType!CTInit+0x2556 (6ee25bd6) ; <------------------------------- (2)
0:000> p
Time Travel Position: 13FA44:F9E
eax=57d82e38 ebx=5ba2cfe8 ecx=6ee25bfe edx=00000000 esi=5ba2cff0 edi=00000039
eip=6ee28386 esp=04efc408 ebp=04efc420 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+0x4d06:
6ee28386 8906            mov     dword ptr [esi],eax  ds:002b:5ba2cff0=00000000
0:000> dd eax                        ; <------------------------------- (3)
57d82e38  c0c0c0c0 c0c0c0c0 c0c0c0c0 c0c0c0c0
57d82e48  c0c0c0c0 c0c0c0c0 c0c0c0c0 c0c0c0c0
57d82e58  c0c0c0c0 c0c0c0c0 c0c0c0c0 c0c0c0c0
57d82e68  c0c0c0c0 c0c0c0c0 c0c0c0c0 c0c0c0c0
57d82e78  c0c0c0c0 c0c0c0c0 c0c0c0c0 c0c0c0c0
57d82e88  c0c0c0c0 c0c0c0c0 c0c0c0c0 c0c0c0c0
57d82e98  c0c0c0c0 c0c0c0c0 c0c0c0c0 c0c0c0c0
57d82ea8  c0c0c0c0 c0c0c0c0 c0c0c0c0 c0c0c0c0

At (1), the value of eax is equal to CharStringsCount. The CharStringsCount is used to calculate the size of the vulnerable buffer. The buffer is allocated by the method called at (2). After allocation, the content of the vulnerable buffer can be examined at (3). It contains two pointers for each glyph. Once the vulnerable buffer is set, the method responsible for processing glyphs is called.

Time Travel Position: 3CDC96:222
eax=0ddc51ee ebx=00000000 ecx=6ee28f70 edx=00004000 esi=6ee28f70 edi=b6364ff0
eip=6eeae186 esp=b4ced048 ebp=b4ced118 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!CTInit+0x8ab06:
6eeae186 8b4d84          mov     ecx,dword ptr [ebp-7Ch] ss:002b:b4ced09c=5ba2cfe8
0:001> p
Time Travel Position: 3CDC96:223
eax=0ddc51ee ebx=00000000 ecx=5ba2cfe8 edx=00004000 esi=6ee28f70 edi=b6364ff0
eip=6eeae189 esp=b4ced048 ebp=b4ced118 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!CTInit+0x8ab09:
6eeae189 ffd6            call    esi {CoolType!CTInit+0x58f0 (6ee28f70)}  ; <------------------------------------- (4)
0:001> p
Time Travel Position: 3CDC96:226
eax=57d82e38 ebx=00000000 ecx=5ba2cfe8 edx=00004000 esi=6ee28f70 edi=b6364ff0
eip=6eeae18b esp=b4ced048 ebp=b4ced118 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!CTInit+0x8ab0b:
6eeae18b 8bc8            mov     ecx,eax
0:001> dd eax                                  ; <------------------------------------- (5)
57d82e38  3831efe0 38320ff8 520a4fe0 5968eff0 
57d82e48  59690fe0 59692ff0 59694fe0 59696ff0
57d82e58  59698fe0 5969aff0 57dc0fe0 57dc2ff0
57d82e68  57dc4fe0 57dc6ff0 57dc8fe0 57dcaff0
57d82e78  57dccfe0 57de6ff0 57de8fe0 57deaff0
57d82e88  57decfe0 57deeff0 57df0fe0 57df2ff0
57d82e98  4bc66fe0 4bc68ff0 4bc6afe0 4bc6cff0
57d82ea8  4bc6efe0 4bc70ff0 4bc72fe0 4bc32ff0
0:001> p
Time Travel Position: 3CDC96:227
eax=57d82e38 ebx=00000000 ecx=57d82e38 edx=00004000 esi=6ee28f70 edi=b6364ff0
eip=6eeae18d esp=b4ced048 ebp=b4ced118 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!CTInit+0x8ab0d:
6eeae18d 899d78ffffff    mov     dword ptr [ebp-88h],ebx ss:002b:b4ced090=00000003
0:001> p
Time Travel Position: 3CDC96:228
eax=57d82e38 ebx=00000000 ecx=57d82e38 edx=00004000 esi=6ee28f70 edi=b6364ff0
eip=6eeae193 esp=b4ced048 ebp=b4ced118 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!CTInit+0x8ab13:
6eeae193 8bc3            mov     eax,ebx  
0:001> p
Time Travel Position: 3CDC96:229
eax=00000000 ebx=00000000 ecx=57d82e38 edx=00004000 esi=6ee28f70 edi=b6364ff0
eip=6eeae195 esp=b4ced048 ebp=b4ced118 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!CTInit+0x8ab15:
6eeae195 894d8c          mov     dword ptr [ebp-74h],ecx ss:002b:b4ced0a4=6ec579a1 ; <------------------------------------- (6)
0:001> p
Time Travel Position: 3CDC96:22A
eax=00000000 ebx=00000000 ecx=57d82e38 edx=00004000 esi=6ee28f70 edi=b6364ff0
eip=6eeae198 esp=b4ced048 ebp=b4ced118 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!CTInit+0x8ab18:
6eeae198 3b857cffffff    cmp     eax,dword ptr [ebp-84h] ss:002b:b4ced094=00005039 ; <------------------------------------- (7)
0:001> p
Time Travel Position: 3CDC96:22B
eax=00000000 ebx=00000000 ecx=57d82e38 edx=00004000 esi=6ee28f70 edi=b6364ff0
eip=6eeae19e esp=b4ced048 ebp=b4ced118 iopl=0         nv up ei ng nz ac po cy
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000293
CoolType!CTInit+0x8ab1e:
6eeae19e 0f8d18010000    jge     CoolType!CTInit+0x8ac3c (6eeae2bc)      [br=0]
0:001> p
Time Travel Position: 3CDC96:22C
eax=00000000 ebx=00000000 ecx=57d82e38 edx=00004000 esi=6ee28f70 edi=b6364ff0
eip=6eeae1a4 esp=b4ced048 ebp=b4ced118 iopl=0         nv up ei ng nz ac po cy
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000293
CoolType!CTInit+0x8ab24:
6eeae1a4 8bc3            mov     eax,ebx

The method called at (4) retrieves the vulnerable buffer. A loop starts at (6), where eax represents the index value used to access pointers stored in the vulnerable buffer. At (7), the index is compared against the numGlyphs field. If the index is smaller than numGlyphs, the loop continues. Since the size of the buffer is determined by CharStringsCount, which is smaller than numGlyphs, continuing the loop may lead to out-of-bounds reads. This can be observed at the time of the crash:

0:001> g
(558.b54): 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: 3CDDE5:0
eax=00000000 ebx=00000000 ecx=57d83000 edx=00004000 esi=49026fb8 edi=b6364ff0
eip=6eeae1b9 esp=b4ced048 ebp=b4ced118 iopl=0         nv up ei ng nz ac po cy
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000293
CoolType!CTInit+0x8ab39:
6eeae1b9 3919            cmp     dword ptr [ecx],ebx  ds:002b:57d83000=????????
0:001> 
0:001> u
CoolType!CTInit+0x8ab39:
6eeae1b9 3919            cmp     dword ptr [ecx],ebx
6eeae1bb 0f848c000000    je      CoolType!CTInit+0x8abcd (6eeae24d)
6eeae1c1 53              push    ebx
6eeae1c2 53              push    ebx
6eeae1c3 50              push    eax
6eeae1c4 8d4588          lea     eax,[ebp-78h]
6eeae1c7 50              push    eax
6eeae1c8 8d459c          lea     eax,[ebp-64h]
0:001> dd 57d83000
57d83000  ???????? ???????? ???????? ????????
57d83010  ???????? ???????? ???????? ????????
57d83020  ???????? ???????? ???????? ????????
57d83030  ???????? ???????? ???????? ????????
57d83040  ???????? ???????? ???????? ????????

0:001> kb
WARNING: Stack pointer is outside the normal stack bounds. Stack unwinding can be inaccurate.
 # ChildEBP RetAddr      Args to Child              
WARNING: Stack unwind information not available. Following frames may be wrong.
00 b4ced118 6ee4e0ee     57b00bd8 192be5c3 b4ced710 CoolType!CTInit+0x8ab39
01 b4ced61c 6ee4b5aa     57b00bd8 48fe4fc0 b4ced67c CoolType!CTInit+0x2aa6e
02 b4ced654 6ee4b31e     57b00bd8 48fe4fc0 b4ced67c CoolType!CTInit+0x27f2a
03 b4ced6a4 6ee4b1c2     b4ced8fc 48fe4fc0 00000000 CoolType!CTInit+0x27c9e
04 b4ced6e4 6ee56ca7     b4ced8fc 48fe4fc0 00000000 CoolType!CTInit+0x27b42
05 b4ced750 6ee85256     b4ced8fc b4ced7cc 00000004 CoolType!CTInit+0x33627
06 b4ced894 6ee850e1     b4ced8fc b4ced8f4 00000000 CoolType!CTInit+0x61bd6
07 b4ced8cc 6ee85026     b4ced8fc b4ced8f4 00000000 CoolType!CTInit+0x61a61
08 b4ced90c 6ee48083     57b00bd8 b4ced944 192beab3 CoolType!CTInit+0x619a6
09 b4ced96c 6ee49bb0     6f136d2c 00000001 00000000 CoolType!CTInit+0x24a03
0a b4ced9a0 6ee4a482     57b00bd8 6f136d2c 00000001 CoolType!CTInit+0x26530
0b b4ced9d8 6ee5fcdb     6f136d2c b4ceda3c 00000004 CoolType!CTInit+0x26e02
0c b4cedd00 6ee84a2b     b4cede28 00000000 b4cedfe8 CoolType!CTInit+0x3c65b
0d b4ceeac0 6ee83534     00000000 ad3c6cec 00004718 CoolType!CTInit+0x613ab
0e b4ceeb14 6ee81cba     ad3c6ce0 00000032 b4ceebf8 CoolType!CTInit+0x5feb4
0f b4ceed78 6ee813d9     b4cef238 b4cef9ac 0002044a CoolType!CTInit+0x5e63a
10 b4ceedc8 6ded13e6     5c3b6d90 b4cef238 b4cef9ac CoolType!CTInit+0x5dd59
11 b4cef1e4 6ded00a9     52f4c884 b4cef238 b4cef9ac AGM!AGMInitialize+0x66f86
12 b4cef368 6de8d66b     52f4c820 b4cefa44 b4cef9ac AGM!AGMInitialize+0x65c49
13 b4cef404 6de9318a     b4cef650 52f4c820 b4cefa44 AGM!AGMInitialize+0x2320b
14 b4cef9dc 6de91e14     5c25a954 b35b2f70 52f4c820 AGM!AGMInitialize+0x28d2a
15 b4cefba0 6de91352     5c25a954 b35b2f70 0cd4bf89 AGM!AGMInitialize+0x279b4
16 b4cefbe0 6dec6c0b     5c25a954 b35b2f70 00000008 AGM!AGMInitialize+0x26ef2
17 b4cefc04 6de90e19     00000301 6dec68e4 b35b2f70 AGM!AGMInitialize+0x5c7ab
18 b4cefc0c 6dec68e4     b35b2f70 aac96d00 aac96fe4 AGM!AGMInitialize+0x269b9
19 b4cefc2c 6dec65b9     b35b2f70 0cd4b8f5 aac96d18 AGM!AGMInitialize+0x5c484
1a b4cefc6c 6f67d472     00000000 ffffffff aac96fe4 AGM!AGMInitialize+0x5c159
1b b4cefc9c 6dec63a4     b35b2f70 0cd4b8bd aaebcf58 AcroRd32!DllCanUnloadNow+0x1d4fd2
1c b4cefcd4 6f67d759     ad41ed28 a9d6eec8 b4cefcec AGM!AGMInitialize+0x5bf44
1d b4cefd50 6f6e3bda     9eb04f3b b17fcfb8 b5aacff8 AcroRd32!DllCanUnloadNow+0x1d52b9
1e b4cefdc0 6f6e3a83     9eb04f13 6f6e3a10 b5a8cff8 AcroRd32!DllCanUnloadNow+0x23b73a
1f b4cefde8 6f6e3a21     30a76c50 6f6e3a10 b4cefe08 AcroRd32!DllCanUnloadNow+0x23b5e3
20 b4cefe64 77dd806e     ffffffff 77df9121 00000000 AcroRd32!DllCanUnloadNow+0x23b581
21 b4cefe6c 77df9121     00000000 00000000 6f6e3a10 ntdll!_RtlUserThreadStart+0x1b
22 b4cefe78 6f6e3a10     b5a8cff8 00000000 00000000 ntdll!FinalExceptionHandlerPad33
23 00000000 00000000     00000000 00000000 00000000 AcroRd32!DllCanUnloadNow+0x23b570

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.

Please note that this vulnerability is a patch bypass of TALOS-2023-1908.

TIMELINE

2025-01-22 - Vendor Disclosure
2025-03-11 - Vendor Patch Release
2025-03-12 - Public Release

Credit

Discovered by KPC of Cisco Talos.