声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由用户承担全部法律及连带责任,文章作者不承担任何法律及连带责任。
Horizon3.ai的研究团队在流行的开源服务台系统 osTicket 中发现了一个编号为 CVE-2026-22200 的高危安全漏洞。该漏洞允许匿名攻击者通过向工单中注入恶意的PHP过滤器链表达式,然后在导出为PDF文档时,读取服务器上的任意文件。攻击者不仅可以窃取敏感文件,若结合利用另一个已知的glibc漏洞(CVE-2024-2961,亦称 CNEXT),甚至可实现远程代码执行(RCE)。该问题已在 osTicket 1.18.3 / 1.17.7 版本中修复,强烈建议所有用户立即升级。
漏洞背景与影响
osTicket 是一款广泛使用的开源服务台系统,其面向互联网的部署量可观。作为 工单系统 ,它通常包含敏感信息,是高价值攻击目标。此前的类似系统,如 SolarWinds Web Help Desk 和 SysAid,也曾曝出严重漏洞。
漏洞技术原理剖析
攻击入口:陈旧的 mPDF 库
osTicket 使用一个老版本的第三方 PHP 库 mPDF 来将工单生成 PDF。任何有权查看工单的用户(在默认配置下包括匿名访客)都可以使用此功能。
PDF生成库在处理外部资源(如图片URL)时容易出现问题。攻击源自一个 CTF 挑战(web2pdf)的启发:通过在 HTML 中嵌入类似 ” /> 的标签,可以诱使 mPDF 读取本地文件。关键在于两点:
- 路径规范化绕过: 旧版 mPDF 在规范化路径之前检查危险的流包装器(如
php://)。攻击者可以使用 php%3a//(URL编码的冒号)绕过黑名单,因为检查发生在 URL 解码之前。
- PHP 过滤器链: 为了绕过 mPDF 的图像格式验证,攻击者使用 PHP 过滤器链在任意文件内容前添加一个有效的位图(BMP)文件头。这使得 mPDF 误将文件(如
/etc/passwd)作为有效图像渲染进 PDF,后续可从中提取数据。
在 mPDF 库的旧版本中,相关代码如下所示。代码显示,在条件判断中会检查本地文件路径,并尝试用 fopen 打开,如果成功,则使用 file_get_contents 读取内容:
if ($orig_srcpath && $this->mpdf->basepathIsLocal && $check = @fopen($orig_srcpath, 'rb')) {
fclose($check);
$file = $orig_srcpath;
$this->logger->debug(sprintf('Fetching (file_get_contents) content of ”%s” with local basepath', $file), ['context' => LogContext::REMOTE_CONTENT]);
$data = file_get_contents($file);
$type = $this->guessers->guess($data);
}
第一道防线:htmLawed HTML 净化
攻击载荷需要先通过 osTicket 的输入验证。所有富文本 HTML 都会经过 htmLawed 库进行严格净化,它会基于白名单中和可疑的 URI 方案,并能识别 %3a 这样的编码。因此,类似 ` 的输入会被添加denied:` 前缀,变为:
<figure></figure>
style 属性中的 URI 也会被完全阻止。例如,以下代码会被中和:
<ul><li style=”list-style-image:url(http://myurl.com)”>listitem</li></ul>
处理后会变成:
<ul><li style=”list-style-image:url(denied)”>listitem</li></ul>
关键绕过:osTicket 的自定义清理回调
测试中发现,如果在 url 和括号间加空格,如 url (http://myurl.com),可以绕过 htmLawed 的检查。但这不符合 CSS 标准,mPDF 无法处理。
然而,研究人员注意到 osTicket 向 htmLawed 注册了一个名为 __html_cleanup 的自定义回调函数,对 style 属性执行额外的字符串操作,这非常危险。该函数会进行 HTML 实体解码并剥离引号等字符。
以下是 __html_cleanup 函数的部分代码,它负责清理浏览器特定的样式属性:
static function __html_cleanup($el, $attributes=0) {
// Clean browser-specific style attributes
if (isset($attributes['style'])) {
$styles = preg_split('/;\s*/S', html_entity_decode($attributes['style']));
$props = array();
foreach ($styles as $i=>&$s) {
@list($prop, $val) = explode(':', $s);
if (isset($props[$prop])) {
unset($styles[$i]);
continue;
}
$props[$prop] = true;
// Remove unset or browser-specific style rules
if (! $val || ! $prop || $prop[0] == '-' || substr($prop, 0, 4) == 'msO-') unset($styles[$i]);
// Remove quotes of properties without enclosed space
if (!strpos($val, ' ') ) {
$val = str_replace(”'”,””, $val);
} else {
$val = str_replace(”””,””, $val);
}
$s = ”$prop:”.trim($val);
}
unset($s);
if ($styles) $attributes['style'] = Format::htmlchars(implode(';', $styles));
else unset($attributes['style']);
}
}
最终,研究人员构造出能同时绕过 htmLawed 和 __html_cleanup 的有效载荷:
<ul><li style=”list-style-image:url"(php%3a//myurl)”>listitem</li></ul>
这个载荷使用了代表双引号的 HTML 实体 "(无结尾分号)。其生效流程如下:
- 恶意输入:
url"(php%3a//myurl)
- htmLawed 输出: 未改动,
url"(php%3a//myurl)
__html_cleanup 中的实体解码: url”(php%3a//myurl)
__html_cleanup 中的字符剥离: url(php%3a//myurl)
- mPDF 中的 URL 解码:
url(php://myurl)
组合利用:从文件读取到远程代码执行
第一步:获取工单访问权限
在默认配置下,匿名攻击者可以通过启用自助注册或暴力破解“检查工单状态”功能来获取工单查看权限。工单号默认为6位数字,通过开启新会话绕过速率限制,通常可在不到一小时内完成暴力破解。
第二步:注入载荷并导出PDF
获得权限后,攻击者将构造好的文件读取载荷作为工单的富文本内容提交。随后,在工单查看页面选择“打印为PDF”。这将触发 mPDF 处理恶意 list-style-image 属性,通过 PHP 过滤器链读取目标文件(如 /etc/passwd 或 include/ost-config.php),并将其内容伪装成 BMP 图像嵌入 PDF。
生成的PDF中包含了被窃取的数据。通过专用脚本剥离伪造的BMP文件头,即可还原出原始文件内容。
第三步:窃取关键信息与权限升级
窃取到的 include/ost-config.php 配置文件价值极高,其中包含数据库访问凭据和用于加密的 SECRET_SALT。结合另一个 SQL 注入漏洞(CVE-2025-26241),攻击者可完全解密并窃取数据库中的所有敏感配置,如 LDAP/SMTP 凭据。此外,利用 SECRET_SALT 甚至可以伪造工单访问链接,直接绕过身份验证。
第四步:升级为远程代码执行(结合CVE-2024-2961)
仅文件读取危害有限,但结合另一个 glibc 漏洞(CVE-2024-2961,CNEXT)可实现 RCE。该漏洞利用需要知道 PHP 进程的内存布局(/proc/self/maps)和完整的 libc.so.6 文件。
- 窃取内存信息和部分 libc: 首先利用 CVE-2026-22200 读取
/proc/self/maps 和部分 libc 文件(使用 zlib+base64 编码确保二进制数据完整)。
- 识别并下载完整 libc: 从部分
libc 中提取 Build ID,利用工具从在线库(如 libc.rip)下载匹配的完整 libc 文件。终端操作示例如下:
(venv) % python try_download_libc.py page1_img5.bmp.extracted
Build ID: a43bfbc8428df6623cd498c9c0caeb91aec9be4f9
Downloaded libc file saved to: libc
Extracted libc version: GNU C Library (Ubuntu GLIBC 2.35-0ubuntu3.4) stable release version 2.35
- 生成并触发 CNEXT 载荷: 利用获取到的内存地图和完整
libc,生成一个能覆盖 PHP 堆结构、最终调用 system() 函数写入 Web Shell 的 CNEXT 漏洞利用载荷。将该载荷作为工单回复提交,并再次导出为 PDF 触发漏洞。虽然可能导致连接重置或内部服务器错误,但 Web Shell 会成功写入 Web 根目录。
- 获得 Shell: 访问写入的 Web Shell(如
shell.php),即可在服务器上执行任意命令,完成从信息泄露到完全控制服务器的攻击链。成功后的浏览器访问示例如下:
osticket.example.com/shell.php?cmd=uid=33(www-data) gid=33(www-data) groups=33(www-data)
AI辅助验证
研究人员甚至尝试使用AI(Claude)来复现整个复杂的攻击链。在提供漏洞描述和步骤提示后,AI在10分钟内成功完成了一次从检测到获取flag的完整 CTF 挑战,仅在需要邮箱验证时请求了一次人工帮助。这说明了漏洞利用过程的标准化程度以及自动化攻击的潜在风险。
修复与缓解建议
- 立即升级: 升级到 osTicket v1.18.3+ 或 v1.17.7+。补丁通过在调用 mPDF 前禁用 PHP 流包装器来修复漏洞。
- 升级系统库: 检查并升级系统的
glibc 至 > 2.39 版本,以防御 CVE-2024-2961 (CNEXT) 被利用。
- 缓解措施(如果无法立即升级):
- 在网络或主机层面限制对 osTicket 的访问。
- 在管理员面板中禁用“自助注册”功能。
- 要求用户必须登录才能提交工单。
- 在系统设置中禁用工单和邮件中的 HTML 内容。
检测与入侵指标(IOC)
可使用公开的检测脚本 check.py 来检查系统是否存在漏洞。该脚本通过检查补丁中包含的其他安全改动(如对 img 标签 srcset 属性的净化是否生效)来进行判断。脚本运行结果示例如下:
Target: http://osticket.example.com/
...
[!] VULNERABLE - srcset attribute was NOT stripped
[!] Target appears to be running osTicket < v1.18.3 / < v1.17.7
[!] Target is LIKELY VULNERABLE to CVE-2026-22200
以下日志异常可能表明系统正在遭受攻击:
- 大量对
/login.php 的请求,可能伴随 python-requests 等可疑 User-Agent,表明在暴力破解工单。
- 工单创建或用户注册数量异常激增。
- 大量包含
GET /tickets.php?a=print&id=... 的日志,表示频繁的 PDF 导出操作。
- 访问日志中出现包含超长路径、带有
php%3a// 和 convert.iconv 等字符串的请求(可能返回 414 错误)。
- 在 Web 根目录下发现陌生的 Web Shell 文件(如
.php)。
参考资料
[1] CVE-2026-22200:osTicket工单直通系统Shell的漏洞剖析, 微信公众号:mp.weixin.qq.com/s/wV547T3xTfYvtTyjjLrvXg
版权声明:本文由 云栈社区 整理发布,版权归原作者所有。