FirmAudit2 进阶篇:3天5本CNVD,从固件到操作系统——新增 OS 镜像 / 内核审计能力
原创
JiuZero
JiuZero
90Safe
2026年10月11日 11:55
广东
在小说阅读器读本章
去阅读
在公众号小说中沉浸阅读
一、先交代问题设定
首篇推文讲的是固件审计那条线:上传一份路由器固件,平台替你走完解包、静态、仿真、验证,最后打包出一份能直接交 CNVD 的资料。
但有我在宿舍突发奇想,考虑另一个场景:如果手里不是一份固件,而是一整张系统盘/系统镜像——一个 .iso、一个 qcow2 虚拟机镜像、或者一组 bzImage + rootfs呢?
这种目标,光\"解包看文件系统\"远远不够,真正要审的是内核、基础网络栈、驱动和认证权限组件:syscall 接口的越界写入、ioctl 的 copy_from_user 边界、提权路径里的 setuid/capability 泄漏、PAM 默认凭据……这些都不是靠静态扫文件系统能碰到的,得真去 fuzz、去触发 crash、去读内核 oops。
所以 FirmAudit2 在 v1.1.47 把审计对象从\"固件\"扩到了\"操作系统\",新增了操作系统漏洞审计(OS)。它和固件审计共用同一套沙箱、同一套 findings 协议、同一套 CNVD 打包管线,但流水线、攻击面口径和几条纪律是重新设计的。
数据同样统一从 log/database 抽取成文本展示,敏感信息不截图(依旧懒得打码)
二、系统总览
能力概览 
工作流程图 
攻击面台账是这条流水线和固件审计最大的分叉点。固件审计的收口门判的是\"段落填没填完\",而 OS 审计多了一本必须逐行闭合的账。
三、OS 审计流水线:多了一个专门干 fuzz 的阶段
固件审计的五阶段是解包/结构/静态/动态/报告。OS 审计换成另一套口径,对应 defaults/prompts/os.md 的阶段上报协议:
// internal/agent/os_prompt.go buildOSPromptFallback(节选)
// 执行流程:recon → setup → fuzz → analyze → verify
// fuzz:syzkaller syscall fuzz、AFL++ 网络/文件 fuzz、ioctl/驱动/认证组件专项
// analyze:crash root cause、提权路径、认证绕过
// verify:PoC + script 转录证据
五个阶段里,fuzz 是 OS 审计专属的核心阶段,固件审计没有这一档:
- recon(环境侦察)
:识别镜像/内核类型(file、binwalk、qemu-img info、fdisk -l)、版本号(uname -a、cat /etc/os-release、内核版本串)、架构、引导方式(GRUB/syslinux/U-Boot),并建立攻击面台账;
2. setup(环境搭建)
:在目标系统内装白名单内的辅助工具——编译(gcc/clang/make/cmake/musl-dev)、调试(gdb/strace/ltrace/valgrind/ASan)、二进制分析(objdump/readelf/radare2/rizin)、源码获取(git/wget/curl);
3. fuzz(模糊测试)
:按攻击面选策略——syzkaller 对 syscall 做结构化 fuzz、AFL++ 对网络服务二进制做文件/网络 fuzz、libFuzzer 对有解析函数的库做 in-process fuzz、对 login/sudo/su/pam 做输入变异;
4. analyze(分析)
:crash/oops 的 root cause 定位、提权路径分析、认证绕过验证;
5. verify(验证)
:每个可确认漏洞写独立 PoC,script 命令录制终端转录作证据。
工具链在 provision.sh 里有专门的 OS 安装段:syzkaller(GitHub 源码编译,含 Go 工具链自动安装)、AFL++/rr/crash(apt)、angr(pip);装不上也只是降级成警告,不阻断部署。
四、攻击面台账:把\"审没审完\"变成平台能读的东西
为什么要造这本账 
后台统计过同一份镜像、同一模型的多轮审计:攻击面条目数分别是 16 / 13 / 8 / 9,已证实分别是 5 / 3 / 0 / 2,而回合数和 token 消耗几乎恒定。
差别不在算力,在于模型有没有把\"还没看过的入口\"记在账上——它经常自报 done,但台账里还躺着 7 行没审。
首篇提过的收口门只管\"漏洞段落填没填完\",管不到\"攻击面有没有审全\"。于是单独做了一条平台真正消费的协议:Agent 覆盖写
/workspace/.cpi/audit/attack_surface.json,每行是一个可控入口点及其审查结论;
监视器每轮读取,落四道门(代码在 internal/agent/attack_surface.go):
// ledgerGateCategory:固件审计与操作系统审计都要求台账每行闭合;
// 复现与历史题型没有台账协议。
func ledgerGateCategory(category platform.Category) bool {
return category == platform.CategoryFirmware || category == platform.CategoryOS
}
// 状态取值与提示词一致:未审 / 审查中 / 已证实 / 已排除 / 已放弃
// summarizeAttackSurface 把\"收口障碍\"挑出来:
// pending = 未审 / 审查中(不允许留到收尾)
// unreasoned = 已排除 / 已放弃但没写 reason
// unknown = status 取值非法
落下的四道门各管一摊。
G1 负责把进度亮出来——台账状态分布一变就发 audit.surface 事件,前端阶段区能直接看到覆盖进度,不用等黑盒跑完才知道审了多少。
G2 卡的是收口:Agent 自报 done 时台账里还躺着\"未审/审查中\"的行,平台直接拒收、发 audit.stage.blocked,并把待办清单回投给会话让它补判,而不是一刀切判停。
G3 逼着写理由——已排除或已放弃的行必须带 reason,否则照样算收口障碍,免得出现\"我看过了但没写为什么排除\"这种没法复核的结论。
G4 只在矛盾时存疑:Agent 自报的阶段和工作区客观产物对不上(比如自报 verify,却既没已证实条目也没转录证据),平台发 audit.stage.suspect 提醒操作员,但只提示不改判——万一是同回合里刚补上的,误判成卡住反而更糟。
还有个细节救过事故:台账用 cat > 覆盖写,文件会瞬间变成 0 字节或半截 JSON;收口门只读一次会把撕裂写当成\"台账缺失\"误判暂停,所以收口门专用读取会等 400ms 再读一次区分\"真没写\"和\"正在写\"。
漏洞数量波动的根因,就是这本账之前平台从不读。现在它既是 Agent 的待办清单,也是平台收口的硬依据。
五、反扫描器纪律:强制手工 fuzz,堵住\"扫描器捷径\"
OS 审计和固件审计有个本质区别:固件审计偏静态+仿真验证,OS 审计要真去跑 fuzz、触发内核 crash。最容易偷懒的做法是装个 Trivy / Semgrep 扫一遍、把扫描报告当发现——但那种输出没有运行时证据,交 CNVD 根本站不住。
所以 os.md 列了明确的黑名单,平台在提示词层就把它堵死:
// defaults/prompts/os.md 安全边界与约束(节选)
// - 辅助审计工具安装白名单:gcc、clang、make、cmake、musl-dev、gdb、lldb、
// strace、ltrace、valgrind、objdump、readelf、nm、strings、xxd、file、
// radare2、rizin、capstone、python3、AFL++、libFuzzer、honggfuzz;
// - 禁止安装或使用自动化审计工具、自动化漏洞扫描器、自动化审计平台:
// Semgrep、CodeQL、SonarQube、Coverity、Snyk、Trivy、Grype、Clair、
// Nessus、OpenVAS 等一律不得安装或执行;
// - 静态分析与模糊测试必须由你手工构建目标与用例,不得依赖上述黑名单工具。
换句话说,OS 审计的\"发现\"必须来自 syzkaller/AFL++/libFuzzer 这类手工构建目标与用例的 fuzzing,或手工的静态分析+复现;扫描器能出的只是线索,不能当结论。
这条纪律和首篇讲的\"仿真验证硬门只认运行时回显\"是同一个底层原则——证据必须可复核,只是到了 OS 层面换成了\"拒绝扫描器捷径\"。
六、产品信息纪律:不替你猜厂商与型号
固件审计那边,平台能从文件名、版本文件里读出版本信息;OS 镜像这块我特意改了口径——平台取消厂商/型号的自动识别,上报表里的\"厂商/产品/版本\"只是操作员的可选填写值。报告里写什么,必须以 Agent 从镜像内容实测确证的结果为准:
// defaults/prompts/os.md 产品信息纪律(节选)
// 报告中的厂商/型号/版本必须以你从镜像内容实测确证的结果为准
// (os-release、内核版本字符串、启动横幅等交叉印证),
// 无法确证时必须在报告中如实注明「厂商/型号未能确认」,
// 禁止用文件名猜测或组件版本冒充产品信息。
这条规定是为防一种常见幻觉:模型看一眼文件名 ubuntu-virt-3.24.1.iso 就把厂商写成 Ubuntu、版本写成 3.24.1,却从没真正 cat /etc/os-release 印证。
现在窗口里留的占位文本就是\"未提供(操作员未填写;请从镜像内容确证,无法确证时在报告中如实注明「未能确认」,禁止臆测)\"——把\"我不知道\"变成强制声明,而不是默默编一个。
七、网络门控与模型网关:流量只走宿主中转
OS 审计里 Agent 要装工具、拉源码、跑 fuzz,网络需求比固件审计重,所以隔离边界更要讲清楚。沙箱的网络不是\"能连就能连\",而是 daemon 在任务命名空间内用 iptables 白名单实施的,提示词必须和内核实际行为一致,否则 Agent 会去试被丢弃的外联、白白浪费回合:
// internal/agent/firmware_prompt.go sandboxNetworkNotice(OS 审计同用)
// NetworkRestricted:默认禁止出站流量,仅放行宿主模型网关与配置的内网白名单。
// 联网模式:可访问官方软件源/包仓库,但结论必须基于对镜像本身的独立验证,
// 不得把网上信息直接当作发现;攻击载荷只允许打在沙箱内部仿真目标与 127.0.0.1 上。
这里有两件事得说清。模型流量统一走宿主网关中转(modelgateway),容器里禁止直连外网 API,上游密钥只在 daemon 侧持有,既不序列化给前端也不进容器;攻击载荷则只落在沙箱内部仿真目标与回环地址,绝不允许把 payload 打向任何真实第三方。
这和首篇的 overlay 临时根是同一套\"运行时约束\"思路:让 Agent 能干活,但写不坏宿主、连不到外面、密钥不出 daemon。
八、交付形态:复用同一套平铺总包
OS 审计的发现走的是和固件审计完全相同的收口路径:每段漏洞资料过首篇讲过的分段引导与四道质量门,过了门就平铺进 CNVD 交付包——镜像副本 + 报告 + PoC + 证据,全部平铺在包根,评审者不需要理解目录结构。v1.1.57 把攻击面台账收口门也并进了这条路径,所以 OS 任务不会在台账缺失/未闭合时被判\"资料齐备\"。
唯一 OS 专属的交付物是 审计总报告.md(v1.1.56 起三个审计类别都有了独立的报告页签 + Markdown 渲染):至少含镜像概览、实测版本信息、攻击面覆盖清单、漏洞汇总表与每漏洞的复现证据索引。
九、现有产出展示(貌似成果才是重点...)
十、购买入口(白帽集市)
FirmAudit2 · 固件 / 操作系统漏洞审计平台
- 【占位:价格】
¥50(公测价)
* 【授权方式】单设备永久授权
* 【库存余量】11
* 【售后与更新】版本更新:保持迭代推送;答疑渠道:微信群聊
* 【购买入口】白帽集市搜索「FirmAudit2」,或直接扫描上方二维码
前置环境:Windows 11 x64、已启用 WSL2(wsl --install)、磁盘建议 ≥ 40GB 可用、首次部署工具链需联网。大模型 API 需自备,平台不内置任何模型额度。(OS 审计要装 syzkaller/AFL++ 这类重工具,建议用聪明一点的模型,并给沙箱留足磁盘与内存。)
结语
首篇解决了\"怎么保证固件漏洞是真的、能拿出证据、能变成可交的格式\"。本篇补的是另一半:
当目标从一份固件变成一整张系统镜像/内核,审计对象从文件系统挪到了内核态与系统组件,平台该怎么把\"审没审全\"也变成可校验的东西——攻击面台账、反扫描器纪律、产品信息实测确证,加上延续下来的网络门控,合起来就是 OS 审计线的底气。
版权声明
本站仅做备份收录,仅供研究与教学参考之用。
读者将信息用于其他用途的,全部法律及连带责任由读者自行承担,本站不承担任何责任。








评论