FirmAgent: Fuzzing + LLM Agents for IoT Firmware Vulnerability Discovery (NDSS 2026)
FirmAgent is an academic research toolkit (NDSS 2026) for finding vulnerabilities in re-hosted IoT firmware web services by combining IDA-based pre-fuzzing (CFG/sink distance scoring), QEMU instrumented tracing, CFG-guided API fuzzing, and an optional LLM agent (LLMATaint.py) that consumes runtime taint traces to reason about and produce PoCs for bug classes like command injection. The pipeline is explicit about scope: it is meant for authorized, owned or re-hosted firmware (e.g. Greenhouse-style containers), and stitches together sink-scope extraction, feedback-driven fuzzing, and request-to-trace attribution before handing traces to an LLM for taint-to-PoC analysis.
What it is
FirmAgent's repo README, giving the exact command sequence to run the pipeline the summary describes. The summary tells you the pipeline exists; the README tells you how to invoke each stage, in what order, and what artifacts each produces. Requires Python 3.8+, IDA Pro (idat/idat64), Docker with a Greenhouse/re-hosting image, an instrumented QEMU binary with a shared host trace directory, and an LLM endpoint only for API extraction and LLMATaint.py.
What to do
Configure via environment (do not commit keys):
export IDAT_BIN=/path/to/idat64
export Private_API_KEY=...
export LLM_BASE_URL=https://your-endpoint
export LLM_MODEL=your-modelStage 1 — pre-fuzzing writes sink_scope_addr.txt, sink_cfg.json, sink_distance_scores.json/.csv, and IDA evidence next to the binary:
Pre_Fuzzing/run.sh /path/to/httpdAPI/parameter extraction is a separate, commented-out LLM step — run it explicitly to produce Pre_fuzzing.json (must contain api_endpoints and para; may add a mutation dictionary):
python3 Pre_Fuzzing/llm_extract_api_params.py --input /path/to/export-for-ai-httpd --output /path/to/Pre_fuzzing.jsonStage 2 — start the instrumented container with tracing restricted to the sink scope:
FuzzingRecord/rehost_session.sh start --container <container> \
--dfilter "$(python3 FuzzingRecord/make_dfilter.py /path/to/sink_scope_addr.txt)"This sets QEMU_DFILTER, QEMU_LOG=exec,nochain, QEMU_LOG_FILENAME=/scratch/qemu_trace.log. Stop with rehost_session.sh stop --container <container> — this resets the trace log.
The fuzzer has two modes that the summary collapses into one. Legacy (no feedback) needs only --json-file. CFG-guided feedback mode additionally requires both --distance-file and --qemu-log:
python3 FuzzingRecord/Fuzzer.py --json-file /path/to/Pre_fuzzing.json \
--distance-file /path/to/sink_distance_scores.json \
--qemu-log /host/shared/qemu_trace.log --host <container-ip> \
--feedback-settle 0.3 --iterations 1000 --random-seed 1 --tag-mode uniqueFeedback mode fuses dictionary mutations with runtime block/edge coverage and shortest distance to a sink. Outputs result.json, result_stream.ndjson, fuzzing_summary.json.
Stage 3 — for exact request-to-trace attribution, run the sidecar before fuzzing with --tag-mode unique, then build Source.json:
python3 FuzzingRecord/taint_monitor.py --stream /path/to/result_stream.ndjson \
--qemu-log /host/shared/qemu_trace.log --out /path/to/trace_windows.jsonl --settle 0.3
python3 FuzzingRecord/build_source_json.py --qemu-log /host/shared/qemu_trace.log \
--windows /path/to/trace_windows.jsonl --result-stream /path/to/result_stream.ndjson \
--output /path/to/Source.jsonStage 4 — LLM taint-to-PoC. Source.json (or source.json) must sit in the same directory as the target binary; Indirect_call.json/indirect_data.json may be supplied when available. The -t flag names the bug class (ci/command_injection), -m selects the model:
python LLMATaint.py -b ./bin/httpd -p False -t ci -o ./results/ASUS -m R1_officialCaveats
- Ordering matters: the taint sidecar must run before fuzzing and depends on unique tags for attribution.
- If the trace file is missing, rotated, or truncated, that iteration is marked invalid and the loop continues — so a misconfigured shared directory silently degrades feedback mode to noise.
- README states scope explicitly: only firmware/services you own or are explicitly authorized to test.
For citation: NDSS 2026, doi 10.14722/ndss.2026.231943.
Technologies named here
- IoT firmware httpd binaries
- FirmAgent
- IDA Pro
- Pre_Fuzzing/run.sh
- Pre_Fuzzing/llm_extract_api_params.py
- Docker / Greenhouse re-hosting image
- Instrumented QEMU
- FuzzingRecord/rehost_session.sh
- FuzzingRecord/make_dfilter.py
- FuzzingRecord/Fuzzer.py
- FuzzingRecord/taint_monitor.py
- FuzzingRecord/build_source_json.py
- LLMATaint.py
- LLM endpoint
- QEMU
- Docker
- Python
- LLM
Techniques named here
- IDA-based pre-fuzzing
- QEMU dynamic instrumentation / tracing
- CFG-guided feedback fuzzing
- Legacy taint traversal fuzzing
- Request-to-trace attribution
- LLM-assisted taint analysis / PoC generation
- Command injection
Related in Hardware & RF

HAETAE on a Diet: Less Stack, Less Time on Low-End Microcontrollers
geo-tp/ESP32-Bit-Pirate
