CVE-2026-99999
网安之家-CyberHomestead
2026年10月19日 07:00
湖北
去阅读
Part'counter(counterh1CVE-2026-99999「PwnEscape」× 双仓库深度审计与攻防研究报告
研究对象:
https://github.com/Quaerendir/cve-2026-99999-audit(防御侧审计,Bash)https://github.com/PeronGH/CVE-2026-99999(攻击侧 PoC,Go)前置声明(重要):这两个仓库围绕的
CVE-2026-99999是虚构的占位编号(真实 CVE 流水号不会用五位的99999),内含的6.18.x/7.2.x/7.3-rc7内核版本、Go1.27.1、以及 \"PwnEscape\" 漏洞与 PWN.AI 博客、修复 commit6785011f8b16均为杜撰。仓库是设计精良的教学/演练 demo,不是对真实漏洞的攻击。本报告把它当作「如果这类 bug 真存在,代码是怎么打穿的 / 怎么检测的」来完整还原,并严格按源码逐行审计。
ounter(counterh2目录
- 名字寓意
- 这两个工具分别是什么
- 前世今生与诞生动机
- 漏洞原理抽丝剥茧(含操作系统调用链)
- 它用在什么攻击链 / 渗透测试链条
- 双仓库源码逐行审计(细节妙处)
- 开发与测试架构分析
-h/--help命令配方 Cheatsheet- 冷门但准确、常用的命令语法
- 攻击建议、漏洞类型分析、推荐工具与方法
- 落地实网攻防环境
- AI 辅助设计头脑风暴与多版本对比
- 交付说明
ounter(counterh21. 名字的寓意
1.1 CVE-2026-99999 是刻意造出来的占位符
- 真实 CVE 编号格式是
CVE-<年>-<四位流水号>,流水号从0001起,任何公开 CVE 都不可能出现五位的99999。这个编号的作用是表达「这是一个概念上的、教学性质的、未分配的真实漏洞编号」,既不指向任何已披露漏洞,也避免误伤真实系统。 - 年份 2026 是作者设想里的披露年(README 声称公开于 2026-10-15,编出的时间线是「刚刚披露」,制造新鲜紧迫感)。
1.2 PwnEscape(别名)
Escape= 逃逸,点名这是 VM 逃逸(guest→host)或容器逃逸类漏洞。Pwn= 攻破/玩弄的黑话。- 合起来「攻破逃逸」,指从被隔离的 VM/容器穿越到宿主机。这类路径正是 tun/tap、vhost-net、virtio-net 半虚拟化网络,以及 Docker/Podman 默认容器能力(
CAP_NET_RAW)最被关注的那类隔离边界。
1.3 两个仓库名
Quaerendir(拉丁语「寻求者/调查者」)+audit→ 防御审计方。PeronGH+CVE-2026-99999→ 攻击验证方(PoC)。
两者是同一虚构漏洞的「攻防对」:一个证明「能打穿」,一个扫描并给出防御结论。这正是真实安全工具链的标准形态——一份 CVE 对应一枚攻击 PoC + 一个修复巡检脚本。
ounter(counterh22. 这两个工具分别是什么
2.1 PeronGH/CVE-2026-99999 —— 攻击侧 PoC(Go)
一个 439 行 Go 程序 + setup/teardown 脚本,自称 lab probe(实验探针)。它:
- 附着到预先准备好的 TAP 接口,向内核注入携带恶意
virtio_net_hdr的报文; - 探测内核的 IPv4 分片重组器是否信任一个被
skb_checksum_help()重写过的 IPv4 头; - 宣称在
6.18.54复现、6.18.57修复; go build编译,sudo ./scripts/setup.sh建一个pwnescnetns 实验室。
本质是一个「信息泄露(disclosure)原语」的验证器,不是能弹 shell 的完整 exp。 它验证的是:分片器会不会把超过线性头 28 字节的那 32 字节内容复制进输出报文的 IPv4 options 里——用输出包里的「过去线性头字节」来证明一次越界读发生了。
2.2 Quaerendir/cve-2026-99999-audit —— 防御侧审计(Bash)
一个 878 行纯 Bash 只读审计脚本cve-2026-99999-audit.sh,自称绝不动系统状态(不加载模块、不改 sysctl、不写 modprobe.d、不跑任何 PoC)。它:
- 盘点运行内核、发行版、内核包;
- 在内核包 changelog 里找修复(CVE 号或上游 commit subject);
- 把运行内核与「上游 stable 修复线」和「每发行版修复矩阵」比对;
- 探测
tun / macvtap / ipvtap / af_packet四条入口模块可达性; - 报告 VM/容器托管信号、用户拥有的 tun 设备;
- 检查 userns / AppArmor 门控;
- 在内核日志里 grep 崩溃签名;
- 输出 0–100 风险分 与 PATCHED / MITIGATED / VULNERABLE / UNKNOWN / NOT\_APPLICABLE 判定(带 exit code 0/1/2/3/4)。
配有 48 个回归测试(tests/run_tests.sh)、mock 二进制、fixture 树、ShellCheck CI,以及 examples/sample-output.txt / .json。
ounter(counterh23. 前世今生与诞生动机
3.1 虚构叙事的技术源头
README 声称它来自一条 Linux 内核攻击面,且用了大量真实存在的东西做背书:
- 核心函数
skb_checksum_help()(真实存在,负责补齐 IPv4/UDP/TCP 校验和); -
两条
Fixes:tag 指出的「坏元数据进入点」: -
f43798c27684 \"tun: Allow GSO using virtio_net_hdr\"(2.6.27,tun/tap)——真实 Kconfig/源码线; -
bfd5f4a3d605 \"packet: Add GSO/csum offload support.\"(AF\_PACKET)——真实; -
报告人
Paulos Yibelo(pwn.ai)——真实存在的前安全研究员(曾披露 2024 年CVE-2024-1086nf\_tables UAF); - 声称复现环境是 \"Vercel 的 sandbox challenge\"。
这套叙事 = 真实内核代码 + 真实人物 + 完全虚构漏洞,是安全 repo 常见的「可信度工程」写法,目的在于让教学 demo 显得可信。
3.2 为什么「诞生」(真实动机)
从代码质量、测试深度、README 成熟度看,动机至少有四层:
- 教学/演示:用一套能跑的工具链,把「内核校验和卸载信任未验证的
csum_start/csum_offset」这类真实存在的一类 bug(CWE-787 越界写)完整演示——从构造恶意报文、到内核写给分片重组器触发。 - 演练攻防协作:同一虚构 CVE 分「PoC 仓库」与「audit 仓库」,模拟真实世界里一 CVE 上「攻击者写 exp + 防御者写检测/巡检」的完整节奏。
- 工具能力示范:
- PoC 侧示范用 Go 裸 syscall 操纵 TUN/TAP + AF\_PACKET + 手工校验和 + 分片报文;
- audit 侧示范一个生产级安全巡检脚本该有的东西(固定 PATH、os-release 不 source、测试钩子隔离、48 用例、风险打分、exit code 语义)。
- 可能是 CTF/培训/红队课程的配套 demo,或讲「如何写内核网络类漏洞 PoC 与其对应检测器」的公开教材。
ounter(counterh24. 漏洞原理抽丝剥茧(含操作系统调用链)
以下是对虚构漏洞机制的忠实还原,把它当成「如果这类 bug 真存在,代码是怎么打穿的」来读。
4.1 漏洞类型
CWE-787 Out-of-bounds Write(越界写,写入点落在网络头及之后)。- 触发机制是「校验和卸载信任未验证的
csum_start/csum_offset」:当 skb 标记为CHECKSUM_PARTIAL时,内核在skb_checksum_help()里补全校验和,写回位置由skb->csum_start+skb->csum_offset决定。若这两个偏移直接取自外部可控的 virtio\_net\_hdr(来自 guest 或 AF\_PACKET),且内核在读 IPv4 头ihl字段之前完成这次写入,则写入位置可被攻击者精确控制。
4.2 构造:把 csum\_start 推到 IPv4 头部
PoC 的 frame():
// L2 层: [eth 14][vlanproto 2][vid0 2][vlanproto 2][vid0 2][ethertype 2] + IPv4
// l2Len = 14 + 4 + 4 = 22, csumAtIP = l2Len = 22
- 把 virtio 校验和头的
csum_start = 22(csumAtIP)。 - 内核收到后经
skb_vlan_untag()剥掉两层 VID-0 priority tag(每层 4 字节)。 - 内核在计算
csum_start时仍用原始偏移 22 →skb_checksum_help()在偏移 22 处写 2 字节校验和 → 正好落在 IPv4 头的version/ihl字节(offset 0,原值0x45)及其后一字节。
4.3 关键:把 0x45 改成 0x4f
0x45= version 4 + IHL 5(5×4 = 20 字节头)。- 通过
solve()枚举一个 16 位调整字w,让转发端 TTL 递减后,补全校验和写出的值恰好是0x004f→ 首字节变0x4f= version 4 + IHL 15(15×4 = 60 字节头)。 solve()逻辑:从偏移adj=32(落在第二个分片内容区)枚举0x0000..0xffff,对每个候选调forwardTTL()模拟 TTL 递减,再算csumPartial并 fold,找那个让 fold 值等于0x004f的w。
if csumFold(csumPartial(pkt)) == 0x004f { ... return true }
4.4 引爆:分片器用 60 字节头复制,读越界
- 现在 IHL 变成 15(60 字节)。IPv4 分片重组器(
ip_fragment())切分时会按ihl复制一个 60 字节头部。 - 但该 skb 的线性头(skb\_headlen)只有 28 字节(
ipLen 20 + head 8,-head 8默认)。于是 60 字节复制从skb_headlen之后多读60 - 28 = 32字节,这些是 skb 尾部 /skb_shared_info/ 相邻内存,最终嵌入 输出分片的 IPv4 options 字段。 - 这就是信息泄露原语:把「越界写(改 ihl)」转成「越界读(把 skb 尾巴 32 字节抓回输出包)」,攻击者在
pwnv1侧捕获。
4.5 逐层操作系统调用链(真实内核路径还原)
poc.go (writeTap)
└─ TUNSETIFF (ioctl 0x400454ca, IFF_TAP|IFF_NO_PI|IFF_VNET_HDR)
└─ write() to /dev/net/tun → tun_net_xmit()
└─ 进入网络栈收包路径 __netif_receive_skb_core()
└─ skb_vlan_untag() // 剥两层 VID-0 tag (14+4+4)
└─ ip_rcv() → ip_rcv_core() // IPv4 收包
└─ nf_conntrack prerouting (ct state new accept)
└─ nf_defrag_ipv4 触发 conntrack 分片重组
└─ 重组后 ip_forward() 路由转发
└─ ip_fragment() ← 按 ihl=15 复制 60 字节头
└─ 读 skb_headlen=28 之后的 32 字节 → 越界读
└─ 转发到 pwnv0 (MTU 200) → 再次分片 → 落在 pwnv1
校验和补全触发点 skb_checksum_help():
skb_checksum_help(skb)
// skb->csum_start = 22 (来自 virtio_net_hdr, 未验证)
// skb->csum_offset 指向目标字段
→ 在 csum_start 处写入 0x004f
→ 写坏 IPv4 version/IHL 字节 (offset 0): 0x45 → 0x4f
关键前提(README 明确):内核先补全校验和、后读 ihl。若先读 ihl 再算校验和,就不会用被污染的偏移破坏头——这正是修复 commit 6785011f8b16 声称做的事(在读 ihl 前校验 csum_start 是否落在合法头范围内)。
4.6 触发前提条件(攻击者视角)
- 宿主内核未打补丁(guest 内核版本无关,修 guest 没用,必须修 host)。
- 入口可达(二选一):
- Guest 路径:恶意 VM 的 virtio-net 经 tun/tap(QEMU/vhost-net/Firecracker/Cloud Hypervisor)或 macvtap 进主机;
- 本地路径:非特权用户或容器拿到
CAP_NET_RAW(AF\_PACKET +PACKET_VNET_HDR)或CAP_NET_ADMIN(tun),常见于非特权 userns、Docker/Podman 默认CAP_NET_RAW、持久存在的用户拥有 tun/tap 设备(不需任何能力即可注入)。
- 需要
nf_conntrack+nf_defrag_ipv4(conntrack 分片重组才走到ip_fragment),nftables的ct state new accept激活 conntrack。 /dev/net/tun存在 +ip/nft/sysctl/root。
ounter(counterh25. 它用在什么攻击链 / 渗透测试链条
初始访问(拿到一个 VM / 容器的受限 shell)
└─【逃逸原语】:guest→host 或 容器→宿主机 内存越界操作
├─ 信息泄露(本 PoC):读 skb 尾部内存 → 泄漏地址/相邻对象
└─ 若升级为任意读写 → 内核提权 → 宿主机 root
└─ 横向移动:以宿主机为跳板扫内网 / 打其他 VM / 维持权限
在攻击链中,它位居\\「边界突破 / 提权 / 隔离区内核利用」\\阶段,紧跟在「拿到一个受限制 VM 或容器 shell」之后。特别适合:
- 云沙箱 / 多租户(Firecracker、Cloud Hypervisor、Vercel 类 sandbox):guest→host 逃逸是核心 KPI;
- 容器集群:
CAP_NET_RAW是默认 cap + userns 开启,让几乎所有容器都具备构造 AF\_PACKET 恶意报文的能力,是容器化环境里内核网络驱动的经典提权面。
对渗透人员的意义:它演示了「不需要先有内核代码执行,只需要能向 host 网络栈注入报文」就能评估一个隔离边界是否可穿透,适合作为沙箱/VM 逃逸评估的一环,也可用于蓝队插件判断宿主机的真实暴露面。
ounter(counterh26. 双仓库源码逐行审计(细节妙处)
6.1 PoC(poc.go)—— Go 写内核网络层手工操作的教科书
妙点 1:魔法常量即文档。
const (
iffTap = 0x0002
iffNoPI = 0x1000
iffVnetHdr = 0x4000
tunSetIFF = 0x400454ca
tunSetSndBuf = 0x400454d4
)
0x400454ca 是经典 TUNSETIFF,0x400454d4 是 TUNSETSNDBUF。不用 libc,直接 syscall.Syscall(SYS_IOCTL)。
妙点 2:零第三方依赖,go.mod 仅三行。module pwnescape / go 1.27.1。只 import net、syscall、unsafe。这是「武器化代码」的典型风格——glibc/依赖越少,越易避开 seccomp 拦截、越易交叉编译。
妙点 3:unsafe 直接写 struct 偏移发 ioctl。
var req [64]byte
copy(req[:], name)
*(*uint16)(unsafe.Pointer(&req[16])) = iffTap | iffNoPI | iffVnetHdr
手工按 ifreq 布局写 flags,无新内核头文件也能编译。
妙点 4:solve() 用离线暴力搜索,不依赖内核响应。不是发包时猜校验和,而是预先枚举 64K 字,纯数学模拟 forwardTTL(+1 carry 传播)+ csumFold,找写入值会变成 0x004f 的载荷字。这是「确定性校验和重写」的优雅解法。
for w := 0; w < 65536; w++ {
payload[off] = byte(w)
payload[off+1] = byte(w >> 8)
pkt := datagram(id, payload)
forwardTTL(pkt)
if csumFold(csumPartial(pkt)) == 0x004f { ... }
}
妙点 5:IPv4 checksum 用手写 csumPartial(小端累加)。
sum += uint32(b[i]) | uint32(b[i+1])<<8 // 注意是小端累加
func csumFold(sum uint32) uint16 { ... return ^uint16(sum) }
对 TCP/UDP 校验和这类要「16 位回卷」的算法,先小端累加再 fold 取反;csumPartial 与 csumFold 职责分明,非常严谨。
妙点 6:用 -spray / -head / -page / -spray-fill 做「堆风水」。
-head:选择线性头长度(8 的倍数 8..40),控制越界字节数60-(20+head);-spray:在 head skb 之前分配同尺寸帧的数量(伙伴系统喷涂);-spray-fill:喷涂帧填充字节;-page:TUNSETSNDBUF=INT_MAX + O_NONBLOCK强制page_frag分支(tun_build_skb),默认 slabtun_alloc_skb。
妙点 7:verifyWrite() 先做一次「单数据报头改写」确定性自检。先凑出让写入值变 0x4f 的填字、发一个 datagram、抓输出、检查首字节是否为 0x4f;不对就 abort。先验证原语、再升级攻击,工程素养。
妙点 8:信息泄露用 AF_PACKET 抓包判定越界读。listen() 开 AF_PACKET SOCK_RAW ETH_P_ALL,Bind 到 SockaddrLinklayer 指定 ifindex,SO_RCVTIMEO 100ms。collect() 只收指定 ipID 的分片;把 ihl>20 的 options 段里、越过 c.head 的部分统计 nonzero 字节——nonzero 越多 = 越界读出的内容越新鲜(不是零填充),泄露越可用。
6.2 setup.sh / teardown.sh —— 把实验室做得可重复、可销毁
妙点 9:自动进入 netns 的递归重启技巧。
if [ -z \"${PWNESC_INNER:-}\" ]; then
ip netns add \"$NS\" 2>/dev/null || true
exec ip netns exec \"$NS\" env PWNESC_INNER=1 \"$0\"
fi
第一次在 host 跑就 exec ip netns exec pwnesc env PWNESC_INNER=1 \"$0\" 用同一脚本在 ns 内重跑自己,环境变量标记「已在内部」。
妙点 10:pwnv0 设 mtu 200 强制「输出再次分片」。重组后的 400+ 字节报文转发回去时被再次分片,触发 ip_fragment() 做「60 字节头复制」——让越界读真正发生。控制面完全自洽。
妙点 11:nft 只建 prerouting priority -450; ct state new accept,触发 conntrack。priority -450 是 conntrack 框架钩点,ct state new accept 激活连接跟踪(进而激活 nf_defrag_ipv4)。目的纯粹——让 conntrack 分片重组发生。
妙点 12:teardown.sh 只 ip netns del pwnesc。所有 sysctl/nft/接口都在 ns 里,删 ns = 一键回滚。这是「命名空间即实验室沙箱」的教科书用法。
6.3 audit 脚本(cve-2026-99999-audit.sh)—— 生产级安全巡检范本
妙点 13:固定 PATH + #!/bin/bash,防环境劫持。
#!/bin/bash
PATH=/usr/sbin:/usr/bin:/sbin:/bin
注释明写「它保护不了解释器自身,所以用 #!/bin/bash 而不是 #!/usr/bin/env bash」。针对 PATH 劫持/环境注入的加固。
妙点 14:测试钩子被「非 root」限定。
if [ -n \"${CVE_2026_99999_TEST_PATH:-}\" ] && [ \"${EUID:-0}\" -ne 0 ]; then
PATH=\"${CVE_2026_99999_TEST_PATH}:${PATH}\"
fi
R=\"\"
if [ -n \"${CVE_2026_99999_TEST_ROOT:-}\" ] && [ \"${EUID:-0}\" -ne 0 ]; then
R=\"${CVE_2026_99999_TEST_ROOT}\"
fi
审计脚本授意用环境变量注入 fixture 树/mock PATH,但在 root 实跑时强制忽略。run_tests.sh 同样有 [ EUID -eq 0 ] && exit。
妙点 15:/etc/os-release 用 sed 文本解析,绝不 source。
OS_ID=\"$(sed -n 's/^ID=//p' \"$OS_RELEASE_FILE\" | head -n1 | tr -d \"\\\"'\")\"
注释:「parsed as plain key=value, not sourced」。测试里有专门用例:PRETTY_NAME=\"$(touch <marker>)\",断言执行后 marker 未创建(PASS: os-release was not executed)。
妙点 16:版本比较「双保险」。
ver_ge() {
if have dpkg; then
dpkg --compare-versions \"$1\" ge \"$2\" && return 0 || return 1
fi
local smaller
smaller=\"$(printf '%s\
%s\
' \"$1\" \"$2\" | sort -V | head -n1)\"
[ \"$smaller\" = \"$2\" ]
}
有 dpkg 用权威比较器,否则回退 GNU version sort。权威优先、通用回退。
妙点 17:内核版本解析 kparse() 支持 -rc 与各种 distro 后缀。
[[ \"$s\" =~ ^([0-9]+)\\.([0-9]+)(\\.([0-9]+))? ]]
[[ \"$s\" =~ -rc([0-9]+) ]] && rc=\"${BASH_REMATCH[1]}\" || rc=999
把 6.12.113+deb13-amd64、7.3.0-rc6-1-mainline、5.14.0-611.el9.x86_64 统一解析为 maj min pat rc 四元组,rc=999 让非 rc 排最后;再 kernel_ver_ge() 四段逐位比大小。
妙点 18:上游修复矩阵 + 「EOL/未回传」语义。upstream_status() 内置「每个分支首个携带修复的版本号」表(5.10.272 / 5.15.223 / 6.1.190 / 6.6.159 / 6.12.113 / 6.18.57 / 7.2.11 / mainline 7.3-rc7),并明确区分:已知分支且到修复线 → PATCHED;低于修复线 → VULNERABLE;该 series 无稳定回传(EOL)→ VULNERABLE;< 2.6.27(bug 引入前)→ NOT\_APPLICABLE;≥ 7.3-rc7 → PATCHED。不是「版本新就安全」,而是精确到每个分支阈值。
妙点 19:probe_module() 是状态机式判定。每个条件拆成独立布尔(M_LOADED/M_BUILTIN/M_SHIPPED/M_COMPILED_OUT/M_BLACKLISTED/M_BLACKLIST_EFFECTIVE/M_INSTALL_OVERRIDE/M_REACHABLE),合成「是否 blocked」与「是否 truly reachable」。注释尤其精彩:
# A modprobe rule does not unload a module that is already in, and
# cannot touch a built-in one.
if [ \"$M_LOADED\" -eq 1 ] || [ \"$M_BUILTIN\" -eq 1 ] || \\
{ [ \"$M_SHIPPED\" -eq 1 ] && [ \"$blocked\" -eq 0 ]; }; then
「已加载/内建模块,光放 block 规则没用」——易被绕过的坑被点出来了。
妙点 20:「blacklist 只有 alias 被内核认可才有效」。
if modinfo -F alias \"$m\" 2>/dev/null | grep -qxF \"$alias\"; then M_BLACKLIST_EFFECTIVE=1; fi
blacklist 只挡自动加载(alias),不挡显式 modprobe;而漏洞入口正是「unprivileged/guest 通过 alias 触发的自动加载」,所以只有当模块真声明了该 alias 时 blacklist 才有效。测试用例 blacklist, module lacks alias: VULNERABLE 专门验证。
妙点 21:「not shipped」只在模块树存在时才可信。
if [ \"$HAVE_MODULES_DEP\" -eq 1 ]; then ... else M_SHIPPED=1; fi
容器里没有 /lib/modules,但宿主机内核照样能自动加载模块。所以模块树不存在时宁可保守按 M_SHIPPED=1。测试用例 no module tree, nothing provable: VULNERABLE。
妙点 22:风险评分模型带上下文注释。
# Score model (0..100). Tuned so that:
# - patched -> ~0..10
# - vulnerable, not a VM/container host, userns off -> ~55
# - vulnerable VM host with running guests -> 80+
# - + IoCs -> +10 (capped)
VULNERABLE +50、UNKNOWN +30、guest path +10、VM/接口 +15、local path(AppArmor 是否参与 +8/+15,AppArmor userns 门控可被公开链绕过,只降分不当硬阻断)、用户 tun +5、容器运行时 +5、IoC +10、cap 100。每一分都有上下文。
妙点 23:引用式「verdict → action」行动建议。五种 verdict 各自给出明确下一步(详见第 10 节)。尤其对 VULNERABLE 分场景给出 VM 宿主 / 非 VM 宿主 / 容器 / unprivileged userns / 用户 tun 的不同中间缓解,并明确提示副作用(sysctl -w user.max_user_namespaces=0 会破坏 rootless 容器、浏览器沙箱、Flatpak)。
妙点 24:--json 无依赖输出,不依赖 jq。json_escape() 手工处理 \\ \" \
\\r \\t,顺序遍历排序 key(mapfile + sort),非负整数输出数字、其余加引号。管线里可直接喂给 python3 -c \"import sys,json; ...\",不必装 jq。
妙点 25:-h|--help 直接用 sed 打印脚本头部。
-h|--help)
sed -n '2,63p' \"$0\"; exit 0 ;;
帮助文档就存在脚本注释头部里,sed -n '2,63p' 打印它——帮助信息永远和代码同源、不会版本漂移。前提是脚本为 Bash 且注释结构固定。
妙点 26:exit code 语义化(0/1/2/3/4)。
0 = PATCHED or NOT_APPLICABLE
1 = MITIGATED
2 = VULNERABLE
3 = UNKNOWN
4 = ERROR
可进 CI/Ansible 作「判定门」,run_tests.sh 大量 rc:N 断言验证。
妙点 27:测试钩子把「含注释的 modprobe 覆写」也隔离覆盖。CLOSED_MODPROBE=\"install tun /bin/true\
install macvtap /bin/true\
install ipvtap /bin/false\" 与 commented-out override is ignored 用例——\ 分隔并支持注释,防「你以为在 block,实际被注释掉」。
6.4 测试工程架构(run_tests.sh)
妙点 28:mock 二进制 + fixture 根目录 + 环境变量驱动,零第三方框架。MOCK_BIN/tests/mock_bin 里的 uname/dpkg/rpm/lsmod/modinfo/sysctl/journalctl/ps 都是带 TEST_* 环境变量的 shell 脚本;FIXTURES 按发行版分类;build_root() 动态搭 fixture 目录;CVE_2026_99999_TEST_ROOT / CVE_2026_99999_TEST_PATH 注入。
妙点 29:run_case 把「环境变量」和「期望」写成声明式测试。
run_case NAME [VAR=value ...] -- [key:expected ...]
每条测试一行、可读性极好,例如 \"tap blocked, userns open: VULNERABLE (local)\"。
妙点 30:每条 verdict 相关分支都被 mutation-tested。48 个用例覆盖:Arch/Fedora/Debian(13/12/sid/bookworm)/Ubuntu/Rocky9/Oracle UEK 各 patch 态;容器内→UNKNOWN;以及种种缓解语义(全关→MITIGATED、override 但已加载→VULNERABLE、tun 内建忽略 override、blacklist 有无 alias、modules_disabled=1、用户 tun、容器运行时+AF\_PACKET、运行 guest→risk\_score=90);还有 CLI 形态、quiet、未知选项 exit 4、以及 os-release-hostile 安全边界用例。
妙点 31:mock dpkg 把 --compare-versions 实装。因为 ver_ge() 需要真实的 debian 版本比较器做正确性基准,mock 只在 -l「查包」路径伪装,--compare-versions 直接 exec /usr/bin/dpkg --compare-versions \"$@\"。既 mock 了「包存在性」,又保住「比较语义权威」。
妙点 32:mock 覆盖「当前内核模块 alias 查询」。modinfo mock 里 tun 返回 devname:net/tun+char-major-10-200,macvtap/ipvtap/af_packet 各自返回对应 alias,使 blacklist 是否 effective 可以在测试中被真实验证。
ounter(counterh27. 开发与测试架构分析
7.1 整体架构分层
┌─────────────────────────────────────────────────────────┐
│ CVE-2026-99999 │
│ (虚构内核网络漏洞) │
└───────────────┬─────────────────────────┬───────────────┘
│ │
┌────────────▼────────────┐ ┌───────▼────────────────┐
│ PoC 仓库 (PeronGH) │ │ Audit 仓库 (Quaerendir)│
│ 攻击侧 / 验证侧 │ │ 防御侧 / 巡检侧 │
├─────────────────────────┤ ├────────────────────────┤
│ Go 1.27.1 │ │ Bash 4+ │
│ poc.go (439 行) │ │ cve-…-audit.sh (878 行)│
│ scripts/setup.sh │ │ README / LICENSE(MIT) │
│ scripts/teardown.sh │ │ CHANGELOG │
│ go.mod (3 行) │ │ examples/sample-*.{txt,json}│
│ 自建 pwnesc netns 实验室 │ │ tests/ (48 用例) │
│ │ │ run_tests.sh │
│ │ │ mock_bin/ (8 个) │
│ │ │ fixtures/ (8 个) │
│ │ │ .github/workflows/ │
│ │ │ shellcheck.yml │
└─────────────────────────┘ └────────────────────────┘
7.2 设计决策(选型背后的考虑)
- PoC 用 Go:
unsafe+syscall直通 ioctl/AF\_PACKET,单二进制、无 CGO、易跨平台、躲避某些 seccomp 过滤,适合做「可靠、可复现的资源级网络探测」。 - audit 用 Bash:安全巡检脚本要在任何 Linux 发行版轻量镜像里裸奔(
/etc/os-release、dpkg/rpm、lsmod/modinfo、sysctl),不引入 jq、不引入 go,curl | bash或从 Ansible 直接调都行。 - 两者都坚持「不留痕」:PoC 把可变状态全塞进一个
netns,teardown 删 ns 一了百了;audit 明确 never loads module / never touches sysctl / never runs PoC。 - 可复现性:audit 用 fixture 树 + mock PATH 把系统行为全部参数化,48 用例在 CI(shellcheck.yml)每次 push 就跑。
ounter(counterh28. -h/--help 命令配方 Cheatsheet
依据 audit 脚本头部注释(
sed -n '2,63p')与实际参数整理;PoC 侧用flag包,对应-flag。
8.1 audit 脚本 —— 参数一览(cve-2026-99999-audit.sh)
| 参数 | 作用 | 默认 |
|---|---|---|
--json |
输出结构化 JSON(到 stdout),关 banner | 无 |
--quiet |
只打一行 verdict + score + exit code | 无 |
--no-banner |
不打印 ASCII banner | 开 |
--no-hunt |
跳过内核日志 IoC 搜索 | 猎取开 |
--since=YYYY-MM-DD |
IoC 搜索起始时间 | 2026-09-22 |
--until=YYYY-MM-DD |
IoC 搜索结束时间 | 空(至今) |
-h / --help |
打印脚本头部注释(即帮助) | — |
--version |
打印版本 | — |
| (未知) | exit 4 | — |
8.2 PoC —— 参数一览(./poc)
| 参数 | 作用 | 默认 |
|---|---|---|
-head N |
offset-0 分片 payload(8..40,8 的倍数);越界字节 = 40-head |
8 |
-rest N |
完成分片的 payload(≥16) | 400 |
-spray N |
在 head skb 前喷同尺寸帧的数量 | 0 |
-spray-fill B |
喷涂帧填充字节 | 0xee |
-page |
强制 page_frag head |
slab head |
-skip-verify |
跳过单报文头改写自检 | 做自检 |
PWN_TAP / PWN_CAP(env) |
选 TAP 与抓包接口 | pwntap / pwnv1 |
ounter(counterh29. Command Recipes ——「把参数当可编程功能模块」
核心思想:参数是功能函数,用 shell 的循环、变量注入、管道、并发、批量自动化把它们搭成可编程体系。
9.1 一个函数封装 verdict 判定(CI/Ansible 安全门)
audit_cve99999() {
./cve-2026-99999-audit.sh --no-banner --no-hunt --quiet
}
if ! audit_cve99999; then
echo \"!! host still risky, route to patch queue\"; exit 1
fi
--quiet 只输出 VULNERABLE (xx/100): detail 一行;配合 exit code 直接塞进 Ansible failed_when / CI 判断。
9.2 批量扫描整批主机(for + 后台并发 + 汇总 JSON)
out=/tmp/cve99999
mkdir -p \"$out\"
for host in $(cat inventory.txt); do
ssh -t \"$host\" 'sudo /opt/cve99999/cve-2026-99999-audit.sh --json' \\
> \"$out/${host}.json\" 2>\"$out/${host}.err\" &
done
wait
for f in \"$out\"/*.json; do
python3 - \"$f\" <<'EOF'
import sys, json
d = json.load(open(sys.argv[1]))
print(f\"{d['hostname']:30s} ver={d['verdict']:12s} score={d['risk_score']:>3} \"
f\"guest={d['guest_path_reachable']} local={d['local_path_reachable']}\")
EOF
done
--json 把主机名、verdict、risk\_score、guest/local path 全吐出,适合做资产大盘。
9.3 用 --since/--until 做 4 小时轮询 + 告警
while true; do
cve-2026-99999-audit.sh --quiet --since \"$(date -d '-4 hours' +%F)\" --until \"$(date +%F)\"
sleep 14400
done
--since/--until 让 IoC 搜索变成「时间窗滚动检测器」。
9.4 输出送给监控/告警平台(无 jq)
cve-2026-99999-audit.sh --json --no-banner | python3 -c '
import sys, json
d = json.load(sys.stdin)
print(f\"cve99999_risk{{host=\\\"{d[\\\"hostname\\\"]}\\\"}} {d[\\\"risk_score\\\"]}\")
print(f\"cve99999_vuln{{host=\\\"{d[\\\"hostname\\\"]}\\\"}} {1 if d[\\\"verdict\\\"]==\\\"VULNERABLE\\\" else 0}\")
'
把 JSON 变成 Prometheus 指标样式行。
9.5 用环境变量把测试套件变成「参数化场景模拟」
TEST_UNAME_R=7.2.10-arch1-1 \\
TEST_OS_RELEASE=fixtures/os-release-arch \\
TEST_USERNS_MAX=0 \\
CVE_2026_99999_TEST_ROOT=\"$fixture_root\" \\
CVE_2026_99999_TEST_PATH=\"$MOCK_BIN\" \\
./cve-2026-99999-audit.sh --json
用来做「如果我把 userns 关了会怎样」的假想演练,不必真动系统。
9.6 PoC 侧批量「头部长度/喷涂」参数扫描(内存布局影响敏感性)
sudo ip netns exec pwnesc bash -lc '
for head in 8 16 24 32 40; do
for spray in 0 1 4 16 64; do
echo \"== head=$head spray=$spray ==\"
./poc -head \"$head\" -spray \"$spray\" -spray-fill 0xcc 2>&1 | grep -E \"result|past linear|frag 0\"
done
done
'
把 -head/-spray/-spray-fill 当「内存布局旋钮」扫描,量化哪个组合泄露最稳定、nonzero 最多。
9.7 用 PWN_TAP/PWN_CAP 做成可移植接口名
PWN_TAP=labtap PWN_CAP=labcap sudo ip netns exec pwnesc ./poc -skip-verify
接口名参数化,可在不同测试机/命名空间复用同一二进制。
9.8 「确定性自检」与「快速迭代」分层跑法
sudo ip netns exec pwnesc ./poc -page # 单独测 page_frag 路径
sudo ip netns exec pwnesc ./poc -skip-verify -head 16 -spray 32 # 快速进泄露阶段
-skip-verify 用于快速迭代,-page 专测 tun_build_skb(page\_frag)。
9.9 CI 一体化:setup → build → probe → teardown → audit
set -e
sudo ./scripts/setup.sh
sudo ip netns exec pwnesc ./poc -head 8 -spray 64 || true
sudo ./scripts/teardown.sh
./cve-2026-99999-audit.sh --no-hunt # 看该机判定
「自建 ns → 验证原语 → 销毁 ns → 独立审计脚本判定风险」本身就是完备的安全评估 Playbook。
ounter(counterh210. 冷门但准确、常用的命令语法
| 语法 | 说明 | 出处 |
|---|---|---|
set -o pipefail |
管道任一段失败即整条失败 | audit / run\_tests |
set -u |
未定义变量报错,防 $UNSET 当空串 |
audit |
[ -t 1 ] |
判断 stdout 是否 TTY,用于开/关颜色 | print_banner |
$'\\033[1;31m' |
Bash 里用 $'...' 写转义控制码 |
banner |
local a b; read -ra a <<< \"$pa\" |
按空白拆字符串为数组 | kernel_ver_ge |
[[ \"$s\" =~ ^([0-9]+)\\.([0-9]+) ]] |
Bash 正则绑定 BASH_REMATCH |
kparse |
BASH_REMATCH[4]:-0 |
未匹配到小组时给默认值 | kparse |
mapfile -t arr < <(...) |
读进程替换进数组,避免 fork 子 shell | JSON 排序 |
| `printf '%s\ | ||
| ' ... \ | sort -V \ | head -n1|sort -V= version sort |ver_ge` |
command -v \"$1\" >/dev/null 2>&1 |
判断命令存在(比 which 可靠) |
have() |
install tun /bin/true |
modprobe.d 的 install 覆写,把模块接管为 no-op | 缓解 |
blacklist alias + modinfo -F alias |
blacklist 只挡 alias 自动加载 | probe_module |
grep -qxF |
全行、quiet、固定字符串,避免正则误解 | modinfo alias 比对 |
awk '{print $1}' \\| grep -qx \"$m\" |
从 lsmod 输出精确匹配模块名 | probe_module |
kernel.modules_disabled=1 |
内核禁止再加载新模块 | 硬化信号 |
sysctl -w net.ipv4.conf.all.rp_filter=0 |
关反向路径过滤(实验室放行转发) | setup |
ip netns exec NS env VAR=1 \"$0\" |
递归脚本进入 netns,用环境变量标记 | setup |
ip neigh replace X lladdr Y dev Z nud permanent |
写永久 ARP 邻居 | setup |
nft add chain ... '{ type filter hook prerouting priority -450; policy accept }' |
手工起 conntrack 分片重组 | setup |
exec /usr/bin/dpkg --compare-versions \"$@\" |
mock 里把权威比较器转发真实工具 | mock dpkg |
sed -n '2,63p' \"$0\" 当 help |
帮助文档与脚本同源、永不漂移 | audit -h |
${var%% *} / ${var#* } |
参数切分(取包名/版本) | mock/dpkg |
declare -A J + ${!J[@]} |
关联数组存 JSON 字段,列出全部 key | audit JSON |
python3 -c \"...json.load(sys.stdin)...\" |
无 jq 场景解析 JSON 断言 | run\_tests |
ounter(counterh211. 攻击建议、漏洞类型分析、推荐工具与方法
11.1 漏洞类型断定
- 核心:
CWE-787(out-of-bounds write) + 派生CWE-125(out-of-bounds read)。 - 触发机制:信任外部可控的元数据(
csum_start/csum_offset) 写入未验证位置 → 改坏 IPv4 头 → 让分片器读取越界。 - 内核层面:
net/ipv4校验和卸载路径,借道skb_checksum_help/ip_fragment。 - 披露形态属性:类「Checksum Offload Confusion」,与 vhost-net/virtio-net checksum offload 对「外部偏移」的信任面相关。
11.2 攻击结束后的收益(真实升级路径)
- 本 PoC 只做信息泄露:读 skb 尾部 32 字节,可能含 skb 元数据、页指针、相邻对象。
- 真实目标是把其升级为内核任意读写/提权:读出
skb_shared_info、struct page指针后,构造 fake object、做 slab/page 攻击,最终拿 root。 - guest→host 场景即 VM 逃逸;容器→宿主机即 容器逃逸 + 宿主机提权。
11.3 推荐工具(按阶段)
| 阶段 | 工具 |
|---|---|
| 构建/验证 PoC | Go 1.2x + go build;strace -e ioctl,write,recvfrom;tcpdump -i pwnv1 -XX -vv |
| 内核层排查 | dpkg -l \\| grep linux-image 、uname -r、modinfo -F alias tun、cat /lib/modules/$(uname -r)/modules.dep |
| 模块可达性 | lsmod 、find /sys/class/net -name tun_flags、cat .../owner/group |
| 门控检查 | sysctl -n user.max_user_namespaces 、kernel.unprivileged_userns_clone、kernel.apparmor_restrict_unprivileged_userns |
| 日志猎捕 | journalctl -k --since ... --until ... \\| grep -iE 'skb_checksum_help|skb_warn_bad_offload|pwnescape' |
| CI/批量 | shellcheck 、bash -n、python3、ansible |
| 主动防御旋钮 | sysctl -w user.max_user_namespaces=0 、--cap-drop NET_RAW、modprobe.d install ... /bin/true |
| 可解释评估 | 本 audit 脚本的 --json + --quiet |
11.4 攻击建议(若真做这一类评估,红线提醒)
- 只在自建、隔离、可销毁的测试环境(
netns/ 独立 VM / 断开外网)里做。 - PoC 依赖 root +
tun+nf_conntrack,一次失败可能触发内核 oops,别在生产机/他人机器上跑。 - 验证成功后用
audit脚本独立判定主机风险,以「评估报告的漏/修结论」为主要产出。
11.5 缓解建议(防御视角,按优先级)
- 更新宿主内核并重启(关键:
6.18.57+/7.3-rc7+/ 各发行版对应修复线)。 - 限制入口:
--cap-drop NET_RAW、降容器/非特权 userns、modprobe.d install tun/macvtap/ipvtap /bin/true。 - 非特权 userns:按发行版
user.max_user_namespaces=0/kernel.unprivileged_userns_clone=0(注意副作用)。 - 去掉用户拥有的 tun/tap 设备(
owner/group非 -1 且非 0 的)。 - 监控:滚动
journalctl -k找skb_checksum_help/skb_warn_bad_offload/skb_csum_hwoffload_help签名。 - 加固内核配置:
CONFIG_INIT_ON_ALLOC_DEFAULT_ON(让泄露区域呈零,降可利用性)。 - 对 VM 宿主:打补丁前别跑不信任 guest;无运行时缓解。
ounter(counterh212. 落地实网攻防环境
- 红队侧:这是精确、可复现的边界验证工具。在 VM/容器受限用户上下文里,用它验证「宿主机是否信任未经校验的 checksum 元数据」。重点场景:云沙箱、多租户、容器集群、自建虚拟化。
- 蓝队侧:本
audit脚本可放进基线巡检 / CI 门禁 / Ansible 加固 Playbook,用它做「资产是否有 CVE-2026-99999 类入口暴露」的快速分类;可扩展成停机前必查项。 - 现实边界声明:由于整个 CVE 是虚构的,它不能替代真实的内核网络漏洞评估。其价值在于方法论与工程实现——你在真实评估同类「checksum offload / tun / af\_packet / userns-cap」暴露面时,可直接借鉴本仓库的探测维度、风险评分、verdict 语义和测试架构。
ounter(counterh213. AI 辅助设计头脑风暴与多版本对比
13.1 若由我重新设计,会怎么选型
版本 A(当前设计,最稳妥):攻防分离。
- 优点:PoC 与 audit 解耦、scope 清晰;audit 只读、可无损跑真实主机;PoC 藏于 netns 可销毁;工程化程度高。
- 缺点:两仓库各做各的,缺少 cross-validation(audit 没验证「PoC 是否真的让内核表现出 0x4f」)。
版本 B(加一个联调层):PoC 状态机 + audit 断言联动。
- 方案:让 PoC 的
verifyWrite输出「0x4f/非 0x4f」作为 exit code,audit 测试套件加一条「已知 vulnerable 内核上 PoC 报 0x4f、patched 内核上报非 0x4f」的对照。 - 优点:防「audit 说漏了但 PoC 打不动」的不一致。
- 缺点:需要能布置「打补丁前后」都有的 lab,复杂。
版本 C(降依赖):把 audit 做成单一 POSIX sh + 无 declare -A。
- 方案:去掉 Bash 关联数组,改用 line-separated 文本做 JSON,降到
/bin/sh可用,提升精简镜像兼容性。 - 缺点:牺牲 Bash 4 的
mapfile/BASH_REMATCH/declare -A,代码更啰嗦;现代 Linux 都有 bash,兼容性并非主要瓶颈。 - 结论:当前选 Bash 是正确权衡,不轻改动。
版本 D(更强防御语义):把风险分拆成「运维」与「合规」两套输出。
- 方案:
--json加mitigation_plan(按优先级排序的动作列表)与evidence(每条结论引用的实际命令/文件),供合规审计留痕。 - 优点:更职业,ISO / 等保类上报友好。
- 缺点:脚本更长,需更多 fixture。
推荐:版本 A 保持现状是正确选择——同时满足「可直接落地真实环境(audit 只读)」与「可做教学/演练(PoC 自研 lab)」。若要做成真实攻防工具,在 A 基础上补 B 的联调与 D 的行动计划/证据输出。
ounter(counterh214. 交付说明
已对两个仓库做全量代码审计(PoC 439 行 Go、setup/teardown、audit 878 行 Bash、48 条测试、8 个 mock 二进制、8 个 fixture、shellcheck.yml 工作流、README/CHANGELOG/LICENSE 全部读完),并在生成报告前把下载的 GitHub 文件彻底删除——_tmp_audit/ 已从磁盘移除,本地工作区清洁。
核心结论一句话
这两个仓库是围绕一个虚构的 CVE-2026-99999(\"PwnEscape\")精心打造的「攻防教学对」:PeronGH/CVE-2026-99999 是 Go 写的内核 skb_checksum_help() 校验和偏移越界读写 PoC(验证一个分片器越界读原语),Quaerendir/cve-2026-99999-audit 是 Bash 写的只读风险巡检脚本(打分 + verdict + 修复阈值矩阵 + 48 回归测试)。其真实价值不是「打某个真漏洞」,而是完整示范了网络内核类漏洞的 PoC 构建、lab 隔离、确定性校验、以及生产级审计脚本所有该有的安全工程细节。
本报告基于对 GitHub 上两个仓库源码的完整审计(含逐行通读),已按要求删除所有下载文件;产物为本文档。
版权声明
本站仅做备份收录,仅供研究与教学参考之用。
读者将信息用于其他用途的,全部法律及连带责任由读者自行承担,本站不承担任何责任。










评论