Architecture
Three agents. Zero shared state. One rule: CONFIRMED requires a positive tool return.
The Pipeline
Layered security architecture — adversarial training signal through output management.
Mounted Disk Image (read-only)
|
v
+---------------------------+
| Deterministic Sweep | ~25 SIFT commands, no LLM, <60s
| (fast_triage.py) | corpus-calibrated signal weights
+---------------------------+
|
v
+---------------------------+ +---------------------------+
| Disk Agent | | Memory Agent |
| (blue_agent.py) | | (memory_agent.py) |
| 75-call Claude budget | | Volatility 3 parallel |
| raw artifacts only | | process injection, |
| no scores, no labels | | VAD anomalies, |
| | | credential dumps |
+---------------------------+ +---------------------------+
| findings list only |
| (technique IDs, nothing else)|
+------------------------------+
v
+---------------------------+
| Forensic Auditor | isolated MCP session, no shared state
| (auditor_agent.py) | mandate: assume every finding is false
| | 5 rounds × 3 tool calls per technique
+---------------------------+
|
v
CONFIRMED / REFUTED / INCONCLUSIVE
+ append-only audit log (audit_log.jsonl)
+ HTML report
The structural guarantee: The Auditor receives technique IDs and nothing else from the investigator sessions. CONFIRMED requires a positive tool return value. Timeout returns INCONCLUSIVE — never CONFIRMED.
Architectural Pattern
Custom MCP Server + Multi-Agent Framework
A purpose-built MCP server exposes typed forensic functions rather than a generic shell. Three Claude agents run in completely separate MCP sessions with zero shared state. The Disk Agent and Memory Agent investigate on disjoint evidentiary layers. The Forensic Auditor independently re-runs tool calls to verify every finding. A CONFIRMED verdict requires a positive tool return — not model confidence. The agents physically cannot run destructive commands because the MCP server does not expose them.
Security Boundaries
| Boundary | Type | Enforcement |
|---|---|---|
| Operator instructions / system prompts | Prompt-based | Model can ignore |
| MCP Validator Gate (4 gates) | Architectural | Python-enforced before any subprocess.run() |
| Binary allowlist | Architectural | 53 approved SIFT tools — unknown binaries rejected |
| Redirect guard | Architectural | os.path.realpath() — no writes outside reports/ |
| Auditor independence | Architectural | Separate MCP session, separate asyncio task, no shared state |
The MCP Validator Gate
Every forensic action flows through one primitive: run_terminal_command. Four gates execute in Python before any subprocess call:
Gate 1 — Argument injection prevention 22 hard-blocked tokens: destructive ops (shred, mkfs), exfil tools (curl, wget, nc), privilege escalation (sudo), and command substitution ($(), backtick). An approved binary can still be weaponized if attacker-controlled log content injects shell metacharacters into its arguments.
Gate 2 — Deny-all binary enforcement 53-binary SIFT allowlist. Every binary not on the list is rejected unconditionally. sed is excluded — its -e flag passes the pattern space to the shell.
Gate 3 — Quote-aware pipeline parser Each pipe segment validated after tracking single-quoted substrings. grep -iE '(http|https|ftp)' passes correctly — | inside quotes is argument content, not a separator.
Gate 4 — Write-target guard All >, >>, tee targets resolved via os.path.realpath(). Must resolve inside reports/. Symlink and ../ attacks fail at the path arithmetic level.
Every command is atomically appended to audit_log.jsonl via os.open + os.write before subprocess.run is called. Evidence modification is structurally impossible.
Auditor Independence
The Forensic Auditor receives:
- A JSON list of technique IDs
The Forensic Auditor does not receive:
- The Disk Agent’s or Memory Agent’s tool call history
- Either investigator’s reasoning or analysis
- Confidence scores or weights
- Any context from the investigator MCP sessions
This is enforced by the code, not a prompt. Reading auditor_agent.py verifies the property.
Campaign IOC Propagation
After a host is investigated, confirmed artifacts are extracted to a structured JSON file. Only Auditor-confirmed artifacts — nothing the Auditor rejected.
# Investigate second host with first host's confirmed IOCs injected
python3 custom-agent/investigate.py --case ~/cases/tdungan nfury
The IOC file injects into the deterministic sweep of the next host — no LLM, no API call. Hallucinations from the first host cannot contaminate the second investigation because they never made it into the IOC file.
Detection Layer — Proof of Concept
The triage layer generates candidates for the Auditor. It is not the architectural contribution.
| Component | What it does | Status |
|---|---|---|
fast_triage.py | ~25 SIFT commands, corpus-calibrated weights, <10s, no API key | POC |
blue_agent.py Pass 2 | 75-call Claude loop, raw artifacts only, no labels | Working |
memory_agent.py | Volatility 3 parallel path | Working |
| Corpus weights | Log-odds from 800+ MalwareBazaar/HybridAnalysis samples | POC, 9 MITRE techniques |
The roadmap replaces corpus weights with a neural network trained against a validated benign baseline. The Auditor architecture is unchanged — any detection signal feeds the same verification layer.