针对 CVE-2026-93616 的未认证补丁状态检查。该漏洞是 Check Point Security Management (CPM) 服务器中的未认证目录遍历漏洞,可到达 Runtime.exec(),已于 2026-09-22 在 sk1000171 中修复。CWE-22,CVSS 9.8 严重(CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H),已被在野利用。Check Point 是 CNA;该问题是通过在野利用而非外部报告发现的。
CPM 服务器在 TCP 19009 上暴露 SOAP Web 服务。UpgradeSvcRemote 的登录方法 loginAsApplicationReadOnlyPublicSession 将其 targetVersion 参数转发到传递给 Runtime.exec 的文件系统路径中,且未进行验证,因此未认证的调用者可以将执行重定向到攻击者选择的脚本。修复方案为 targetVersion 添加了一个 Bean Validation 模式,用于拒绝路径分隔符。
该脚本报告每个目标上是否存在该修复。它在 targetVersion 中发送一个非法字符,绝不发送遍历序列,并且不会在目标上进行认证、上传文件或执行任何操作。除 VULNERABLE 之外的结果本身并不表示目标已打补丁;请参阅解读非易受攻击结果。
对已停止支持的版本进行了显式处理。在早于 R81.10 的版本系列上,登录方法接受的参数更少,因此主探测会在补丁状态可读取之前被 XML 反序列化拒绝。当 Check Point 服务器以这种方式拒绝探测时,脚本会以较旧的参数形式发送一次后续调用。如果登录方法随后成功分派,则目标是一个受影响且 Check Point 从未发布修复的版本,判定为 VULNERABLE,原因为 affected-eos-no-fix。该后续调用不携带 mode 和 targetVersion,因此它同样不会发送任何需要验证或遍历的内容。这填补了一个缺口,否则旧版服务器将被视为无法识别。
# single target (port defaults to 19009)
./cve_2026_93616_check.py mgmt.example.com
# explicit port
./cve_2026_93616_check.py mgmt.example.com:19009
# several targets
./cve_2026_93616_check.py mds-a.example.com mds-b.example.com
# scan a list, one target per line ('#' comments allowed), compact output
./cve_2026_93616_check.py -f targets.txt --brief
# machine-readable output for pipelines
./cve_2026_93616_check.py -f targets.txt --json > results.json
Python 3.8+,仅使用标准库——无第三方包。
请针对管理服务器(Security Management、Multi-Domain Management、Log、Multi-Domain Log 和 SmartEvent)运行该工具,而不是网关。该检查针对 TCP 19009 上的 CPM SOAP 监听器,网关不运行该监听器。
| 标志 | 描述 |
|---|---|
TARGET | 一个或多个 HOST[:PORT] 目标;端口默认为 19009 |
-f, --targets-file FILE | 从文件读取目标(每行一个;# 注释) |
-p, --port PORT | 当目标省略端口时的默认端口(默认:19009) |
--timeout SECS | 每次探测的超时时间(默认:15) |
--workers N | 并发目标数(默认:16);输出保持输入顺序 |
-b, --brief | 每个目标一行对齐输出,用于扫描大量主机 |
--json | 输出结构化 JSON,包括每个目标的探测标记 |
--no-color | 禁用彩色输出(也遵循 NO_COLOR 和非 TTY) |
易受攻击的服务器(默认输出;在 TTY 上 [!] 标记和 VULNERABLE 以红色显示):
$ ./cve_2026_93616_check.py mgmt.example.com
[!] mgmt.example.com:19009: VULNERABLE [dispatched-without-validation]
loginAsApplicationReadOnlyPublicSession accepted an illegal targetVersion and dispatched it; the fix's input validation is absent
已打补丁的服务器:
$ ./cve_2026_93616_check.py mgmt-dr.example.com
[+] mgmt-dr.example.com:19009: PATCHED [validation-constraint-present]
the server rejected an illegal targetVersion with the input-validation constraint added by the fix (Jumbo Hotfix R82.10 Take 45 or equivalent)
一个 Check Point 管理服务器作出了响应但未分派登录方法,因此无法读取补丁状态:
$ ./cve_2026_93616_check.py log.example.com
[?] log.example.com:19009: INCONCLUSIVE [cpm-no-dispatch]
a Check Point management fault came back, but the UpgradeSvcRemote login method did not dispatch, so patch state could not be read
使用 --brief 的多个目标:
$ ./cve_2026_93616_check.py -f targets.txt --brief; echo "exit: $?"
VULNERABLE mgmt.example.com:19009 dispatched-without-validation
PATCHED mgmt-dr.example.com:19009 validation-constraint-present
INCONCLUSIVE log.example.com:19009 cpm-no-dispatch
UNAFFECTED web.example.com:19009 not-cpm
ERROR offline.example.com:19009 unreachable
exit: 1
使用 --json 的 JSON 输出。包含探测标记,以便将判定结果与响应实际包含的内容进行核对:
$ ./cve_2026_93616_check.py mgmt.example.com mgmt-dr.example.com --json
[
{
"target": "mgmt.example.com:19009",
"verdict": "VULNERABLE",
"reason": "dispatched-without-validation",
"detail": "loginAsApplicationReadOnlyPublicSession accepted an illegal targetVersion and dispatched it; the fix's input validation is absent",
"http_status": 500,
"probe": {
"http_status": 500,
"saw_validation": false,
"saw_dispatch": true,
"saw_cpm": true,
"note": ""
}
},
{
"target": "mgmt-dr.example.com:19009",
"verdict": "PATCHED",
"reason": "validation-constraint-present",
"detail": "the server rejected an illegal targetVersion with the input-validation constraint added by the fix (Jumbo Hotfix R82.10 Take 45 or equivalent)",
"http_status": 500,
"probe": {
"http_status": 500,
"saw_validation": true,
"saw_dispatch": true,
"saw_cpm": true,
"note": ""
}
}
]
该检查旨在用于生产系统:
targetVersion 中放入一个非法字符(!);这一个字符就足以触发已打补丁版本的验证,并且它无法像路径分隔符那样逃逸目录。旧版后续探测完全不携带 targetVersion。domainId 元素,因此在未打补丁的服务器上,该方法在构建参数时会抛出 NullPointerException,此时 ReflectionUtils 尚未到达 Runtime.exec。即使没有这一点,探测值也会解析到一个不存在的目录,因此没有脚本可运行。旧版探测仅发送应用程序名称和超时,因此没有可用于构建路径的内容。| # | 调用 | 时机 | 目的 |
|---|---|---|---|
| 1 | UpgradeSvcRemote.loginAsApplicationReadOnlyPublicSession(当前形式) | 始终 | 补丁状态探测 |
| 2 | UpgradeSvcRemote.loginAsApplicationReadOnlyPublicSession(旧版形式) | 仅当调用 1 被 Check Point 服务器在反序列化时拒绝 | 确认已停止支持的版本 |
探测会记录到 $MDS_FWDIR/log/cpm.elg。已打补丁的服务器记录验证拒绝;未打补丁的服务器记录分派及其 NullPointerException;已停止支持的服务器记录调用 1 的反序列化错误和调用 2 的分派。两个探测都发送应用程序名称 bf-check(调用 1 还发送值 bf-check!),在日志中可识别为本工具而非攻击。
修复为每个接受 targetVersion 的 UpgradeSvcRemote 方法的 targetVersion 参数添加了一个 Bean Validation 模式 ^[A-Za-z0-9._-]*$。该模式允许版本字符串但不允许路径分隔符,从而关闭了进入 Runtime.exec 的遍历。该检查发送一个非法字符并读取服务器的响应方式:
| 对一个非法字符的响应 | 版本 | 判定 |
|---|---|---|
targetVersion contains illegal characters | 已修复(Take 45 及等效版本) | PATCHED |
| 方法已分派,无验证消息 | 受影响(R81.10 及更高版本) | VULNERABLE |
| 探测被 XML 反序列化拒绝,随后旧版形式分派 | 受影响,已停止支持 | VULNERABLE |
| Check Point 故障,方法未分派 | 两者皆可 | INCONCLUSIVE |
分派通过响应中的完整方法名 UpgradeSvcRemote.loginAsApplicationReadOnlyPublicSession 来确认。仅字符串 UpgradeSvcRemote 不可用:Check Point 故障会在命名空间映射中列出每个服务,因此无论该方法是否运行,该子字符串都会出现在任何 CPM 响应中。该检查仅匹配完整方法名。
loginAsApplicationReadOnlyPublicSession 的参数形式在不同版本之间发生了变化。R81.10 及更高版本接受主探测发送的 mode 和 targetVersion 元素;早于 R81.10 的版本不接受,并在产生任何补丁状态信号之前,在 XML 反序列化时拒绝主探测(一个 generalRemoteFaultException,其服务器堆栈始于 JAXB/CXF 反序列化器)。由于这些较旧的版本系列均已停止支持且从未获得修复,该拒绝本身就是发现结果。
脚本识别出这一点——Check Point 故障加上反序列化器堆栈——并以较旧的形式(仅 applicationName 和 timeout)发送一次后续调用。如果登录方法对该形式成功分派,则目标被确认为受影响且无法打补丁的版本,并报告为 VULNERABLE / affected-eos-no-fix。后续调用仅在此情况下发送,因此当前受支持的服务器仍只需一次请求即可检查。
该检查观察已打补丁代码路径的行为,不读取版本字符串。它不报告已打补丁的服务器正在运行哪个 Jumbo Hotfix take。
VULNERABLE 和 PATCHED 均基于观察到的服务器响应。INCONCLUSIVE 和 ERROR 表示探测未能对目标进行分类,因此其补丁状态未知。因此,INCONCLUSIVE 与 PATCHED 分开报告。
每个判定都带有一个简短的 reason 标签。--brief 将其打印为第三列,--json 将其作为 reason 携带。
| 判定 | 原因标签 | 含义 |
|---|---|---|
VULNERABLE | dispatched-without-validation | 登录方法接受了非法的 targetVersion 并分派了它。修复不存在。 |
VULNERABLE | affected-eos-no-fix | 已停止支持的版本(早于 R81.10):登录方法对旧版参数形式成功分派,且该版本系列从未发布修复。它无法打补丁;请升级到受支持的已修复版本。 |
PATCHED | validation-constraint-present | 服务器以修复添加的验证消息拒绝了非法值。仅适用于此 CVE。 |
INCONCLUSIVE | cpm-no-dispatch | 返回了 Check Point 管理故障,但登录方法未分派,因此无法读取补丁状态。 |
INCONCLUSIVE | legacy-no-dispatch | Check Point 服务器拒绝了当前登录形式,且旧版后续调用也未分派,因此无法识别版本。 |
INCONCLUSIVE | legacy-probe-failed | Check Point 服务器拒绝了当前登录形式,但旧版后续调用未返回任何可用内容(例如,两次调用之间连接断开)。 |
UNAFFECTED | not-cpm | 端点未作为 Check Point CPM SOAP 服务响应。 |
ERROR | unreachable | TCP 连接、TLS 握手或 HTTP 交换失败。 |
| 代码 | 含义 |
|---|---|
0 | 没有目标为 VULNERABLE |
1 | 至少有一个目标为 VULNERABLE |
2 | 用法错误(参数错误、目标无效或目标文件不可读) |
130 | 已中断(Ctrl-C) |
退出代码 0 涵盖 PATCHED、UNAFFECTED、INCONCLUSIVE 和 ERROR。要区分已打补丁的目标与无法分类的目标,请读取判定(--brief 的第一列或 --json 的 verdict 字段),而不是退出代码。