Deterministic protocol fuzzing core with native TCP/UDP execution and portable JSONL replay
moon install GuoXBQ-Q/moon-boofuzz/cmd/boofuzz # 主 CLI,装到 ~/.moon/bin
moon install GuoXBQ-Q/moon-boofuzz/cmd/httpd # 可选:自带 HTTP fuzz 靶子{
"schema_version": 1,
"requests": [{
"name": "request",
"children": [
{"type": "text", "name": "method", "value": "GET", "fuzzable": false},
{"type": "static", "name": "sp1", "value_hex": "20"},
{"type": "text", "name": "uri", "value": "/index.html", "fuzzable": true},
{"type": "static", "name": "sp2", "value_hex": "20"},
{"type": "text", "name": "version", "value": "HTTP/1.1", "fuzzable": false},
{"type": "static", "name": "crlf0", "value_hex": "0d0a"},
{"type": "static", "name": "name_0", "value_hex": "486f73743a20"},
{"type": "text", "name": "hdr_0", "value": "127.0.0.1:9000", "fuzzable": false},
{"type": "static", "name": "crlf1", "value_hex": "0d0a"},
{"type": "static", "name": "name_1", "value_hex": "557365722d4167656e743a20"},
{"type": "text", "name": "hdr_1", "value": "moon-boofuzz", "fuzzable": false},
{"type": "static", "name": "crlf2", "value_hex": "0d0a"},
{"type": "static", "name": "name_2", "value_hex": "436f6e6e656374696f6e3a20"},
{"type": "text", "name": "hdr_2", "value": "close", "fuzzable": false},
{"type": "static", "name": "crlf3", "value_hex": "0d0a"},
{"type": "static", "name": "head_end", "value_hex": "0d0a"},
{"type": "text", "name": "body", "value": "", "fuzzable": false}
]
}],
"execution": {
"transport": "tcp",
"endpoint": {"host": "127.0.0.1", "port": 9000, "receive_timeout_ms": 500},
"policies": {"request": {"kind": "until", "delimiter_hex": "0d0a0d0a"}}
}
}# 终端 1:启动靶子
httpd --port 9000
# httpd listening on 127.0.0.1:9000
# 终端 2:执行 fuzz
boofuzz run http.json --output cases.jsonlboofuzz run http.json --output cases.jsonl --target-cmd "httpd --port 9000"Web interface can be found at http://localhost:26000{"kind":"run_summary","executed":1954,"state":"exhausted","records":"cases.jsonl","database":"boofuzz-results/run-<UTC时间戳>.db"}boofuzz report cases.jsonl{"kind":"report","summary":{"total":1954,"outcomes":{"passed":1947,"connection_ignored":7},
"failures":[{"line":240,"case_id":"[\"request\"]/v1:request.uri:239","outcome":"connection_ignored",
"failed_step":0,"detail":"ConnectionIgnored(\"send reset/aborted at step 0\")"}, ...]},"error":null}import {
"GuoXBQ-Q/moon-boofuzz" @boofuzz,
"GuoXBQ-Q/moon-boofuzz/runner",
"GuoXBQ-Q/moon-boofuzz/transport",
}
supported_targets = "native"
pkgtype(kind: "executable")///|
/// 冻结的结构字节:空格、CRLF、头名,永不变异。
fn fixed(bytes : Bytes) -> @boofuzz.Field {
@boofuzz.Field::simple(bytes, [], fuzzable=false)
}
///|
/// 请求行 + Host/Accept/X-Fuzz 头。变异点:动词组(GET/HEAD)、
/// URI 显式候选、两个字符串库字段。
fn http_get() -> @boofuzz.CompiledRequest raise {
@boofuzz.CompiledRequest::compile(
@boofuzz.Block::new("http_get", [
@boofuzz.Leaf("verb", @boofuzz.Field::group([b"GET", b"HEAD"])),
@boofuzz.Leaf("sp1", fixed(b" ")),
@boofuzz.Leaf(
"uri",
@boofuzz.Field::simple(b"/", [b"/echo", b"/headers", b"/nope"]),
),
@boofuzz.Leaf("sp2", fixed(b" ")),
@boofuzz.Leaf("version", fixed(b"HTTP/1.1")),
@boofuzz.Leaf("crlf0", fixed(b"\r\n")),
@boofuzz.Leaf("host", fixed(b"Host: 127.0.0.1:9000\r\n")),
@boofuzz.Leaf("accept_name", fixed(b"Accept: ")),
@boofuzz.Leaf("accept", @boofuzz.Field::text("application/json")),
@boofuzz.Leaf("crlf1", fixed(b"\r\n")),
@boofuzz.Leaf("xfuzz_name", fixed(b"X-Fuzz: ")),
@boofuzz.Leaf("xfuzz", @boofuzz.Field::text("seed")),
@boofuzz.Leaf("crlf2", fixed(b"\r\n")),
@boofuzz.Leaf("head_end", fixed(b"\r\n")),
]),
)
}
///|
fn main raise {
let request = http_get()
let graph = @boofuzz.SessionGraph::new()
graph.add(request)
let paths = graph.paths(targets=["http_get"])
let endpoint = @transport.Endpoint::new(
"127.0.0.1",
9000,
connect_timeout_ms=750,
receive_timeout_ms=750,
)
// 读到 HTTP 头结束为止;每例新建连接。
let policies : Map[String, @runner.ReadPolicy] = Map([
("http_get", @runner.Until(b"\r\n\r\n")),
])
let runner = @runner.Runner::new(
paths,
endpoint,
policies~,
limit=@runner.NO_CASE_LIMIT,
restart_threshold=Some(1),
restart_sleep_ms=0,
)
for ;; {
match runner.next() {
Some(result) =>
println(
"\{result.case.field_path}:\{result.case.mutation_index} -> \{result.outcome.name()}",
)
None => break
}
}
}moon run --target native cmd/main| 命令 | 用途 |
|---|---|
| boofuzz generate DEFINITION.json | JSON 协议定义 → 变异载荷 JSONL,不连接目标 |
| boofuzz run DEFINITION.json --output CASES.jsonl | 逐例执行并保存实际流量;--target-cmd CMD 可让 boofuzz 自己孵化并监视靶子(崩溃记为 fault 并自动重启),--web-port N 控制实时界面端口 |
| boofuzz report CASES.jsonl | 分类计数、失败身份和行号 |
| boofuzz replay CASES.jsonl --id CASE_ID | 按身份重放保存的字节,可用 --host/--port 覆盖目标 |
| boofuzz open FILE | 结果库/JSONL → 本地只读 Web 视图 |
| boofuzz convert | Web 页面:粘贴原始 HTTP 报文 → 自动生成协议定义 JSON |
| 需求 | 文档 |
|---|---|
| CLI 全部选项、示例文件与退出码 | CLI.md |
| 编写 JSON 协议、字段与读取策略 | DEFINITIONS.md |
| 用 Web 页面把 HTTP 报文转成定义 | CONVERT.md |
| 运行本地 HTTP fuzz 靶子(httpd) | HTTPD.md |
| 用 MoonBit 代码编写 fuzz 脚本 | 原生 MoonBit 完整示例、CODE.md、可执行 API 示例、MODEL.md |
| 配置前置路径和执行器 | SESSION.md、RUNNER.md |
| 响应检查、故障通知和恢复回调 | MONITORS.md |
| 理解日志、重放和退出码 | RECORDS.md、REPLAY.md、REPORT.md |
| 支持范围、兼容边界、验收与许可 | UPSTREAM.md、ACCEPTANCE.md |
pub struct CaseStream {
request : CompiledRequest
limit : Int
skip : Int
ordinal : Int
emitted : Int
state : EnumerationState
stack : Array[StreamFrame]
combinatorial : Bool
units : Array[StreamUnit]
max_depth : Int?
depth : Int
depth_emitted : Int
frames : Array[CombFrame]
vars : Map[String, Bytes]?
}pub struct ChecksumSpec {
target : String
endian : Endian
mutations : Array[Bytes]
algorithm : ChecksumAlgorithm
}fn CompiledRequest::cases(self : CompiledRequest, start? : Int, limit? : Int, vars? : Map[String, Bytes]?) -> CaseStream raise ModelErrorfn CompiledRequest::cases_with_variables(self : CompiledRequest, vars : Map[String, Bytes]?, start? : Int, limit? : Int) -> CaseStream raise ModelErrorfn CompiledRequest::combinatorial_cases(self : CompiledRequest, start? : Int, limit? : Int, max_depth? : Int) -> CaseStream raise ModelErrorfn CompiledRequest::combinatorial_cases_with_variables(self : CompiledRequest, vars : Map[String, Bytes]?, start? : Int, limit? : Int, max_depth? : Int) -> CaseStream raise ModelErrorfn CompiledRequest::from_request(name : String, request : Request, max_bytes? : Int) -> CompiledRequest raise ModelErrorfn CompiledRequest::raw_prefix_before(self : CompiledRequest, path : String) -> Int64 raise ModelErrorfn CompiledRequest::render_case(self : CompiledRequest, parts : Array[(String, Int)], vars : Map[String, Bytes]?) -> Bytes raise ModelErrorfn CompiledRequest::render_with(self : CompiledRequest, vars : Map[String, Bytes]?) -> Bytes raise ModelErrorpub struct Field {
value : Bytes
count : Int
candidate : (Int) -> Bytes
candidate_length : (Int) -> Int64
}fn Field::binary(value : Bytes, size? : Int, max_len? : Int, padding? : Bytes, fuzzable? : Bool, fuzz_values? : Array[Bytes]) -> Field raise ModelErrorfn Field::float(default_value? : Double, s_format? : String, f_min? : Double, f_max? : Double, max_mutations? : Int, seed? : Int64, encode_as_ieee_754? : Bool, endian? : Endian, fuzzable? : Bool, fuzz_values? : Array[Bytes]) -> Field raise ModelErrorfn Field::from_lines(default_value? : Bytes, lines? : Array[Bytes], max_len? : Int, fuzzable? : Bool, fuzz_values? : Array[Bytes]) -> Field raise ModelErrorfn Field::group(values : Array[Bytes], default_value? : Bytes, fuzzable? : Bool, fuzz_values? : Array[Bytes]) -> Field raise ModelErrorfn Field::integer(value : UInt64, width? : Int, endian? : Endian, fuzzable? : Bool, fuzz_values? : Array[Bytes]) -> Field raise ModelErrorfn Field::random_data(default_value? : Bytes, min_length? : Int, max_length? : Int, max_mutations? : Int, step? : Int, fuzzable? : Bool, fuzz_values? : Array[Bytes]) -> Field raise ModelErrorpub(all) enum Node {
Leaf(String, Field)
Nested(Block)
Repeat(String, String, Int, Int, Int, String?)
Sized(String, SizeSpec)
Checksummed(String, ChecksumSpec)
Mirrored(String, String)
Dynamic(String, String, Field)
}fn Node::checksum(name : String, target : String, endian? : Endian, mutations? : Array[UInt], fuzzable? : Bool, algorithm? : ChecksumAlgorithm) -> Node raise ModelErrorpub struct SessionGraph {
requests : Array[CompiledRequest]
edges : Array[Array[Int]]
names : Map[String, Int]
edge_callbacks : Map[(Int, Int), (StepContext) -> Bytes?]
}fn SessionGraph::connect(self : SessionGraph, from : String, to : String, callback? : (StepContext) -> Bytes?) -> Unit raise ModelErrorfn SessionGraph::paths(self : SessionGraph, targets? : Array[String], max_paths? : Int) -> Array[SessionPath] raise ModelErrorpub struct SessionPath {
requests : Array[CompiledRequest]
transitions : Array[(StepContext) -> Bytes??]
}fn SessionPath::cases(self : SessionPath, start? : Int, limit? : Int, variables? : Map[String, Bytes]?) -> CaseStream raise ModelErrorfn SessionPath::cases_with_variables(self : SessionPath, vars : Map[String, Bytes]?, start? : Int, limit? : Int) -> CaseStream raise ModelErrorfn SessionPath::combinatorial_cases_with_variables(self : SessionPath, vars : Map[String, Bytes]?, start? : Int, limit? : Int, max_depth? : Int) -> CaseStream raise ModelErrorfn adler32(bytes : Bytes) -> UIntfn crc32(bytes : Bytes) -> UIntfn crc32c(bytes : Bytes) -> UIntfn ipv4_checksum(header : Bytes) -> UIntfn udp_checksum(source : Bytes, destination : Bytes, udp_bytes : Bytes) -> UIntInstall
Download zipDeterministic protocol fuzzing core with native TCP/UDP execution and portable JSONL replay