本目录包含一个用于如实复现 Firefox 字体处理中未初始化内存泄露问题的复现包。一个精心构造的 @font-face 字体会使 OTS 输出流进入未初始化堆内存的空隙,普通内容则可通过 canvas 的 measureText API 读回该空隙中的字节。本复现包仅用于针对本地下载的易受攻击 Firefox 构建、使用合成数据运行。它绝不会接触任何在线或第三方系统。
对应的 PoC 仓库:https://github.com/defineid/Palimpsest
技术分析文档:本作品集中的 writeup-font-cmap-leak.html。
| 字段 | 值 |
|---|---|
| CVE | CVE-2026-74945 |
| 组件 | Core / Graphics: Text |
| 类别 | 未初始化内存泄露(CWE-908 · CWE-200) |
| 严重程度 | sec-high(Web 可达的内存泄露) |
| 修复版本 | Firefox 154 · ESR 140.14 |
| Bug | bugzilla.mozilla.org/show_bug.cgi?id=2057808 |
| 交互 | 无 · 仅需页面加载 |
Firefox 在使用 Web 字体前会通过 OTS 对其进行净化。三个相互独立的缺陷叠加在一起,使得精心构造的字体会在净化后的输出中留下未初始化堆内存空隙,而内容可以将其读回:
moz_xrealloc)增长,而该分配器不会对新空间进行零填充。nonDefaultUVSOffset 的 cmap format-14 子表会使 OTS 在重新序列化净化后的字体时向前 seek 其输出流。已写入字节与 seek 目标之间被跳过的区间会连同未初始化堆内存一起交给调用方,而 forget() 会将缓冲区大小设为所到达的最高偏移量。cmap 表的剩余部分而非子表进行边界检查,因此某个段的 idRangeOffset 即使超出 format-4 数据的末尾,仍能通过检查,并读取到相邻子表的未初始化字节。精心构造的字体将一个 format-4 子表(其最后一个段 0xFFFF 使用了一个异常的、越界的 idRangeOffset)与一个携带较大 nonDefaultUVSOffset 的 format-14 子表配对,再加上 maxp.numGlyphs = 65535 以及每个字形 id 各不相同的 hmtx advance(advance == 字形 id)。因此,测量 U+FFFF 会解析出一个从未初始化空隙中读取的字形 id,其 advance 宽度可从脚本读取,每次测量在大约 64 KB 的窗口内泄露约两个字节的堆内存。
一个普通网页通过 @font-face 提供精心构造的字体,并调用 ctx.measureText("")。无需点击、权限、扩展或更改首选项。OTS 在进程内运行,且不属于沙箱化的库,因此泄露的字节是内容进程的堆内存,其中可能包含指针或机密信息。
| 文件 | 用途 |
|---|---|
make_font.py | 构建 leak.otf 和 leak.woff,即精心构造的字体。 |
trigger.html | 通过 @font-face 加载该字体,并通过 measureText 读回 U+FFFF advance 的裸页面。 |
https://ftp.mozilla.org/pub/firefox/releases/140.9.0esr/ 下载对应平台的构建。pip install fonttools brotli
python3 make_font.py # writes leak.otf and leak.woff in this directory
然后在存在漏洞的构建版本中,通过 file:// 路径或你控制的本地静态服务器打开 trigger.html,并点击 Measure。
.notdef),且 advance 为 0。不会泄漏任何间隙,因为输出流已被清零,且 format-4 查找被限制在其自身的子表内。make_font.py 已在此处使用 fontTools 4.63.0 执行,并生成了 leak.otf(393816 字节)和 leak.woff(132372 字节)。生成的字体已从原始字节重新解析,并且每个构造的成分都得到了确认:maxp.numGlyphs = 65535、hhea.numberOfHMetrics = 65535、每个 glyph id 具有不同的 hmtx advance(advance == glyph id)、两个 cmap 子表(platform 3 encoding 1 format 4,以及 platform 0 encoding 5 format 14)、format-4 的最后一段 endCode = 0xFFFF 且带有奇数的越界 idRangeOffset = 0x4001,以及 format-14 的 nonDefaultUVSOffset = 0x8000。trigger.html 以观察泄漏。此包演示了构造的输入和回读路径;它不是武器化的漏洞利用,也不包含任何受控的代码执行原语。此内容针对的是本地下载的构建版本,使用合成的、自行生成的字体数据,用于撰写作者本人已报告且已修补的 CVE 的文章。它不攻击任何在线或第三方服务。