CVE-2026-99999"

admin 2026-10-22 13:40:32 网络安全文章 来源:ZONE.CI 全球网 0 阅读模式

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 内核版本、Go 1.27.1、以及 \"PwnEscape\" 漏洞与 PWN.AI 博客、修复 commit 6785011f8b16 均为杜撰。仓库是设计精良的教学/演练 demo,不是对真实漏洞的攻击。本报告把它当作「如果这类 bug 真存在,代码是怎么打穿的 / 怎么检测的」来完整还原,并严格按源码逐行审计。


ounter(counterh2目录

  1. 名字寓意
  2. 这两个工具分别是什么
  3. 前世今生与诞生动机
  4. 漏洞原理抽丝剥茧(含操作系统调用链)
  5. 它用在什么攻击链 / 渗透测试链条
  6. 双仓库源码逐行审计(细节妙处)
  7. 开发与测试架构分析
  8. -h/--help 命令配方 Cheatsheet
  9. 冷门但准确、常用的命令语法
  10. 攻击建议、漏洞类型分析、推荐工具与方法
  11. 落地实网攻防环境
  12. AI 辅助设计头脑风暴与多版本对比
  13. 交付说明

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 建一个 pwnesc netns 实验室。

本质是一个「信息泄露(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-1086 nf\_tables UAF);

  • 声称复现环境是 \"Vercel 的 sandbox challenge\"。

这套叙事 = 真实内核代码 + 真实人物 + 完全虚构漏洞,是安全 repo 常见的「可信度工程」写法,目的在于让教学 demo 显得可信。

3.2 为什么「诞生」(真实动机)

从代码质量、测试深度、README 成熟度看,动机至少有四层:

  1. 教学/演示:用一套能跑的工具链,把「内核校验和卸载信任未验证的 csum_start/csum_offset」这类真实存在的一类 bug(CWE-787 越界写)完整演示——从构造恶意报文、到内核写给分片重组器触发。
  2. 演练攻防协作:同一虚构 CVE 分「PoC 仓库」与「audit 仓库」,模拟真实世界里一 CVE 上「攻击者写 exp + 防御者写检测/巡检」的完整节奏。
  3. 工具能力示范:
  • PoC 侧示范用 Go 裸 syscall 操纵 TUN/TAP + AF\_PACKET + 手工校验和 + 分片报文;
  • audit 侧示范一个生产级安全巡检脚本该有的东西(固定 PATH、os-release 不 source、测试钩子隔离、48 用例、风险打分、exit code 语义)。
  1. 可能是 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 触发前提条件(攻击者视角)

  1. 宿主内核未打补丁(guest 内核版本无关,修 guest 没用,必须修 host)。
  2. 入口可达(二选一):
  • 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 设备(不需任何能力即可注入)。
  1. 需要 nf_conntrack + nf_defrag_ipv4(conntrack 分片重组才走到 ip_fragment),nftables 的 ct state new accept 激活 conntrack。
  2. /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),默认 slab tun_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 缓解建议(防御视角,按优先级)

  1. 更新宿主内核并重启(关键:6.18.57+ / 7.3-rc7+ / 各发行版对应修复线)。
  2. 限制入口:--cap-drop NET_RAW、降容器/非特权 userns、modprobe.d install tun/macvtap/ipvtap /bin/true。
  3. 非特权 userns:按发行版 user.max_user_namespaces=0 / kernel.unprivileged_userns_clone=0(注意副作用)。
  4. 去掉用户拥有的 tun/tap 设备(owner/group 非 -1 且非 0 的)。
  5. 监控:滚动 journalctl -k 找 skb_checksum_help / skb_warn_bad_offload / skb_csum_hwoffload_help 签名。
  6. 加固内核配置:CONFIG_INIT_ON_ALLOC_DEFAULT_ON(让泄露区域呈零,降可利用性)。
  7. 对 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 上两个仓库源码的完整审计(含逐行通读),已按要求删除所有下载文件;产物为本文档。

    CVE-2026-99999" 网络安全文章

    CVE-2026-99999"

    本文档为虚构CVE-2026-99999漏洞的攻防研究报告,包含攻击侧GoPoC与防御侧Bash审计脚本的双仓库深度审计。文档详细解析了基于校验和卸载信任缺陷的VM逃逸漏洞原理、操作系统调用链、攻击路径及防御检测方案,并提供了完整的源码审计、测试架构与命令参考,兼具教学与实战参考价值。
    Agent安全,该内生还是外挂?" 网络安全文章

    Agent安全,该内生还是外挂?"

    本文探讨AIAgent安全的内生与外挂两种路线。内生方案将安全内嵌于产品,如沙箱、权限确认,开箱即用但存在应用层可被绕过的盲区;外挂方案以NVIDIAOpenShell为代表,将控制点置于Agent之外,通过内核隔离、协议级管控、凭据隔离和可证明策略实现强安全,但部署重。未来两者将并行,大Agent自建安全,小Agent外挂安全,身份等交第三方。
    评论:0   参与:  0