An autonomous agent that investigates Windows attack telemetry across 73,909 real events, reconstructs the kill chain, and maps findings to MITRE ATT&CK — then deploys a second, independent Gemini instance as a Forensic Auditor that re-queries the raw Elasticsearch evidence and challenges every claim before the report is written to disk.
Most AI security tools produce a report and stop. This one interrogates its own conclusions. After the Triage Agent finishes, a second Gemini instance — with access only to read-only ES|QL tools and no knowledge of the first agent's tool history — reissues fresh queries against Elasticsearch and labels every MITRE ATT&CK claim independently.
Refutals are findings. Each refuted claim cites the exact ES|QL query output that contradicted it — narrowing the confirmed attack surface and eliminating hallucinated timestamps, host names, or techniques before the report reaches an analyst.
Results vary by investigation quality. When the Triage Agent gets every timestamp right, the Auditor returns all verified. When it hallucinates — a fabricated date, a wrong host, an inferred-but-absent technique — the Auditor catches it and cites the exact ES|QL output that contradicted the claim.
Run with clean triage output (May 2026):
Run with accurate triage output (June 2026):
Both outcomes are correct behaviour. Twenty refutals means the Triage Agent fabricated timestamps — the Auditor eliminated those false positives before the report was saved. Zero refutals means the Triage Agent got everything right and the Auditor confirmed it independently. Two examples from the run with refutals:
cmd.exe process creation on
MSEDGEWIN10 by IEUser at 2019-07-19T14:42:53Z
MalSeclogon.exe and samir.exe
accessing lsass on MSEDGEWIN10 and LAPTOP-JU4M3I0E
15-step kill chain spanning two hosts and two and a half years (2019–2021), reconstructed from raw Windows event logs via ES|QL queries. Every step independently verified by the Forensic Auditor.
| # | Timestamp | Host | Technique | Process / IOC | Evidence |
|---|---|---|---|---|---|
| 01 | 2019-07-19T14:42 | MSEDGEWIN10 | T1059.003 | cmd.exe | Windows Command Shell, IEUser |
| 02 | 2019-07-19T14:43 | MSEDGEWIN10 | T1059.001 | powershell.exe | PowerShell, IEUser |
| 03 | 2019-07-19T14:44 | MSEDGEWIN10 | T1543.003 | sc.exe | New service creation via sc.exe |
| 04 | 2019-07-19T14:44 | MSEDGEWIN10 | T1543.003 | AtomicService.exe | Service binary launched as SYSTEM |
| 05 | 2019-07-19T14:45 | MSEDGEWIN10 | T1059.005 | csc.exe | Compiled .NET code at runtime |
| 06 | 2019-07-19T14:45 | MSEDGEWIN10 | T1559.001 | RegAsm.exe | COM hijacking via RegAsm/RegSvcs |
| 07 | 2019-07-19T14:46 | MSEDGEWIN10 | T1559.001 | RegSvcs.exe | COM event triggered execution |
| 08 | 2019-07-19T14:47 | MSEDGEWIN10 | T1490 | vssadmin.exe | Shadow copy deletion |
| 09 | 2019-07-19T14:47 | MSEDGEWIN10 | T1490 | wbadmin.exe | Backup catalog deletion |
| 10 | 2019-07-19T14:47 | MSEDGEWIN10 | T1547.006 | bcdedit.exe | Boot config modification |
| 11 | 2019-07-19T14:48 | MSEDGEWIN10 | T1105 | bitsadmin.exe | BITS job — ingress tool transfer |
| 12 | 2020-10-27T10:17 | LAPTOP-JU4M3I0E | T1003.001 | samir.exe | LSASS access, event code 10 |
| 13 | 2021-08-07T23:33 | MSEDGEWIN10 | T1003 | IEUser | OS credential dump, event code 4663 |
| 14 | 2021-12-07T17:33 | MSEDGEWIN10 | T1003.001 | MalSeclogon.exe | LSASS access, event code 10 |
| 15 | various | multiple | T1021 | IEUser, user01 | Lateral movement — 2 hosts each |
The Triage Agent calls six ES|QL detection tools through the Elastic Agent Builder MCP server, routed via a Cloud Run proxy that handles auth injection and REST-to-JSON-RPC translation. Findings are written to an Elasticsearch memory index using ELSER sparse-vector + BM25 hybrid search. The Forensic Auditor runs as a second, isolated Gemini pass with read-only tool access.
Security controls are architectural, not instructional. The model is never told to be careful — the code simply makes the bad path impossible.
search_memory is filtered by session_id at the Elasticsearch query level. The model cannot reach another investigation's data — it's not instructed away from it.
time_window: regex match + 100-year DoS cap. host_name: alphanumeric allowlist + ES|QL char blocklist. All integers range-checked before any ES call.
write_memory checks the target index against a hardcoded frozenset at write time. Setting ELASTIC_INDEX_MEMORY=ir-events in the environment is caught in the tool, not at config load.
ELASTIC_API_KEY_READ for ES|QL queries. ELASTIC_API_KEY_WRITE for the memory index only. The read path cannot write under any circumstance.
os.open/os.write — not buffered IO. Rejected calls log the rejection reason; allowed calls log duration and arguments.
os.write per tool call, with duration and arguments