From firmware blob to verified exploit chain — deterministic evidence at every step.
Languages: English (this file), 한국어 README.ko.md
Deterministic firmware analysis engine that produces hash-anchored evidence artifacts — from unpacking through vulnerability discovery to full-chain exploit verification.
Every exploit starts with evidence. SCOUT produces the evidence chain.
Most firmware analysis tools stop at "here's a list of potential vulnerabilities." SCOUT is designed around a different premise: the end goal is a verified, reproducible exploit chain — and every stage exists to build toward that.
Firmware blob → Structure → Attack surface → Vulnerability → Exploit primitive → PoC → Verified chain
SCOUT doesn't guess. Each stage produces hash-anchored artifacts in a run_dir, and no claim advances without traceable evidence. The engine is deterministic by default — LLM judgment and dynamic validation are layered on top by an orchestrator (Terminator), never baked into the evidence chain itself.
- Evidence-first — No finding exists without a file path, offset, hash, and rationale anchored to artifacts
- Fail-open stages, fail-closed governance — Individual stages degrade gracefully (partial results over crashes); final promotion gates reject anything without complete evidence
- Full-chain or nothing — The pipeline doesn't stop at "potential command injection." It traces source→sink→primitive→chain→PoC→verification, marking exactly where the chain breaks
- Deterministic engine, non-deterministic judgment — SCOUT produces reproducible artifacts; LLM tribunal and exploit generation happen in the orchestrator layer with full audit trails
--rootfs /path/to/extracted_rootfsis now supported onanalyzeandanalyze-8mb.- This bypasses weak/partial unpacking for multi-layer firmware layouts (e.g., nested tar/gzip/bzip2/cpio/ext images).
- Extraction now has a built-in quality gate.
- Low extraction coverage is marked as insufficient (
partial) with explicit operator guidance.
- Low extraction coverage is marked as insufficient (
- Inventory now emits deeper artifacts:
stages/inventory/binary_analysis.jsoninventory.json.quality+inventory.json.binary_analysis_summary- config-driven service hints (
services,inetd,xinetd, web server configs).
firmware_profilenow cross-checks OS/arch hints from discovered ELF binaries (arch_guess,elf_hints) to reduce RTOS false positives.firmware_handoff.jsonis now auto-generated by SCOUT (analyzeandstages) with run policy + artifact bundles../scoutis now the preferred launcher (shorter thanPYTHONPATH=... python3 -m aiedge ...).- Dynamic validation and exploit auto-generation are now linked through exploit evidence bundle flow:
stages/dynamic_validation/*.jsonrecords validation outcomesstages/exploit_autopoc/*+exploits/chain_*receivesverifier_refs/verification_refswhen evidence exists- This enables single-screen operator triage of D/E/V confidence.
- Communication/runtime modeling is now explicit:
stages/graph/communication_graph.jsonstages/graph/communication_graph.cypherstages/graph/communication_matrix.json/.csv
- TUI / viewer now provide dedicated panels for:
- threat model (
t), runtime model (m), assets/protocols (a) - dynamic + exploit + chain evidence badges (
D,E,V,S) and reason/truncation hints
- threat model (
AIEDGE_PRIV_RUNNERaccepts relative paths and now resolves in multiple safe locations (includingrun_dirand parent dirs).
┌──────────────────────────────────────────────────────────────────────────┐
│ SCOUT (Evidence Engine) │
│ │
│ Firmware ──► Unpack ──► Profile ──► Inventory ──► Surface ──► Findings │
│ │
│ StageFactory stages: stage.json (sha256 manifest) │
│ Findings step: run_findings() writes structured artifacts │
│ All paths run-relative, all hashes recorded │
│ │
├──────────────────────────────────────────────────────────────────────────┤
│ Handoff (JSON contract) │
├──────────────────────────────────────────────────────────────────────────┤
│ Terminator (Orchestrator) │
│ │
│ Tribunal ──► Validator ──► Exploit Dev ──► Verified Chain │
│ (LLM judge) (emulation) (lab-gated) (confirmed only │
│ with dynamic evidence) │
└──────────────────────────────────────────────────────────────────────────┘
Separation of concerns:
| Layer | Role | Deterministic? |
|---|---|---|
| SCOUT | Evidence production (extraction, profiling, inventory, surfaces, findings) | Yes |
| Handoff | JSON contract between engine and orchestrator (firmware_handoff.json) |
Yes |
| Terminator | LLM tribunal, dynamic validation, exploit development, report promotion | No (auditable) |
flowchart TD
F["Firmware Input<br/>(bin/tar/img)"] --> S0["Stage 0: Ingest<br/>manifest.json + input copy"]
S0 --> S1["Stage 1: Extraction<br/>binwalk/unblob → rootfs + kernel"]
S1 --> S1H{"Extracted<br/>filesystem?"}
S1H -->|Yes| S15L["Stage 1.5: Profile<br/>Linux FS path"]
S1H -->|No| S15R["Stage 1.5: Profile<br/>RTOS / monolithic path"]
S1H -->|Encrypted| S15E["Stage 1.5: Profile<br/>flag: needs key extraction"]
S15L --> S2L["Stage 2: Inventory<br/>file tree + ELF/scripts/configs"]
S15R --> S2R["Stage 2: Inventory<br/>binary-only (strings/sections/prologue)"]
S15E --> S2E["Stage 2: Inventory<br/>minimal (entropy + headers only)"]
S2L --> S3["Stage 3: Attack Surface<br/>source→sink graph"]
S2R --> S3
S2E --> S3
S3 --> S4["Stage 4: Findings<br/>2-layer: pattern_scan + binary_strings"]
S4 --> S4G["Review Gates<br/>critic/triager scoring"]
S4G --> HO["═══ HANDOFF ═══<br/>firmware_handoff.json"]
HO --> T1["Tribunal<br/>(Analyst → Critic → Arbiter)"]
T1 --> T2{"Verdict?"}
T2 -->|"≥0.8 + evidence"| V["Validator<br/>(QEMU/container sandbox)"]
T2 -->|"<0.5 or rebutted"| DISMISS["Dismissed"]
T2 -->|"0.5-0.8"| CAND["Candidate<br/>(needs investigation)"]
V --> V1{"Dynamic<br/>evidence?"}
V1 -->|"crash/trace/response"| CONF["✅ CONFIRMED"]
V1 -->|"infeasible/failed"| HCS["⚠️ HIGH_CONFIDENCE_STATIC"]
CONF --> E["Exploit Chain Dev<br/>(lab-gated, authorized only)"]
E --> E1["Primitive Assembly<br/>leak → write → control"]
E1 --> E2["PoC Script<br/>(pwntools/requests)"]
E2 --> E3["Local Verification<br/>(3x reproduce)"]
E3 --> CHAIN["🔗 VERIFIED CHAIN<br/>Full exploit with evidence"]
style CONF fill:#22c55e,color:#fff
style HCS fill:#f59e0b,color:#fff
style DISMISS fill:#6b7280,color:#fff
style CHAIN fill:#8b5cf6,color:#fff
Creates the immutable run directory and locks all inputs.
run_dir/
├── manifest.json # sha256, size, case_id, time_budget, egress policy
└── input/firmware.bin # immutable copy of input
No analysis happens here — just identity, policy, and directory reservation.
Maximally extracts filesystem, kernel, and partition fragments from the firmware blob.
stages/extraction/
├── stage.json # sha256 of all artifacts
├── binwalk.log # full extraction log
└── _firmware.bin.extracted/
├── squashfs-root/ # extracted rootfs (if Linux)
├── *.uImage # kernel images
└── ... # partition fragments
Current extraction modes:
binwalkbest-effort extraction (default)--rootfs <DIR>direct ingest of operator-supplied extracted filesystem- Recursive nested extraction for common wrapped payloads:
- UBI / SquashFS
- tar / gzip / bzip2 / cpio
- ext filesystem images (via
debugfswhen available)
Quality behavior: extraction emits quality_gate with minimum expected file count and marks sparse output as insufficient (partial).
AI role: None (rule-based). Custom format detection is a future extension.
Classifies the firmware and decides the pipeline branch. This is the routing decision for everything downstream.
stages/firmware_profile/
├── stage.json
└── firmware_profile.json
firmware_profile.json schema:
{
"schema_version": 1,
"firmware_id": "sha256:e3d3fe...",
"os_type_guess": "linux_fs | rtos_monolithic | unextractable_or_unknown",
"arch_guess": "x86_64-64 | mips-32 | ... | null",
"elf_hints": {
"elf_count": 56,
"arch_counts": {
"x86_64-64": 56
},
"sample_paths": [
"stages/extraction/_firmware.bin.extracted/rootfs/usr/bin/httpd"
]
},
"sdk_hints": ["busybox", "openwrt"],
"emulation_feasibility": "high | medium | low",
"branch_plan": {
"inventory_mode": "filesystem | binary_only",
"why": "routing rationale"
},
"evidence_refs": [...],
"limitations": [...]
}Branching logic:
| Profile Result | Inventory Mode | Surface Extraction | Dynamic Validation |
|---|---|---|---|
linux_fs |
Full file tree walk | Yes (init/services/web/CGI) | Viable (QEMU/FirmAE) |
rtos_monolithic |
Binary-only (strings/sections/prologues) | Limited (string-inferred) | Limited (Unicorn/partial) |
unextractable_or_unknown |
Minimal (entropy + headers) | No | No |
Catalogs everything in the extracted firmware. Never crashes — partial results are always better than no results.
stages/inventory/
├── stage.json
├── inventory.json # file/binary catalog with coverage metrics
├── string_hits.json # interesting string patterns across all binaries
└── binary_analysis.json # risky symbol/arch summary for discovered binaries
inventory.json key fields:
{
"status": "ok | partial",
"quality": {
"status": "sufficient | insufficient",
"files_seen": 4521,
"binaries_seen": 731,
"min_files": 50,
"min_binaries": 5,
"reasons": []
},
"coverage_metrics": {
"roots_considered": 3,
"roots_scanned": 2,
"files_seen": 4521,
"binaries_seen": 731,
"configs_seen": 1042,
"string_hits_seen": 188,
"skipped_dirs": 2,
"skipped_files": 14
},
"binary_analysis_summary": {
"binaries_scanned": 400,
"elf_binaries": 129,
"risky_binaries": 17,
"risky_symbol_hits": 49,
"arch_counts": {"x86_64-64": 120, "x86-32": 9}
},
"errors": [
{
"path": "etc/ssh/ssh_config.d/etc",
"op": "listdir",
"errno": 13,
"error": "Permission denied (sanitized)"
}
],
"artifacts": {
"string_hits": "stages/inventory/string_hits.json",
"binary_analysis": "stages/inventory/binary_analysis.json"
}
}Robustness guarantees:
- Permission-denied directories → skip and record in
errors[], continue scanning - Symlink loops → detect via
os.lstat(), skip, record - Dangling symlinks → record as metadata, don't follow
- Corrupt filenames → hex-escape, record
inventory.jsonis always written, even if completely empty (withstatus: "partial"and reason)- Sparse coverage is explicitly marked in
quality.status(insufficient) instead of silently treated as full success
Identifies the entry points an attacker can reach and traces them toward dangerous sinks.
stages/surfaces/
├── surfaces.json # network services, web endpoints, CLI interfaces
├── endpoints.json # specific input handlers (CGI, REST, SOAP, MQTT, ...)
└── source_sink_graph.json # source→processing→sink candidate paths
Source categories:
- Network: HTTP/HTTPS, MQTT, CoAP, UPnP/SSDP, Telnet, SSH, custom TCP/UDP
- Local: CLI, NVRAM reads, environment variables, config file parsing, IPC/Unix sockets
- Hardware: UART, JTAG, SPI/I2C (noted for physical access scenarios)
Sink categories (exploit-relevant):
- Command execution:
system(),popen(),execve(), shell invocations - Memory corruption:
strcpy(),sprintf(),memcpy()without bounds, heap ops - File operations:
open()with user-controlled paths, symlink races - Authentication: hardcoded credentials, bypass conditions, weak token generation
- Crypto: weak algorithms, key reuse, nonce mismanagement
The graph is the exploit planner's input — each path from source to sink is a potential exploit chain candidate.
Two-layer findings with deterministic scoring, designed for tribunal consumption.
stages/findings/
├── pattern_scan.json # high-level findings (AI-consumable)
├── binary_strings_hits.json # low-level string evidence across binaries
├── chains.json # kill-chain hypotheses (source→sink→primitive→impact)
├── review_gates.json # critic/triager scoring per finding
├── known_disclosures.json # CVE matches with NVD citations
└── poc_skeletons/
└── README.txt # safe placeholders (no weaponized content)
Finding structure (each entry in pattern_scan.json):
{
"finding_id": "F-037",
"title": "Command injection via HTTP POST parameter in lighttpd CGI handler",
"vuln_class": "CWE-78",
"severity_estimate": "critical",
"source": {
"type": "http_post",
"binary": "usr/sbin/lighttpd",
"handler": "cgi_handler",
"parameter": "cmd"
},
"sink": {
"function": "system",
"binary": "usr/lib/cgi-bin/admin.cgi",
"offset": "0x401890"
},
"taint_path": ["recv", "parse_post_params", "build_command", "system"],
"evidence_refs": [
"stages/inventory/inventory.json:entries[142]",
"stages/findings/binary_strings_hits.json:hits[37]"
],
"chain_potential": {
"primitive": "arbitrary_command_execution",
"auth_required": false,
"network_reachable": true,
"exploit_complexity": "low"
},
"rationale": "...",
"limitations": ["static analysis only, sink reachability not dynamically confirmed"]
}chains.json — Kill-chain hypotheses:
Each chain maps a complete attack path from initial access to impact:
{
"chain_id": "KC-003",
"title": "Unauthenticated RCE via CGI command injection",
"steps": [
{"step": 1, "action": "HTTP POST to /cgi-bin/admin.cgi", "finding_ref": "F-037"},
{"step": 2, "action": "Inject shell command via 'cmd' parameter", "finding_ref": "F-037"},
{"step": 3, "action": "Achieve root shell (CGI runs as root)", "finding_ref": "F-012"}
],
"preconditions": ["network access to management interface", "no authentication required"],
"impact": "full device compromise (root shell)",
"confidence": "high_confidence_static",
"exploit_feasibility": "high"
}Iron Rule: No Evidence, No Confirmed.
┌─────────────┬──────────────────────────────────────┬────────────────────┐
│ Level │ Requirements │ Appears In │
├─────────────┼──────────────────────────────────────┼────────────────────┤
│ dismissed │ Critic rebuttal strong OR │ Appendix only │
│ │ tribunal confidence < 0.5 │ │
├─────────────┼──────────────────────────────────────┼────────────────────┤
│ candidate │ Tribunal confidence 0.5 - 0.8 │ Report (flagged) │
│ │ Evidence exists but chain incomplete │ │
├─────────────┼──────────────────────────────────────┼────────────────────┤
│ high_conf │ Tribunal confidence ≥ 0.8 │ Report (prominent) │
│ _static │ Static evidence strong │ │
│ │ No dynamic validation available │ │
├─────────────┼──────────────────────────────────────┼────────────────────┤
│ confirmed │ Tribunal confidence ≥ 0.8 AND │ Report (top) │
│ │ ≥1 dynamic validation artifact: │ │
│ │ • crash trace │ │
│ │ • execution log with controlled I/O │ │
│ │ • network response showing code path │ │
├─────────────┼──────────────────────────────────────┼────────────────────┤
│ verified │ Confirmed AND full PoC reproduces 3x │ Exploit report │
│ _chain │ in sandboxed environment │ │
│ │ Complete: access → primitive → impact │ │
└─────────────┴──────────────────────────────────────┴────────────────────┘
verified_chain is the end goal. Everything before it is a step on the path.
SCOUT produces evidence. Terminator consumes it, judges it, validates it, and builds exploits.
SCOUT run_dir/ Terminator report_dir/
├── manifest.json ├── tribunal/
├── stages/ │ ├── analyst_candidates.jsonl
│ ├── extraction/ │ ├── critic_reviews.jsonl
│ ├── firmware_profile/ │ ├── judged_findings.jsonl
│ ├── inventory/ │ └── decision_trace.jsonl
│ ├── surfaces/ ├── validation/
│ └── findings/ │ └── emulation_results/
│ ├── pattern_scan.json ├── exploits/
│ ├── chains.json │ ├── chain_KC-003/
│ └── known_disclosures.json │ │ ├── exploit.py
│ │ │ ├── local_test_log.txt (3x)
│ │ │ └── evidence_bundle.json
│ └── report/
│ ├── report.json
│ └── audit_trail.json
firmware_handoff.json (SCOUT-generated contract)
├── schema_version: 1
├── generated_at: "2026-02-22T14:12:00Z"
├── profile: "analysis | exploit"
├── policy:
│ ├── max_reruns_per_stage
│ ├── max_total_stage_attempts
│ └── max_wallclock_per_run
├── aiedge:
│ ├── run_id
│ ├── run_dir
│ ├── report_json
│ └── report_html
├── bundles[]:
│ ├── id / stage / attempt / status
│ └── artifacts[] (run-relative, existence-checked)
└── exploit_gate (only when profile=exploit)
Terminator agents for firmware pipeline:
| Agent | Role | Reused? |
|---|---|---|
fw_profiler |
Interprets profiling artifacts, suggests analysis strategy | New |
fw_surface |
Deep-dives attack surface using decompiled code | New |
fw_analyst |
Tribunal Analyst — aggressive finding generation | New |
critic |
Tribunal Critic — adversarial rebuttal | Reused from Terminator |
triager_sim |
Tribunal Arbiter — final verdict | Reused from Terminator |
fw_validator |
Dynamic validation via emulation/sandbox | New |
chain |
Exploit chain assembly (leak→write→control) | Reused from Terminator |
verifier |
3x local reproduction of full exploit | Reused from Terminator |
reporter |
Final report with evidence citations | Reused from Terminator |
cd /path/to/SCOUT
# optional short launcher (no PYTHONPATH typing)
./scout --help
# Full deterministic analysis (no LLM, quick profile run)
./scout analyze firmware.bin \
--ack-authorization --no-llm \
--case-id my-analysis \
--stages tooling,extraction,structure,carving,firmware_profile,inventory
# When extraction is known to be weak for your target, provide an already-unpacked rootfs:
./scout analyze firmware.img \
--ack-authorization --no-llm \
--case-id my-analysis \
--rootfs /path/to/extracted/rootfs
# Rerun specific stages on existing run
./scout stages aiedge-runs/<run_id> \
--no-llm \
--stages inventory
# Full analysis profile (default) with evidence-aware LLM flows:
# 1) LLM synthesis (reasoning + attack-chain hypotheses)
# 2) dynamic validation
# 3) exploit_autopoc (LLM-first; deterministic fallback)
./scout analyze firmware.bin \
--ack-authorization \
--case-id my-analysis \
--profile exploit \
--exploit-flag lab \
--exploit-attestation authorized \
--exploit-scope lab-only
# Re-run only evidence generation/execution stages
./scout stages aiedge-runs/<run_id> \
--stages llm_synthesis,dynamic_validation,exploit_autopoc \
--time-budget-s 900
export AIEDGE_LLM_CHAIN_TIMEOUT_S=180
export AIEDGE_LLM_CHAIN_MAX_ATTEMPTS=5
export AIEDGE_AUTOPOC_LLM_TIMEOUT_S=180
export AIEDGE_AUTOPOC_LLM_MAX_ATTEMPTS=4
./scout stages aiedge-runs/<run_id> --stages llm_synthesis,exploit_autopoc
# If dynamic validation is blocked by container sudo policy (e.g. no-new-privileges),
# set a privileged command prefix once (flagless) and rerun dynamic+autopoc:
export AIEDGE_PRIV_RUNNER='./scripts/priv-run'
./scout stages aiedge-runs/<run_id> --stages dynamic_validation,exploit_autopoc
# Port probing defaults (runtime validation): scan priority + top-k first, then stop by default
# Set AIEDGE_PORTSCAN_FULL_RANGE=1 to continue full-range scan when you need complete coverage.
export AIEDGE_PORTSCAN_TOP_K=1000
export AIEDGE_PORTSCAN_START=1
export AIEDGE_PORTSCAN_END=65535
export AIEDGE_PORTSCAN_WORKERS=128
export AIEDGE_PORTSCAN_BUDGET_S=120
export AIEDGE_PORTSCAN_FULL_RANGE=0cd /path/to/Terminator
# Full firmware pipeline: SCOUT analysis → tribunal → validation → exploit dev
./terminator.sh firmware /path/to/firmware.bin
# Monitor
./terminator.sh status
./terminator.sh logs# Digest-first analyst entrypoint (must exist and verify)
test -f aiedge-runs/<run_id>/report/analyst_digest.json
test -f aiedge-runs/<run_id>/report/analyst_digest.md
python3 scripts/verify_analyst_digest.py --run-dir aiedge-runs/<run_id>
# Verified-chain hard gates (all must return [OK] for VERIFIED)
python3 scripts/verify_run_dir_evidence_only.py --run-dir aiedge-runs/<run_id>
python3 scripts/verify_network_isolation.py --run-dir aiedge-runs/<run_id>
python3 scripts/verify_exploit_meaningfulness.py --run-dir aiedge-runs/<run_id>
python3 scripts/verify_verified_chain.py --run-dir aiedge-runs/<run_id>
# SCOUT evidence integrity
python3 scripts/verify_aiedge_analyst_report.py --run-dir aiedge-runs/<run_id>
# Terminator tribunal artifacts
python3 bridge/validate_tribunal_artifacts.py --report-dir reports/<report_id>
# Confirmed policy enforcement
python3 bridge/validate_confirmed_policy.py --report-dir reports/<report_id># Serve a run report directory and print viewer URL
./scout serve aiedge-runs/<run_id>
# Example (automation): bind ephemeral port, serve one request, then exit
./scout serve aiedge-runs/<run_id> --port 0 --once# One-shot terminal dashboard
./scout tui aiedge-runs/<run_id>
# Live-refresh mode (Ctrl+C to exit)
./scout tui aiedge-runs/<run_id> --watch --interval-s 2
./scout tw aiedge-runs/<run_id> -t 2 -n 20 # short alias
# Interactive mode (j/k/arrow navigation, q to quit)
./scout tui aiedge-runs/<run_id> --interactive --limit 30
./scout ti aiedge-runs/<run_id> # short alias
./scout to aiedge-runs/<run_id> # one-shotInteractive keys: j/k or ↑/↓ move, g/G top/bottom, c candidate panel,
t threat panel, m runtime-model panel, a assets/protocol panel, r refresh, q quit.
NEO4J_PASS=<pass> ./scripts/neo4j_comm_import.sh aiedge-runs/<run_id>This applies schema + data + saved query bundle (neo4j-comm-v2, including Query 0: Top D+E+V / D+E chains).
Analyst workflow cockpit (additive, offline-safe):
- Open
<run_dir>/report/viewer.html. - Offline-safe behavior:
viewer.htmlembeds bootstrap JSON and falls back whenfile://fetch is restricted (#file-warning). - Cockpit answers at a glance:
- Executive verdict (state/reason_codes/next_actions)
- Attack surface scale (scope/context counts)
- Verification status (gate applicability/presence)
- Evidence shortcuts (digest/overview/report navigation)
- Trust boundary: cockpit output is convenience only; verifiers remain authoritative:
python3 scripts/verify_analyst_digest.py --run-dir <run_dir>python3 scripts/verify_aiedge_analyst_report.py --run-dir <run_dir>
Every analysis produces a self-contained, reproducible run_dir:
aiedge-runs/<timestamp>_<sha256-prefix>/
├── manifest.json # immutable: input identity + policy
├── firmware_handoff.json # SCOUT handoff contract (auto-generated)
├── input/
│ └── firmware.bin # immutable copy
├── stages/
│ ├── tooling/
│ │ └── stage.json # tool versions + availability
│ ├── extraction/
│ │ ├── stage.json
│ │ ├── binwalk.log
│ │ └── _firmware.bin.extracted/ # extracted filesystem tree
│ ├── structure/
│ │ ├── stage.json
│ │ └── structure.json # partition layout + magic bytes
│ ├── carving/
│ │ ├── stage.json
│ │ └── carving.json # carved fragments + rootfs candidates
│ ├── firmware_profile/
│ │ ├── stage.json
│ │ └── firmware_profile.json # OS/arch/SDK/branch_plan
│ ├── inventory/
│ │ ├── stage.json
│ │ ├── inventory.json # file catalog + coverage metrics
│ │ ├── string_hits.json # interesting strings across binaries
│ │ └── binary_analysis.json # binary risk/arch summary
│ ├── surfaces/
│ │ ├── stage.json
│ │ ├── surfaces.json # network services + interfaces
│ │ ├── endpoints.json # input handlers
│ │ └── source_sink_graph.json # taint path candidates
│ └── findings/
│ ├── pattern_scan.json # structured findings
│ ├── binary_strings_hits.json # string-level evidence
│ ├── chains.json # kill-chain hypotheses
│ ├── review_gates.json # scoring per finding
│ ├── known_disclosures.json # CVE matches
│ └── poc_skeletons/ # safe templates
└── report/
├── report.json # aggregated report
├── report.html # human-readable
├── analyst_overview.json # additive operator payload (derived)
└── viewer.html # additive single-pane viewer (offline-safe)
Every StageFactory stage.json contains (findings is emitted by run_findings()):
{
"stage": "inventory",
"status": "ok | partial | failed",
"started_at": "2026-02-16T03:26:00Z",
"finished_at": "2026-02-16T03:27:14Z",
"artifacts": [
{
"path": "stages/inventory/inventory.json",
"sha256": "a1b2c3..."
}
],
"limitations": [...]
}| Document | Purpose |
|---|---|
docs/blueprint.md |
Full pipeline architecture and design rationale |
docs/status.md |
Current implementation status — single source of truth |
docs/aiedge_firmware_artifacts_v1.md |
Schema contracts for profiling + inventory artifacts |
docs/aiedge_adapter_contract.md |
Terminator↔SCOUT handoff protocol |
docs/aiedge_report_contract.md |
Report structure and governance rules |
docs/analyst_digest_contract.md |
Canonical report/analyst_digest.json schema and verdict semantics |
docs/runbook.md |
Operator flow for digest-first review + verified-chain proof gates |
docs/codex_first_agent_policy.md |
Codex-first execution policy and fallback/limitations |
| Tool | Purpose |
|---|---|
| binwalk | Signature scanning + recursive extraction |
| unblob | Modern firmware extraction (handles edge cases binwalk misses) |
| jefferson | JFFS2 extraction |
| sasquatch | Non-standard squashfs extraction |
| ubi_reader | UBI/UBIFS extraction |
| Tool | Purpose |
|---|---|
| Ghidra (headless + MCP) | Decompilation, CFG, function signatures |
| radare2 (+ MCP) | Disassembly, xrefs, string analysis |
| readelf / objdump | ELF metadata, sections, symbols |
| checksec | Protection matrix (NX/PIE/RELRO/Canary) |
| strings | Raw string extraction |
| FLIRT/Lumina | Library function signature matching |
| rbasefind | Base address detection for RTOS blobs |
| Tool | Purpose |
|---|---|
| QEMU user-mode | Single binary execution (cross-arch) |
| QEMU system-mode | Full system emulation |
| FirmAE | Automated firmware emulation (~80% boot rate) |
| Unicorn Engine | Partial function emulation |
| GDB + pwndbg | Debugging in emulated environment |
| Tool | Purpose |
|---|---|
| AFL++ QEMU mode | Cross-architecture greybox fuzzing |
| libFuzzer | In-process fuzzing (when source available) |
| Boofuzz | Network protocol fuzzing |
| AFLNet | Stateful network protocol fuzzing |
| Tool | Purpose |
|---|---|
| pwntools | Exploit framework (ROP, shellcraft, tubes) |
| ROPgadget / ropper | Gadget discovery |
| one_gadget | Quick win exploit primitives |
| angr | Symbolic execution for path validation |
AUTHORIZED ENVIRONMENTS ONLY
SCOUT and the Terminator firmware pipeline are designed for:
- Authorized security assessments — contracted firmware security audits with explicit vendor permission
- Vulnerability research — responsible disclosure with coordinated timelines
- CTF / lab environments — practice and training on designated targets
Strict guardrails:
- Dynamic validation runs in sandboxed containers with no external network access
- Exploit development requires explicit
--ack-authorizationand lab environment confirmation - No weaponized payloads in default output —
poc_skeletons/contains safe templates only - Full audit trail for every LLM judgment and exploit generation step
confirmedstatus requires dynamic evidence — no exceptions
MIT License