检查 Linux 主机是否可能暴露于 CVE-2026-98432,即内核 IPv4 TCP 握手过程中的释放后使用漏洞。属于 shouldipanic.ch 的一部分。
携带 IPv4 选项(Record Route、Timestamp)的 SYN 会到达读取这些选项的代码,而此时选项可能已被释放。任何开放的 IPv4 TCP 端口都足以触发,无需登录。该漏洞存在于自 4.4(2016)以来的每个内核中,直到各分支的修复版本:5.10.272、5.15.223、6.1.190、6.6.159、6.12.113、6.18.56、7.2.10、7.3。 内核 CNA 将其评为 9.8;已有针对带有 MPTCP 监听器的服务器的可用远程 root 漏洞利用被报告(未公开)。该修复在网络层面不可见:已修复的内核的响应与未修补的内核完全相同。
这决定了每种模式能说明什么:
--on-host 在机器本身上读取正在运行的内核:其版本、
软件包变更日志,以及是否有更新的内核等待重启。Server 头读取 Linux
发行版,并显示该发行版是否已发布修复后的内核。这是一个强有力的提示,而非定论:在
反向代理之后或容器内部,内核属于另一台机器。kernel98scan 有两种形式:
kernel98scan.py:一个无依赖的单一 Python 文件。在主机上,
--on-host 给出真实判定;指向地址时,它会在 IP 范围内进行发行版
指纹识别,并带有端口扫描和 vhost 发现。
curl -LO https://github.com/akaihigurashi/kernel98scan/releases/latest/download/kernel98scan.py
python3 kernel98scan.py --on-host # real verdict for THIS machine
Python 3.8 或更高版本,无需安装任何东西。示例:
python3 kernel98scan.py --on-host # read the running kernel
python3 kernel98scan.py 203.0.113.10 # outside: distribution + fix status
python3 kernel98scan.py 203.0.113.0/24 # a whole range from outside
python3 kernel98scan.py 203.0.113.10 --ip-options # + do IP-options packets get through?
python3 kernel98scan.py 203.0.113.10 -o out.csv # CSV output
python3 kernel98scan.py -h # all options
在主机上(--on-host,退出码 2 / 1 / 0 / 3):
| 状态 | 含义 |
|---|---|
VULNERABLE | 运行中的内核没有任何修复迹象 |
CHECK | 已安装修复后的内核,但旧内核仍在运行:需要重启 |
FIXED | 内核变更日志中提到了该 CVE,或者普通上游内核已处于修复版本 |
厂商内核(Ubuntu、RHEL、Proxmox)会将修复向后移植到旧版本号上,因此
对它们而言只有变更日志才算数。它还会显示 MPTCP 是否开启(所报告的
漏洞利用通过 MPTCP 监听器实现)以及 tcp_migrate_req。
从外部:
| 状态 | 含义 |
|---|---|
POSSIBLY VULNERABLE | 横幅显示为 Linux 发行版:要么它还没有修复后的内核(Ubuntu、RHEL 及重建版本目前如此),要么已有修复内核,但需要安装并启动 |
UNKNOWN | 发行版不可见,或由代理/CDN 应答 |
NOT LINUX | Server 头指向非 Linux 主机 |
VULNERABLE 和 PATCHED 绝不会从外部报告。各发行版的修复状态是
check.py 中的一张表(FIX_STATUS、FIXED_TABLE),
随每次厂商发布而更新。
| 选项 | 作用 |
|---|---|
--on-host | 读取本机运行中的内核并给出真实判定 |
--ip-options | 建立一个连接,其 SYN 携带 Record-Route IP 选项(见下文) |
-o FILE | 同时将所有结果写入 CSV |
-a / -v | 显示隐藏结果 / 显示证据 |
-d | 调试:每个请求、头部和匹配 |
-q | 不显示横幅(用于脚本) |
-V | 打印版本 |
-h | 所有选项 |
--ip-options(需主动启用,仅限命令行)建立一个普通连接和一个 SYN 携带空 Record-Route IP 选项的连接,并报告
此类数据包是否能到达主机(reach)、是否在途中被丢弃(filtered)或
端口已关闭(noport)。这正是触达该漏洞的数据包形态,因此默认关闭,
且从不在网页上运行:请在你自己的主机上使用它,例如确认你的防火墙
会丢弃 IP 选项。单次连接无法赢得该漏洞所需的竞态,而 filtered
结果可能来自中间的任何网络。
nft add table ip cve98432
nft add chain ip cve98432 pre '{ type filter hook prerouting priority -300; }'
nft add rule ip cve98432 pre meta l4proto tcp ip hdrlength != 5 drop
sysctl -w net.mptcp.enabled=0 # if you don't need MPTCP
丢弃携带任何 IP 选项的 IPv4 TCP 数据包。重启后不会保留,且不能替代修复后的内核。
docker run -d --name kernel98scan --restart unless-stopped \
-p 127.0.0.1:8092:8092 \
-e PANIC_ORIGIN=https://kernel98scan.example.ch \
-v kernel98scan-data:/data \
ghcr.io/akaihigurashi/kernel98scan:latest
或使用 Compose:下载 docker-compose.yml,设置
PANIC_ORIGIN,然后运行 docker compose up -d。容器仅监听 localhost;
请在其前面放置一个带 TLS 的反向代理
(nginx 示例)。
| 变量 | 默认值 | 用途 |
|---|---|---|
PANIC_ORIGIN | – | 公开地址。来自其他来源的请求会被拒绝。请设置它。 |
PANIC_OPERATOR / PANIC_CONTACT | – | 页面底部可选的联系方式部分 |
PANIC_IMPRINT_URL | – | 指向现有版本说明的可选链接 |
PANIC_TRUSTED_PROXY | 127.0.0.1,::1 | 其 X-Real-IP 头受信任的代理 |
PANIC_DENY_HOSTS / PANIC_DENY_IPS | – | 永不检查这些域名 / 地址 |
PANIC_AUDIT_DIR | /data/audit | 每日审计日志文件 |
PANIC_RETENTION_DAYS | 30 | 早于此天数的审计文件会被删除 |
该页面具有与 php2scan 相同的保护措施:同意 复选框、审计日志、拒绝 VPN/Tor/数据中心客户端、速率限制、结果缓存、 仅限公共主机名且连接固定到被检查的地址、永不检查的主机(知名服务、访问量最高的 100 个网站、 政府网站、受制裁国家),以及禁止来自受制裁国家的访问。 该网页仅加载起始页;它从不发送 IP 选项。
扫描器引擎(抓取、指纹识别、端口扫描、vhost 发现、Web
服务器)位于 panic-core。
所有与 CVE-2026-98432 相关的内容都在 check.py 中。
kernel98scan.py 和 web.py 由两者生成:
git clone https://github.com/akaihigurashi/panic-core ../panic-core
python3 ../panic-core/bundle.py check.py
GPL-3.0,参见 LICENSE。字体: Bricolage Grotesque、Instrument Sans 和 JetBrains Mono(SIL Open Font License)。