开源GitHub官方博客·原文 2026年9月25日

GitHub Security Lab发布AI模糊测试流水线Fuzzing Taskflow

该流水线基于GitHub Security Lab Taskflow Agent构建,用户只需提供一个GitHub仓库地址,代理就会自动完成识别入口点、编写harness、运行AFL++、读取覆盖率报告、改进harness、分类崩溃并生成漏洞报告的全流程。

GitHub Security Lab发布了Fuzzing Taskflow,一套面向C/C++项目的自主模糊测试流水线,基于其LLM驱动的安全自动化框架Taskflow Agent构建。用户只需向脚本提供一个GitHub owner/repo地址,代理就会自动完成一系列步骤:安装AFL等软件、克隆仓库、识别代码中最相关的函数、为这些函数创建fuzz target,之后运行AFL++、读取覆盖率报告、改进harness、分类每一个崩溃,并为每个独立bug生成漏洞报告。

据GitHub官方博客介绍,启动方式是在https://github.com/GitHubSecurityLab/seclab-taskflows-fuzzing打开一个codespace,然后运行 ./scripts/fuzzing/run_fuzzing.sh PROJECT。GitHub给出了两个示例:针对正式项目运行 ./scripts/fuzzing/run_fuzzing.sh tukaani-project/xz;如果想在投入长时间测试前先快速冒烟测试,可以对小型项目运行 ./scripts/fuzzing/run_fuzzing.sh DaveGamble/cJSON。

GitHub在文中特别警告:该taskflow直接在宿主机上运行afl-fuzz、clang以及由LLM选择的任意构建命令,中间没有容器隔离。被提示注入的代理理论上可以执行该用户能做的任何操作,因此只应在一次性环境(如Codespace或临时虚拟机)中运行,且不要赋予提升的权限。

默认调用Claude Sonnet 5,模型可在配置文件中更换

GitHub表示,部分前沿模型会对输出施加安全护栏。对于这个模糊测试taskflow,默认使用Claude Sonnet 5,因为它通过了GitHub的所有内部测试。用户可以通过修改文件src/seclab_taskflows_fuzzing/configs/model_config.yaml来选择其他模型。

三层架构:shell驱动、taskflow YAML与MCP工具

GitHub把整个流水线分为三层:shell驱动(run_fuzzing.sh)负责串联各阶段;一组taskflow YAML文件对应每个阶段,本质上是指示LLM代理每一步做什么的提示词;一组MCP工具供代理调用以实际执行工作,例如运行AFL、编译harness、存储崩溃、读取覆盖率报告。

作者强调最重要的设计原则是职责分离:LLM代理负责决策,决定fuzz什么、写什么harness、下一步追哪个覆盖率缺口;MCP工具只暴露执行原语,比如run_afl_for或compile_harness,代理从不直接调用AFL或clang。所有状态存放在SQLite数据库(fuzz_context.db)中,各阶段之间不通过内存传递数据。

一个细节是每个harness会被构建两次。AFL的边缘插桩适合引导fuzzer,但无法生成人类可读的覆盖率报告,因此每个harness同时生成 .afl二进制(使用afl-clang-lto -fsanitize=address,undefined构建)和 .cov二进制(使用clang -fprofile-instr-generate -fcoverage-mapping构建)。前者用于fuzz,后者在之后重放AFL队列以生成真实的源码行和分支覆盖率。

覆盖率反馈循环与时间预算翻倍

覆盖率反馈循环是整个流水线的核心。每一轮迭代中,代理对每个harness运行一段有预算的AFL,然后将队列重放到 .cov二进制上获取真实覆盖率报告,读取未覆盖分支列表,并据此选择动作:添加新种子以到达未覆盖分支、编辑harness源码以调用额外API、用guard正在比较的魔数常量自动扩充AFL字典,或者在该缺口属于冷门错误路径、不值得追的vendor代码时直接跳过。

时间预算每轮翻倍:30 秒 → 60 秒 → 120 秒 → 240 秒 → 480 秒 → 960 秒(约 32 分钟/目标)。设计意图是早期用便宜的短轮次摘取低垂果实,后期用更长的轮次突破难啃的guard。停止条件采用平台期检测:当连续两轮迭代的行覆盖率绝对增幅都低于可配置阈值(默认 1%)时,循环判定收益递减并继续下一个目标,避免代理耗费数小时只为了挤出最后零点几个百分点。

结构化输入处理:四类机制

AFL默认的字节级变异器处理二进制格式效果不错,但面对结构化、基于文本的输入比较吃力,通常的解决办法是为每种格式手写自定义mutator。GitHub的这个流水线内置了四种互补机制来生成结构化输入。

第一,针对已识别格式(JSON、XML、regex、PNG、带长度前缀的二进制TLV)提供预置AFL字典和LLVMFuzzerCustomMutator C文件。JSON mutator做token拼接和括号平衡复制;XML mutator了解标签、实体和billion-laughs token;regex mutator携带真实的ReDoS模式。每个mutator把一半变异交还给AFL默认字节变异器,以保留引擎的随机性。

第二,对于无法识别的格式,流水线扫描目标自身的 .c/.h文件,提取字符串字面量和 32 位数字常量(来自 #define、case和enum),过滤噪声后用作拼接token。第三,同一组源码token会在第一轮迭代前以AFL经典字典形式输出,数字常量同时包含两种字节序,之后每次覆盖率步骤还会检查未覆盖行附近的guard(strncmp、memcmp、case 0xN、== 'X'),把新token追加进字典。第四,智能mutator还可以从语料目录加载文件,把随机子区域拼接到输入中。

跨迭代保留语料,崩溃分类与报告

为避免每次运行都从原始种子重新发现相同路径,每个harness拥有一个稳定的语料目录 /corpus/harness_/,在迭代之间和整个campaign之间持续存在。每轮结束时AFL的队列被合并进该目录,并通过afl-cmin限制大小,因此上周campaign中找到的输入可以带入本次运行。

模糊测试循环结束后自动运行三个阶段。首先,每个崩溃用afl-tmin最小化,在ASan下重放以捕获堆栈,并通过栈顶哈希去重(对栈顶规范化帧做哈希,剥离模板、内联命名空间和LTO后缀,使语义相同的崩溃合并)。其次,已知崩溃会在当前二进制上重放,查看上游修复是否已解决它们。第三,代理读取harness源码和崩溃函数,沿调用链回溯到公共API,为每个崩溃撰写markdown报告。

报告会给出一个判定:vulnerability、library_hardening、harness_bug、OOM、timeout、assertion_failure或duplicate。区分真实漏洞(可通过公共API触达并利用)与harness_bug(问题出在自己的harness而非库中),正是过去需要人工追踪代码做出的判断。每份报告包含带file:line引用的根因分析、可达性论证、可利用性评估、以unified diff形式给出的建议修复和回归测试草图。GitHub表示,建议补丁被标记为需要复核,因为代理分析受限于模型对目标代码的理解,确实会出错;判定结果应被视为给人准备的、整理得很好的起点,而不是最终结论。

实时仪表盘与作者的建议

流水线会把所有内容发布到一个实时HTML仪表盘上,campaign启动后该仪表盘会在后台自动启动,监听端口 8765。在Codespace中该端口会自动转发,用户可以在浏览器中实时查看campaign进度。页面展示按harness的运行脉冲、带内联迷你图的覆盖率趋势表、崩溃热力图和迭代时间线。

GitHub的作者在结论中表示,这个项目的动机来自安全研究员熟知的限制:模糊测试有效,但没有人工关注就无法扩展,而人工注意力正是瓶颈。Fuzzing Taskflow把重复性工作(编写harness、读取覆盖率、追缺口、分类崩溃)交给LLM代理,同时保持代理判断力与执行工具的清晰分离。文章建议C/C++项目维护者尝试:从未做过模糊测试的项目可以借此快速起步;做过模糊测试的项目可能通过提升覆盖率发现新bug。源代码开源,遇到问题可以提issue,也欢迎贡献。

信息来源

GitHub官方博客原始来源