一个用于 CVE-2026-74580(“Vghost”)的边界检查与负责任披露辅助工具——这是一个真实的 Linux 内核 vhost 元数据缓存缺陷(CVE 记录、OSV 条目),可在暴露的 vhost/虚拟机监控程序主机上实现本地提权 / 虚拟机逃逸。
主要用例:检查你自己的边界。 输入你自己的 IP 范围、CIDR 或域名(import_list.py)——内部或外部均可——并对它们进行 vhost 相关暴露面(libvirt/Proxmox/QEMU 管理端口)的分类排查,然后为你的跟踪系统获取修复说明(run_pipeline.py --self,见下文“检查你自己的边界”)。无自动发现,无大规模联系人查询——主机本就归你所有。
次要能力:通过公开扫描数据进行第三方发现。 本项目不会自行扫描互联网——它可以选择性地查询已经完成扫描的数据集/API(Rapid7 Open Data / Sonar、Shodan、Censys),以发现其他暴露的主机,然后帮助你起草发给其所有者的披露通知。在投入时间之前值得了解:这件事没有赏金(CSIRT 不会付费,而且“已知 CVE、未打补丁实例”的报告很少符合赏金条件——见下文“影响”)——这是无偿的公民通知工作,而本仓库的大多数实际用途是上面的边界检查场景,而非这一场景。
CVE-2026-74580 是一个内核级、本地缺陷,而非面向 Web/网络的缺陷——由此可得出几点对分类排查很重要的结论:
仅当以下条件全部成立时才适用:
vhost-net/vhost-scsi,通常经由 libvirt 或 Proxmox)。没有虚拟机的裸金属主机,或使用普通 virtio 而未启用 vhost 卸载的虚拟机,无论补丁级别如何都不受此缺陷影响。真正的严重性驱动因素是:谁已经拥有(或能够获得)代码执行能力——而不是主机是否面向互联网。 互联网暴露只影响外部攻击者发现目标或远程到达初始立足点的难易程度;一旦有人获得该立足点,它不会改变该缺陷是否可被利用。仅内部、私有基础设施并非天然风险更低:
最关键的情况,与是否暴露于互联网无关:
互联网暴露真正重要的地方(次要因素,而非主要因素):
merge_and_triage.py 的评分所寻找的。不过,暴露的端口本身
并不能证明什么——只有当攻击者控制的东西能够在本地执行时它才有意义,
而被动扫描数据无法显示这一点(关于为什么本仓库不尝试对你不拥有的主机
进行确认,见下方“为什么仅被动”)。确实较低严重性的情况:
WAF 有帮助吗? 不能直接防御此漏洞——WAF 检查 HTTP(S) 流量,而此漏洞存在于内核的 vhost 元数据缓存 路径中,而非请求解析中。对于该漏洞本身而言,它根本不在暴露路径上。
WAF 确实重要的地方在于更早一步:现实世界中最常见的 攻击链是 Web 应用 RCE → guest 内代码执行 → vhost 漏洞利用 → 主机逃逸 → 横向移动到所有其他租户。WAF(或其他 Web 安全控制)可以阻止为攻击者提供初始立足点的 Web 应用 RCE, 从而防止他们一开始就触及此漏洞的前提条件。因此:与 vhost 漏洞本身无关,但与进入其前提条件的最常见路径之一相关。
真正端到端降低风险的措施:给内核打补丁、在不需要的地方禁用 vhost offload、不将 libvirt/Proxmox 管理端口暴露到互联网、 加固 guest 内运行的 Web 应用/服务(降低初始 RCE 的几率), 以及租户隔离加固(seccomp、namespaces),使任何立足点都更难获得。
主动对你未拥有或未获得书面授权的系统进行端口扫描或 SSH 探测,
在大多数司法管辖区属于未经授权的访问(在欧盟,这属于
Directive 2013/40/EU 的实施范围;捷克法律依据 § 230 trestního zákoníku 涵盖此行为),
并且对大多数主机/云服务商而言违反服务条款——
无论意图多么良好。本仓库只读取第三方扫描器已经收集
并在其自身授权下公开或出售访问权的数据。参见 SCOPE.md。
如果你想验证某个具体发现(确认主机确实存在漏洞,而不仅仅是 “看起来暴露”),这需要所有者的许可——参见披露模板, 其中正是请求这一许可。
src/sonar_query.py — 解析 Rapid7 Open Data / Sonar 扫描文件(免费下载,无需账户),以获取与 vhost 相邻的端口/横幅信息。src/shodan_query.py / src/censys_query.py — 可选,针对相同特征进行免费套餐 API 查询(适用于获取比 Sonar 定期转储更新的数据)。src/merge_and_triage.py — 合并结果、去重、对暴露可能性进行评分,输出 reports/candidates.csv。src/whois_contacts.py — 可选(见下文)— 为每个候选对象查找滥用/安全联系人(RDAP + security.txt 获取),用于披露路由。src/draft_notices.py — 为每个候选对象填充 templates/disclosure_email.md 到 reports/notices/,供你自行审阅并手动发送。此流水线中没有任何环节会自动发送邮件或联系任何人——通知仅为人工审阅和发送而起草,涉及大量组织的发现应通过你所在国家的 CSIRT 处理(见下文)。
上述步骤 4–5(联系人查找、披露起草)仅在你尚不知道主机所有者时才有意义——即通知陌生人。如果你改为输入自己的 IP 范围/域名(下文选项 B),以查看公开扫描数据对你自身暴露情况的描述,那么你已经知道所有者,因此完全跳过 whois_contacts.py,改用 src/draft_remediation.py 而非 draft_notices.py——它会为每个候选对象填充 templates/remediation_note.md 到 reports/remediation/(这是给你自己跟踪系统的工单,而非披露邮件;无需 RDAP/security.txt 查找)。run_pipeline.py --self 会自动执行此操作(见下文“可选:每周运行一次”)。
这也适用于内部主机,而不仅仅是面向互联网的主机。import_list.py/merge_and_triage.py 在 --self 模式下从不连接目标主机——它们仅解析 DNS(针对域名/FQDN)并进行 CIDR 运算,然后对你已提供的元数据进行评分。因此私有 IP 范围(10.x、172.16.x、192.168.x)、内部 FQDN 和 CNAME 均可使用,只要运行此工具的环境能够解析/访问它们(例如你的内部网络或 VPN)——--self 已经跳过了唯一具有 SSRF 限制的步骤(whois_contacts.py),因为无需为第三方查找联系人。Shodan/Censys/Sonar 显然不会覆盖内部主机(它们仅索引公共互联网);请改为通过选项 B 输入你自己的内部资产清单。
确认被标记的主机,而不仅仅是分诊: 上述所有内容均为被动分诊(端口/横幅信号)——它从不告诉你某台主机的内核是否确实未打补丁。为此,在你已获得授权访问的主机上(依据 SCOPE.md 的场景 B),使用 src/vghost_scan.py:它读取 uname -r 以及 vhost 内核模块是否已加载(不涉及任何漏洞利用相关内容,不修改主机),然后对照上文的修复版本表进行检查。
python3 src/vghost_scan.py --local # the machine you're already on
python3 src/vghost_scan.py --ssh-host [email protected] --confirm-authorized # a host you control, via your own SSH access
它每次运行只检查一个显式指定的主机——不支持批量/列表输入,不进行凭据猜测,也不进行发现。--ssh-host 按原样使用你现有的 SSH 访问方式(密钥/agent/配置);它自身从不读取、提示输入或存储凭据。
从仓库根目录运行所有内容——每个脚本使用的路径都相对于当前目录(data/、reports/、templates/、config/),而不是相对于脚本自身。
python3 -m venv .venv && source .venv/bin/activate
pip install -r requirements.txt
# Option A: free Rapid7 Sonar datasets (no key, large download)
python3 src/sonar_query.py --file sonar_tcp_16509.json.gz --port 16509 --port 16514 --port 8006
# Option B: your own list -- CSV with an "ip" and/or "domain" column (+ optional "port", other columns kept).
# The "ip" column may also hold a CIDR range (e.g. 203.0.113.0/24); it's expanded to
# individual addresses as passive-lookup candidates, capped by --max-hosts (default 65536).
python3 src/import_list.py --in hosts.csv
python3 src/import_list.py --in ranges.csv --max-hosts 4096
# Option C: Shodan free tier (set SHODAN_API_KEY env var)
python3 src/shodan_query.py --query 'product:QEMU' --query 'port:16509,16514'
# scoring ports/keywords live in config/scoring.json
python3 src/merge_and_triage.py --out reports/candidates.csv
python3 src/whois_contacts.py --in reports/candidates.csv --out reports/candidates_contacts.csv
python3 src/draft_notices.py --in reports/candidates_contacts.csv --out reports/notices/
以上所有操作都在本地运行——此仓库中没有 CI/定时任务。
API 密钥(SHODAN_API_KEY、CENSYS_API_ID/CENSYS_API_SECRET)从你自己 shell 的环境变量中读取,你使用的任何列表 CSV(选项 B)都保留在你的机器上,除非你选择提交或分享它。
要一次性设置这些密钥,而不是每次手动导出,请运行
scripts/init_env.sh(Linux/macOS)或 scripts/init_env.ps1(Windows)——
它会以交互方式在仓库根目录写入一个 .env 文件(已被 gitignore,永远不会
被提交)。下面的 src/run_pipeline.py 会自动加载它。
src/run_pipeline.py 将上述步骤串联起来(如果存在列表则导入列表 →
合并/分诊 → 联系人 → 起草通知;仅当你传入
--shodan-query 且已设置 SHODAN_API_KEY 时才使用 Shodan)。传入 --self 可检查你
自己的边界,而不是第三方的边界——这会跳过 whois_contacts.py
并写入修复说明(reports/remediation/),而不是
披露草稿:
python3 src/run_pipeline.py --hosts-csv input/hosts.csv --sender-name "Jane Doe"
python3 src/run_pipeline.py --self --hosts-csv input/our_ranges.csv
要在没有 CI/远程服务的情况下按计划运行,仓库提供了两个可选包装脚本——两者都不会自动安装,需要你自行注册:
scripts/weekly_cron.sh——请参阅文件顶部的注释,了解要添加的 crontab -e 行。scripts/weekly_task.ps1——请参阅文件顶部的注释,了解 schtasks /Create 命令。两者都只是在你的机器上从仓库根目录运行 run_pipeline.py,间隔由你设定;除了脚本本身已经发出的 API/RDAP 请求外,不会有任何数据离开你的机器。
当发现数量超过少数几个时,请通过你所在国家的 CSIRT 进行上报,而不是逐个给每个所有者发邮件——他们拥有大规模通知的授权和关系网络,而且通常已经掌握重叠的数据:
/.well-known/security.txt 联系方式,请始终优先使用该联系方式。