krisiasty/vcheck
GitHub: krisiasty/vcheck
通过 SSH 远程检测 Linux 主机特定内核模块漏洞,并支持一键生成黑名单配置进行自动化缓解的安全加固工具。
Stars: 2 | Forks: 0
# vcheck
通过 SSH 审计远程 Linux 主机是否存在 Copy Fail 和 Dirty Frag 内核模块漏洞,并可选择应用缓解措施:
| CVE | 名称 | 受影响的模块 |
| ---------------- | -------------------- | ------------------------------------------- |
| CVE-2026-31431 | Copy Fail | `algif_aead` |
| CVE-2026-43284 | Dirty Frag (IPsec) | `esp4`, `esp6`, `xfrm_algo`, `xfrm_user` |
| CVE-2026-43500 | Dirty Frag (RxRPC) | `rxrpc`, `kafs` |
对于每个受影响的模块,vcheck 会报告其当前是否已加载、是否内建在运行中的内核、在内核日志中是否有历史痕迹、是否有活动的 `AF_ALG` 套接字(仅限 Copy Fail),以及是否已在 `/etc/modprobe.d/` 下被加入黑名单。
使用 `-fix` 参数时,vcheck 会报告初始状态,为任何尚未加入黑名单的模块写入一个 `cve-XXXX-XXXXX-disable.conf` 配置片段,然后重新运行检查并报告最终状态。
## 仅在纯检查运行后使用 `-fix`
**务必先不带 `-fix` 运行 vcheck。** 阅读报告并确认受影响的模块在此主机上可安全禁用后,再使用 `-fix` 重新运行。禁用合法工作负载所依赖的内核模块可能会影响用户并破坏应用程序。
特别是在以下情况:
- **仅当**没有任何受影响的模块当前被加载时,才将 `-fix` 视为安全的——即每个模块都被报告为 `mitigated` 或 `module not blacklisted`(没有 `VULNERABLE` 或 `blacklisted but currently loaded` 行)。已加载的模块几乎总是意味着*主机上的某些程序正在主动使用它*;在将其加入黑名单之前请先进行验证。
- IPsec 模块(`esp4`、`esp6`、`xfrm_algo`、`xfrm_user`)是任何 IPsec/strongSwan/WireGuard-over-IPsec/IKE 部署所必需的。请勿在 VPN 网关、IPsec 端点或任何 `ip xfrm policy` 返回规则的主机上禁用它们。
- RxRPC 模块(`rxrpc`、`kafs`)是任何挂载了 AFS 文件系统的主机所必需的。禁用它们将导致这些挂载在下次启动时失效。
- `algif_aead` 通过 `AF_ALG` 套接字家族暴露内核加密功能。应用程序代码很少直接使用它,但在加入黑名单前,请通过列出活动的套接字(`ss -p --af-alg`)并检查用户空间消费者来进行验证。
vcheck 写入的黑名单配置片段只在模块加载时生效(通常在下次启动,或者在系统空闲时执行 `modprobe -r ` 时生效)。
一个*已经加载*的模块即使在运行 `-fix` 之后也会继续运行——
vcheck 会将其报告为 `blacklisted but currently loaded; run 'modprobe -r' or reboot`。
## 安装
**Homebrew (macOS):**
```
brew install --cask krisiasty/tap/vcheck
```
针对 Linux、macOS 和 Windows 的**预编译二进制文件**发布在 [发布页面](https://github.com/krisiasty/vcheck/releases)。
**从源代码构建**(需要 Go 1.26+):
```
go install github.com/krisiasty/vcheck@latest
```
## 用法
```
vcheck -host HOST [flags]
```
| 标志 | 默认值 | 描述 |
| ------------------- | ------------- | -------------------------------------------------------------------------------------------- |
| `-host` | *(必填)* | 远程主机 |
| `-user` | `$USER` | 远程用户 |
| `-port` | `22` | 远程 SSH 端口 |
| `-agent` | `true` | 使用 SSH 代理进行身份验证 |
| `-identity` | *(空)* | 私钥文件路径(如果已加密,则提示输入密码) |
| `-password` | `false` | 提示输入 SSH 密码 |
| `-insecure` | `false` | 接受尚未记录在 `known_hosts` 中的主机密钥;与已记录的密钥不匹配仍然会失败 |
| `-fix` | `false` | 为尚未加入黑名单的模块写入 `/etc/modprobe.d` 配置片段 |
| `-timeout` | `15s` | SSH 连接超时 |
| `-command-timeout` | `30s` | 远程命令超时(`0` 表示禁用) |
| `-debug` | `false` | 增加日志详细程度 |
| `-version` | `false` | 显示版本并退出 |
必须至少提供 `-agent`、`-identity` 或 `-password` 其中的一个,以产生可用的身份验证方法。
验证方法将按列出的顺序进行尝试。
目标主机上需要使用 Sudo。如果配置了无密码 sudo,该工具将静默执行;
否则,它会提示输入一次密码(输入会被隐藏),并在随后的每个命令中通过 `sudo -S` 传递该密码。
## 退出码
| 代码 | 含义 |
| ---- | ----------------------------------------------------------------------------------- |
| `0` | 所有受影响的模块均已加入黑名单、已卸载、未内建且处于非活动状态 |
| `1` | 用法错误 |
| `2` | SSH 连接失败 |
| `3` | Sudo 身份验证失败 |
| `4` | 一个或多个模块未加入黑名单(当前无暴露风险) |
| `5` | 一个或多个模块当前已加载、内建在内核中或正在被使用 |
| `99` | 内部/检查失败 |
## 示例输出
### 完全缓解的主机
```
$ vcheck -host host.example.com -identity ~/.ssh/id_ed25519
INF connected user=ops host=host.example.com port=22
INF checking vulnerability cve=CVE-2026-31431 name="Copy Fail"
INF checking vulnerability cve=CVE-2026-43284 name="Dirty Frag (IPsec)"
INF checking vulnerability cve=CVE-2026-43500 name="Dirty Frag (RxRPC)"
INF mitigated cve=CVE-2026-31431 module=algif_aead
INF mitigated cve=CVE-2026-43284 module=esp4
INF mitigated cve=CVE-2026-43284 module=esp6
INF mitigated cve=CVE-2026-43284 module=xfrm_algo
INF mitigated cve=CVE-2026-43284 module=xfrm_user
INF mitigated cve=CVE-2026-43500 module=rxrpc
INF mitigated cve=CVE-2026-43500 module=kafs
```
### 未缓解且部分加载 —— 纯检查
```
$ vcheck -host host.example.com -identity ~/.ssh/id_ed25519
INF connected user=ops host=host.example.com port=22
INF checking vulnerability cve=CVE-2026-31431 name="Copy Fail"
INF checking vulnerability cve=CVE-2026-43284 name="Dirty Frag (IPsec)"
INF checking vulnerability cve=CVE-2026-43500 name="Dirty Frag (RxRPC)"
INF mitigated cve=CVE-2026-31431 module=algif_aead
ERR VULNERABLE cve=CVE-2026-43284 module=esp4 loaded=true
ERR module not blacklisted cve=CVE-2026-43284 module=esp6
ERR module not blacklisted cve=CVE-2026-43284 module=xfrm_algo
ERR module not blacklisted cve=CVE-2026-43284 module=xfrm_user
ERR module not blacklisted cve=CVE-2026-43500 module=rxrpc
ERR module not blacklisted cve=CVE-2026-43500 module=kafs
```
### 相同主机使用 `-fix` —— 首次运行
```
$ vcheck -fix -host host.example.com -identity ~/.ssh/id_ed25519
INF connected user=ops host=host.example.com port=22
INF checking vulnerability cve=CVE-2026-31431 name="Copy Fail"
INF checking vulnerability cve=CVE-2026-43284 name="Dirty Frag (IPsec)"
INF checking vulnerability cve=CVE-2026-43500 name="Dirty Frag (RxRPC)"
INF findings before fix
INF mitigated cve=CVE-2026-31431 module=algif_aead
ERR VULNERABLE cve=CVE-2026-43284 module=esp4 loaded=true
ERR module not blacklisted cve=CVE-2026-43284 module=esp6
ERR module not blacklisted cve=CVE-2026-43284 module=xfrm_algo
ERR module not blacklisted cve=CVE-2026-43284 module=xfrm_user
ERR module not blacklisted cve=CVE-2026-43500 module=rxrpc
ERR module not blacklisted cve=CVE-2026-43500 module=kafs
INF writing modprobe.d snippet path=/etc/modprobe.d/cve-2026-43284-disable.conf modules="[esp4 esp6 xfrm_algo xfrm_user]"
INF writing modprobe.d snippet path=/etc/modprobe.d/cve-2026-43500-disable.conf modules="[rxrpc kafs]"
INF re-scanning after fix snippets_written=2
INF checking vulnerability cve=CVE-2026-31431 name="Copy Fail"
INF checking vulnerability cve=CVE-2026-43284 name="Dirty Frag (IPsec)"
INF checking vulnerability cve=CVE-2026-43500 name="Dirty Frag (RxRPC)"
INF findings after fix
INF mitigated cve=CVE-2026-31431 module=algif_aead
ERR blacklisted but currently loaded; run 'modprobe -r' or reboot cve=CVE-2026-43284 module=esp4
INF mitigated cve=CVE-2026-43284 module=esp6
INF mitigated cve=CVE-2026-43284 module=xfrm_algo
INF mitigated cve=CVE-2026-43284 module=xfrm_user
INF mitigated cve=CVE-2026-43500 module=rxrpc
INF mitigated cve=CVE-2026-43500 module=kafs
```
黑名单已生效,但在写入配置片段之前,`esp4` 已被加载到内核中。重新启动或在目标主机上运行 `sudo modprobe -r esp4` 以将其彻底清除。
### 第二次运行 —— esp4 仍被加载
```
$ vcheck -fix -host host.example.com -identity ~/.ssh/id_ed25519
INF connected user=ops host=host.example.com port=22
INF checking vulnerability cve=CVE-2026-31431 name="Copy Fail"
INF checking vulnerability cve=CVE-2026-43284 name="Dirty Frag (IPsec)"
INF checking vulnerability cve=CVE-2026-43500 name="Dirty Frag (RxRPC)"
INF findings before fix
INF mitigated cve=CVE-2026-31431 module=algif_aead
ERR blacklisted but currently loaded; run 'modprobe -r' or reboot cve=CVE-2026-43284 module=esp4
INF mitigated cve=CVE-2026-43284 module=esp6
INF mitigated cve=CVE-2026-43284 module=xfrm_algo
INF mitigated cve=CVE-2026-43284 module=xfrm_user
INF mitigated cve=CVE-2026-43500 module=rxrpc
INF mitigated cve=CVE-2026-43500 module=kafs
INF fix: nothing to do — all affected modules already blacklisted
```
### 第三次运行 —— 执行 `modprobe -r esp4` 之后
```
$ vcheck -host host.example.com -identity ~/.ssh/id_ed25519
INF connected user=ops host=host.example.com port=22
INF checking vulnerability cve=CVE-2026-31431 name="Copy Fail"
INF checking vulnerability cve=CVE-2026-43284 name="Dirty Frag (IPsec)"
INF checking vulnerability cve=CVE-2026-43500 name="Dirty Frag (RxRPC)"
INF mitigated cve=CVE-2026-31431 module=algif_aead
INF mitigated cve=CVE-2026-43284 module=esp4
INF mitigated cve=CVE-2026-43284 module=esp6
INF mitigated cve=CVE-2026-43284 module=xfrm_algo
INF mitigated cve=CVE-2026-43284 module=xfrm_user
INF mitigated cve=CVE-2026-43500 module=rxrpc
INF mitigated cve=CVE-2026-43500 module=kafs
```
### 首次连接的主机 —— `-insecure`
```
$ vcheck -insecure -host host.example.com -identity ~/.ssh/id_ed25519
WRN host key not in known_hosts; accepting due to -insecure host=host.example.com:22 remote=192.0.2.42:22 fingerprint=SHA256:AAAAEXAMPLEfingerPrint000000000000000000000
INF connected user=ops host=host.example.com port=22
INF checking vulnerability cve=CVE-2026-31431 name="Copy Fail"
...
```
`-insecure` 会接受尚未存在于 `known_hosts` 中的主机。如果某台主机*已经*被记录并提供了不同的密钥,连接仍然会失败——
该标志仅作为“首次连接”的覆盖选项,而不是用于在已知主机上抑制中间人警告的方法。
## 检测细节
- **已加载:** 获取一次 `lsmod` 的结果,并将模块名与第一列进行匹配。
- **内建:** 检查 `/lib/modules/$(uname -r)/modules.builtin` 和 `/sys/module`,因为 modprobe 配置片段无法禁用编译进运行中内核的代码。
- **已加入黑名单:** 搜索 `/etc/modprobe.d/` 下的每个文件(使用 `grep -rE`)以查找 `install /bin/false` 指令——这与 vcheck 在使用 `-fix` 时写入的格式相同。其他形式的禁用(例如 `blacklist`)无法被识别。
- **历史活动:** 首先查询 `journalctl -k`,如果不可用则回退到 `/var/log/kern.log`;每个模块保留最后五行匹配的记录。
- **活动套接字(仅限 `algif_aead`):** `ss -p --af-alg` 列出打开的 `AF_ALG` 套接字;除标题外的任何输出都被视为正在使用中。
所有命令均通过 `sudo` 运行,并受 `-command-timeout` 限制——需要特权访问才能读取 `/var/log/kern.log`、列出 `AF_ALG` 套接字以及在 `/etc/modprobe.d/` 下进行写入。
标签:AF_ALG, Copy Fail, CVE-2026-31431, CVE-2026-43284, CVE-2026-43500, Dirty Frag, EVTX分析, GitHub Advanced Security, Go语言(推测), IPsec安全, Linux内核安全, modprobe黑名单, osquery, RxRPC, SSH远程审计, 内存分配, 内核模块检测, 子域名枚举, 安全加固, 安全基线检查, 安全渗透, 无线安全, 日志审计, 漏洞修复, 漏洞缓解, 系统安全, 网络安全, 网络安全培训, 自动化安全工具, 运维安全, 远程执行检查, 隐私保护