CVE-2025-27164
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.
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
Acrobat Reader - https://acrobat.adobe.com/us/en/acrobat/pdf-reader.html
6.5 - CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:N/A:N
CWE-125 - Out-of-bounds Read
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.
2025-01-22 - Vendor Disclosure
2025-03-11 - Vendor Patch Release
2025-03-12 - Public Release
Discovered by KPC of Cisco Talos.