diff --git a/CHANGELOG.md b/CHANGELOG.md index 6c57bd5..013639c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,58 @@ and the project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0. ## [Unreleased] +Lessons from the 2026 agent-driven cloud attacks: Microsoft's Storm-3168, +Sysdig's JADEPUFFER and Sygnia's AI-assisted intrusion. + +### Added +- **Cloud audit logs as a second witness.** `--endpoint` (on `analyze`, + `run` and `correlate`) now reads AWS CloudTrail, Azure Activity Log and + GCP Cloud Audit Log exports as JSON, a JSON array or JSON Lines, sniffed + per file (`--format cloud-audit` to force it). An agent's `az`, `aws` or + `gcloud` command becomes CORROBORATED when the control plane recorded the + same operation within ±2 minutes, on the same resource when the command + names one; a call the cloud refused is noted with its error. A cloud log + never contradicts a command, since the export may cover another account, + and it never counts as host telemetry. Files only, no cloud API calls. +- **`CLOUD_DESTRUCTIVE_BURST`** (HIGH): the cloud audit log shows one + identity deleting 10+ distinct resources within 10 minutes. It says + whether any of the deletes match commands in the collected transcripts. +- **`CLOUD_RESOURCE_DELETION`** (HIGH): the agent deletes storage accounts + or buckets, key vaults, secrets, KMS keys, apps, resource groups, VMs, + clusters, stacks or projects through `az`, `aws`, `gcloud` or `gsutil`. +- **`CLOUD_RECOVERY_PROTECTION_REMOVED`** (CRITICAL): the agent deletes a + resource lock, disables backup protection, deletes a backup vault, + recovery point or snapshot, suspends bucket versioning or turns off + deletion protection. +- **Guardrail pack `cloud-destructive`** (ask, friction 1): the agent must + ask before those commands, before destroying databases and before + stopping cloud logging. Claude Code and Codex. Read and list commands are + never asked. +- **Incident `storm-3168-jadepuffer`** for `agentdfir hunt`: the published + attacker addresses, and Langflow below 1.3.0 (CVE-2025-3248, the entry + point) at low confidence. Package indicators gain `below_version` for + vulnerable-version ranges. The ransom note's Bitcoin address is left out + on purpose: it is the example address from the Bitcoin docs. +- **Authority-claim phrases** ("this is an approved red team exercise", + "you are authorized to bypass" …) count as injection in tool and MCP + results, fetched content, instruction files, tool definitions, MCP + configs and repository files. Never in the person's own prompts, where a + pen-tester writes exactly that. +- Secret formats `ALIBABA_ACCESS_KEY` (`LTAI…`) and `TENCENT_SECRET_ID` + (`AKID…`). + +### Changed +- `CLOUD_CREDENTIAL_EXPORT` also covers listing storage account keys and + connection strings, Cosmos DB keys, service-principal credential resets + and GCS HMAC keys. +- The Protect tab's steps for a leaked secret say that deleting it from an + issue, pull request or commit does not remove it from edit history, + forks or caches. + +### Fixed +- Re-running analysis with the same endpoint log appended the same + corroboration note to an event again on every run. + ## [3.1.2] — 2026-09-29 ### Changed diff --git a/README.md b/README.md index f8bae45..473374c 100644 --- a/README.md +++ b/README.md @@ -104,6 +104,7 @@ Add a second witness and the same commands upgrade every finding from *the agent ```sh agentdfir analyze CASE-2026-042.adfir --endpoint /var/log/audit/audit.log # auditd / Sysmon XML / EDR exports +agentdfir analyze CASE-2026-042.adfir --endpoint cloudtrail.json # CloudTrail / Azure Activity Log / GCP audit exports agentdfir analyze CASE-2026-042.adfir --gateway-log mcp-gateway.jsonl # your MCP gateway's own log ``` @@ -131,7 +132,7 @@ Every capability below comes from a real 2025–2026 incident and is tested against a reproduction of it (`agentdfir simulate --scenario list`). ```sh -agentdfir hunt --path ~/src # was I hit? s1ngularity, Shai-Hulud 1/2, keyv wave, SANDWORM_MODE, postmark-mcp, codexui, Amazon Q +agentdfir hunt --path ~/src # was I hit? s1ngularity, Shai-Hulud 1/2, keyv wave, SANDWORM_MODE, postmark-mcp, codexui, Amazon Q, Storm-3168/JADEPUFFER agentdfir scan-repo ~/src/untrusted # before an agent opens it: committed SessionStart hooks, folderOpen tasks, repo MCP servers, injected AGENTS.md agentdfir decode payload.txt # nested base64/gzip/hex/UTF-16LE payloads, offline — no model refuses to help agentdfir monitor --journal # hash-chain every transcript append; a later edit becomes TRANSCRIPT_REWRITTEN @@ -160,7 +161,8 @@ agentdfir monitor --journal # hash-chain every transcript append; a lat secret-hunting prompt, exfiltration through trusted services (link shorteners, screenshot services, `workers.dev`, blockchain RPC, tunnels), GitHub repo creation as an exfil path, data encoded into DNS - labels, cloud/database destruction (Replit, PocketOS), MCP tool-definition + labels, cloud/database destruction (Replit, PocketOS), cloud resource deletion and + removal of resource locks, backups and deletion protection (Storm-3168), MCP tool-definition rug-pulls and typosquatted MCP packages. ## 🛡️ Stop it happening again — `agentdfir mitigate` (v2.7) @@ -170,7 +172,8 @@ for Claude Code, a rules file for Codex, `permissions.deny` for Cursor, pinned MCP packages and cleared auto-approve lists. Two packs are on by default and never get in the way of legitimate work — **keep the agents' own logs** (a `PreToolUse` guard that refuses transcript deletion) and **keep credential files -away from the agent**. Six more are opt-in, each with its friction stated. +away from the agent**. Seven more are opt-in, each with its friction stated, +including **ask before deleting cloud resources or their backups**. ```sh agentdfir mitigate # the plan — nothing is written @@ -293,7 +296,7 @@ Ships with wrappers for tools IR teams already run: | ✅ | `simulate` — synthetic incident generation (adversary emulation for AI agents): `orphan-agent`, `toxic-chain`, and reproductions of real incidents — `keyv-hook`, `sandworm-mcp`, `s1ngularity`, `mcpoison-rugpull`, `pocketos-wipe`, `swarm-antiforensics` (v3.0) | | ✅ | [Attack chains](docs/attack-chains.md), session cards, investigation tree, whole-case search and the hash-chained analyst case file in the [explorer](docs/serve.md) (v1.0) | | ✅ | Full parsers for 13 products: Claude Code, Claude Cowork (desktop-app agent mode: HMAC audit log, in-VM transcripts, shared folders and egress allowlist per session), Codex CLI + Codex desktop app (rollout JSONL and the SQLite thread store, read with a stdlib-only reader that applies the write-ahead log), Gemini CLI, Cursor, Copilot CLI, Copilot Chat (VS Code), Cline, Roo, OpenClaw, OpenCode, Aider, Warp — plus Kiro (steering, specs, MCP, powers, skills and extension state; no transcript store to parse) | -| ✅ | [Enrich with a second witness](docs/endpoint-corroboration.md) — auditd, Sysmon XML, Velociraptor/osquery/eslogger/EDR exports: tool calls → CONFIRMED / DISPROVED, unlogged agent processes and connections surfaced | +| ✅ | [Enrich with a second witness](docs/endpoint-corroboration.md) — auditd, Sysmon XML, Velociraptor/osquery/eslogger/EDR exports, and AWS CloudTrail / Azure Activity Log / GCP Cloud Audit Log exports for the agent's `az` / `aws` / `gcloud` commands: tool calls → CONFIRMED / DISPROVED, unlogged agent processes and connections surfaced | | ✅ | Reports: network-silent HTML, self-contained PDF (stdlib writer, no renderer deps), JSON, CSV, STIX 2.1, OTel · [OCSF 1.3, SARIF 2.1, Sigma export](docs/siem-interop.md) for SIEM/SOC pipelines | | ✅ | [`serve`](docs/serve.md) — local browser case explorer: agent tree, density-scrubber timeline, raw evidence pane, findings, topology; loopback-only, zero external resources | | ✅ | `monitor` live watch · [`--detect --alert`](docs/realtime-detection.md) real-time sensor (webhook / syslog / file) · `replay` session step-through · `investigate` explorer | diff --git a/docs/detection-coverage.md b/docs/detection-coverage.md index 9b41aa6..88b08aa 100644 --- a/docs/detection-coverage.md +++ b/docs/detection-coverage.md @@ -4,10 +4,10 @@ | | | |---|---| -| Rules (built-in + shipped packs) | **172** (91 built-in, 81 in `rules/`) | -| HIGH / CRITICAL rules | 117, of which **115** carry a MITRE mapping | +| Rules (built-in + shipped packs) | **175** (92 built-in, 83 in `rules/`) | +| HIGH / CRITICAL rules | 120, of which **118** carry a MITRE mapping | | Distinct MITRE ATLAS techniques covered | **28** (ATLAS 5.6.0) | -| Distinct MITRE ATT&CK techniques covered | **69** | +| Distinct MITRE ATT&CK techniques covered | **70** | Every HIGH/CRITICAL rule must map to at least one MITRE technique; every `mitre_atlas` value must exist in the embedded ATLAS release. Both are @@ -52,7 +52,7 @@ agentdfir rules list --packs rules --json # machine-readable | `AML.T0086` | Exfiltration via AI Agent Tool Invocation | `CHAIN_SECRET_TO_EXFIL`, `CHAIN_SUBAGENT_CROSS_TALK_EXFIL`, `EGRESS_VIA_TRUSTED_SERVICE`, `GITHUB_EXFIL_REPO_CREATE`, `POTENTIAL_DATA_EXFILTRATION`, `CLOUD_STORAGE_UPLOAD` (agentdfir-community), `CURL_FILE_UPLOAD` (agentdfir-community), `DNS_EXFIL_LABELS` (agentdfir-community), `DNS_TUNNEL_TOOL` (agentdfir-community), `ENV_DUMP_TO_NETWORK` (agentdfir-community), `GIT_PUSH_TO_URL` (agentdfir-community), `REMOTE_COPY_TO_HOST` (agentdfir-community), `WEBHOOK_C2_EXFIL` (agentdfir-community), `PASTE_SITE_DESTINATION` (agentdfir-starter) | | `AML.T0090` | OS Credential Dumping | `LSASS_CREDENTIAL_DUMP` (agentdfir-community), `MEMORY_CREDENTIAL_DUMP` (agentdfir-community), `SHADOW_FILE_ACCESS` (agentdfir-community) | | `AML.T0099` | AI Agent Tool Data Poisoning | `CHAIN_MCP_RESULT_TO_DESTRUCTIVE`, `MCP_TOOL_POISONING` | -| `AML.T0101` | Data Destruction via AI Agent Tool Invocation | `CHAIN_ACTION_THEN_LOG_TAMPER`, `DESTRUCTIVE_COMMAND`, `TRANSCRIPT_REWRITTEN`, `CLOUD_DATA_DESTRUCTION` (agentdfir-community), `DISK_WIPE` (agentdfir-community) | +| `AML.T0101` | Data Destruction via AI Agent Tool Invocation | `CHAIN_ACTION_THEN_LOG_TAMPER`, `CLOUD_DESTRUCTIVE_BURST`, `DESTRUCTIVE_COMMAND`, `TRANSCRIPT_REWRITTEN`, `CLOUD_DATA_DESTRUCTION` (agentdfir-community), `CLOUD_RECOVERY_PROTECTION_REMOVED` (agentdfir-community), `CLOUD_RESOURCE_DELETION` (agentdfir-community), `DISK_WIPE` (agentdfir-community) | | `AML.T0103` | Deploy AI Agent | `AI_CLI_HEADLESS_BYPASS`, `NESTED_AGENT_PERMISSION_BYPASS` (agentdfir-community) | | `AML.T0110` | AI Agent Tool Poisoning | `MCP_TOOL_DEFINITION_CHANGED`, `MCP_TOOL_DESCRIPTION_POISONING`, `TOOL_POISONING_INDICATOR` | @@ -96,8 +96,9 @@ agentdfir rules list --packs rules --json # machine-readable | [`T1204`](https://attack.mitre.org/techniques/T1204/) | `REPO_INSTRUCTION_INJECTION` | | [`T1204.002`](https://attack.mitre.org/techniques/T1204/002/) | `UNSAFE_MODEL_ARTIFACT_LOAD` (agentdfir-community) | | [`T1222`](https://attack.mitre.org/techniques/T1222/) | `CHMOD_WORLD_WRITABLE` (agentdfir-community) | -| [`T1485`](https://attack.mitre.org/techniques/T1485/) | `CHAIN_MCP_RESULT_TO_DESTRUCTIVE`, `DESTRUCTIVE_COMMAND`, `CLOUD_DATA_DESTRUCTION` (agentdfir-community) | +| [`T1485`](https://attack.mitre.org/techniques/T1485/) | `CHAIN_MCP_RESULT_TO_DESTRUCTIVE`, `CLOUD_DESTRUCTIVE_BURST`, `DESTRUCTIVE_COMMAND`, `CLOUD_DATA_DESTRUCTION` (agentdfir-community), `CLOUD_RESOURCE_DELETION` (agentdfir-community) | | [`T1486`](https://attack.mitre.org/techniques/T1486/) | `BULK_FILE_ENCRYPTION` (agentdfir-community) | +| [`T1490`](https://attack.mitre.org/techniques/T1490/) | `CLOUD_RECOVERY_PROTECTION_REMOVED` (agentdfir-community) | | [`T1496`](https://attack.mitre.org/techniques/T1496/) | `CRYPTOMINER_EXECUTION` (agentdfir-community) | | [`T1543`](https://attack.mitre.org/techniques/T1543/) | `SERVICE_PERSISTENCE` (agentdfir-community) | | [`T1546`](https://attack.mitre.org/techniques/T1546/) | `REPO_AGENT_HOOK_AUTORUN`, `REPO_CODEX_PROJECT_CONFIG`, `REPO_DEVCONTAINER_HOST_COMMAND`, `REPO_GIT_EXEC_CONFIG`, `REPO_VSCODE_AUTORUN_TASK`, `AGENT_CONFIG_SHELL_WRITE` (agentdfir-community), `GIT_HOOK_INSTALL` (agentdfir-community), `MEMORY_INSTRUCTION_CALLOUT` (agentdfir-community) | @@ -140,6 +141,7 @@ agentdfir rules list --packs rules --json # machine-readable | CRITICAL | `CHAIN_MCP_RESULT_TO_DESTRUCTIVE` | transcript | T1485 | AML.T0099 | builtin | | CRITICAL | `CHAIN_ORPHAN_PERSISTENCE` | transcript | T1562.001 | AML.T0081 | builtin | | CRITICAL | `CHAIN_SECRET_TO_EXFIL` | transcript | T1048 | AML.T0086 | builtin | +| CRITICAL | `CLOUD_RECOVERY_PROTECTION_REMOVED` | command | T1490 | AML.T0101 | agentdfir-community | | CRITICAL | `DISK_WIPE` | command | T1561 | AML.T0101 | agentdfir-community | | CRITICAL | `JOURNAL_TAMPERED` | transcript | T1070 | - | builtin | | CRITICAL | `KNOWN_INCIDENT_IOC` | transcript | T1195.002 | AML.T0010 | builtin | @@ -165,9 +167,11 @@ agentdfir rules list --packs rules --json # machine-readable | HIGH | `CHAIN_SUBAGENT_CROSS_TALK_EXFIL` | transcript | T1048 | AML.T0086 | builtin | | HIGH | `CLOUD_CREDENTIAL_EXPORT` | command | T1552.005 | AML.T0055 | agentdfir-community | | HIGH | `CLOUD_DATA_DESTRUCTION` | command | T1485 | AML.T0101 | agentdfir-community | +| HIGH | `CLOUD_DESTRUCTIVE_BURST` | endpoint | T1485 | AML.T0101 | builtin | | HIGH | `CLOUD_IAM_PERSISTENCE` | command | T1098 | - | agentdfir-community | | HIGH | `CLOUD_LOGGING_DISABLE` | command | T1562.008 | - | agentdfir-community | | HIGH | `CLOUD_METADATA_ACCESS` | command | T1552.005 | AML.T0075 | agentdfir-community | +| HIGH | `CLOUD_RESOURCE_DELETION` | command | T1485 | AML.T0101 | agentdfir-community | | HIGH | `CLOUD_STORAGE_UPLOAD` | command | T1567.002 | AML.T0086 | agentdfir-community | | HIGH | `CONFIG_HOOK_REMOTE_FETCH` | config | T1059.004 | AML.T0081 | agentdfir-community | | HIGH | `CONTAINER_ESCAPE_MOUNT` | command | T1611 | - | agentdfir-community | diff --git a/docs/endpoint-corroboration.md b/docs/endpoint-corroboration.md index fceb7df..14674d2 100644 --- a/docs/endpoint-corroboration.md +++ b/docs/endpoint-corroboration.md @@ -17,9 +17,10 @@ agentdfir correlate CASE-42.adfir /var/log/audit/audit.log # Linux au agentdfir correlate CASE-42.adfir sysmon.xml # Windows Sysmon (XML export) agentdfir correlate CASE-42.adfir procs.jsonl netconns.csv # Velociraptor / osquery / eslogger / EDR exports agentdfir analyze CASE-42.adfir --endpoint audit.log # same, inside the one-shot analysis +agentdfir analyze CASE-42.adfir --endpoint cloudtrail.json # cloud audit export: the agent's az / aws / gcloud commands ``` -Format is sniffed per file (`--format auditd|sysmon-xml|jsonl|csv` to override). `--window 3s` sets the match window. Results are written back to `normalized/events.jsonl` (states + an evidence note naming the confirming record) and to `detections/corroboration.json`; `triage` merges the findings so every downstream report, OCSF/SARIF export and PDF carries the upgraded states. +Format is sniffed per file (`--format auditd|sysmon-xml|jsonl|csv|cloud-audit` to override). `--window 3s` sets the match window. Results are written back to `normalized/events.jsonl` (states + an evidence note naming the confirming record) and to `detections/corroboration.json`; `triage` merges the findings so every downstream report, OCSF/SARIF export and PDF carries the upgraded states. ## Supported telemetry @@ -29,6 +30,12 @@ Format is sniffed per file (`--format auditd|sysmon-xml|jsonl|csv` to override). | **Sysmon** | `wevtutil qe Microsoft-Windows-Sysmon/Operational /f:xml > sysmon.xml` | EventID 1 process, 3 network, 11/23/2 file | | **Generic JSONL / CSV** | Velociraptor (`Linux.Events.ProcessExecutions`, `Windows.System.Pslist`…), osquery `process_events`, `evtx_dump -o jsonl`, macOS `eslogger exec open create unlink` | nested JSON flattened; ~90 field aliases cover pid/ppid/exe/cmdline/parent/user/dest ip+port/file path | +| **AWS CloudTrail** | the trail's S3 objects (`{"Records":[…]}`), `aws cloudtrail lookup-events` output, or events one per line | cloud: service and API call, principal ARN, source IP, request resource names, error code | +| **Azure Activity Log** | `az monitor activity-log list -o json`, the REST `{"value":[…]}` shape, or diagnostic-settings `{"records":[…]}` blobs | cloud: Resource Manager operation, caller, resource id, client IP, Failed status | +| **GCP Cloud Audit Logs** | `gcloud logging read 'logName:cloudaudit.googleapis.com' --format=json` | cloud: service and method, principal email, resource name, caller IP, status code | + +Cloud exports are read from files only: nothing calls a cloud API. + Raw `.evtx` is not parsed — export first (documented limitation). macOS unified log lacks exec argv; use `eslogger` (Endpoint Security) output. ## What the engine does @@ -43,8 +50,16 @@ Raw `.evtx` is not parsed — export first (documented limitation). macOS unifie | `UNLOGGED_AGENT_ACTIVITY` | MEDIUM | agent-lineage process exec with no transcript tool call (grouped per program, runtime helpers filtered) | `nc 185.10.10.10 4444` spawned under the agent, not in any transcript | | `UNLOGGED_AGENT_NETWORK` | HIGH | agent-lineage connection to a non-allowlisted destination with no transcript reference | Cursor's `node` child connects to `185.x.x.x:4444` | +| `CLOUD_DESTRUCTIVE_BURST` | HIGH | a cloud audit log shows one identity deleting 10+ distinct resources within 10 minutes (refused calls do not count) | a leaked service principal deletes a dozen storage accounts in six minutes | + Contradiction requires **process** telemetry covering that moment; with only network or file records, unmatched commands stay OBSERVED. +### Cloud audit logs + +An agent's `az`, `aws` or `gcloud` command is **CORROBORATED** when the control plane recorded the same operation within ±2 minutes, on the same resource when the command names one: `az storage account delete -n acct1` matches `Microsoft.Storage/storageAccounts/delete` on `…/storageAccounts/acct1`, `aws s3 rb s3://b` matches `s3:DeleteBucket` for `b`, `gcloud sql instances delete db1` matches `cloudsql.instances.delete` on `…/instances/db1`. When the cloud refused the call, the note says so and names the error. + +A cloud log never **contradicts** a command. The export may be for a different account, subscription or project, so a missing record means nothing. Cloud records also do not count as host telemetry: a cloud-only export gives no process coverage. + ## Example ``` diff --git a/docs/hunt.md b/docs/hunt.md index 04a5411..8562629 100644 --- a/docs/hunt.md +++ b/docs/hunt.md @@ -16,7 +16,7 @@ agentdfir hunt --list # incidents, indicator counts, s Exit status is `1` when an incident's indicators are present, `0` otherwise, so it drops into scripts and CI. -## Incidents shipped (pack `agentdfir-incidents` v1) +## Incidents shipped (pack `agentdfir-incidents` v2) Every indicator is copied from the cited write-up; nothing is inferred. `--list` prints the sources. @@ -31,6 +31,7 @@ Every indicator is copied from the cited write-up; nothing is inferred. | `codexui-android` | Codex token theft, Apr–May 2026 | `codexui-android`, `@friuns/codexui`, `sentry.anyclaw.store`, XOR key | | `keyv-wave` | Shai-Hulud keyv / cacheable wave, Aug 2026 — committed SessionStart hook + folderOpen task | `keyv@6.0.0` and siblings, `npm-cache.com`, payload file names, repo description | | `amazon-q-wiper` | Amazon Q VS Code 1.84.0 wiper prompt, Jul 2025 | the extension install directory | +| `storm-3168-jadepuffer` | LLM-driven cloud and database destruction, Jun–Sep 2026 (Microsoft Storm-3168, Sysdig JADEPUFFER) | the three published attacker addresses; Langflow below 1.3.0 (CVE-2025-3248, the entry point) at low confidence, because a vulnerable version is exposure, not compromise | Incidents without host indicators (Anthropic's GTG-1002 / GTG-2002 reports, the OpenAI–Hugging Face agent intrusion, Replit, PocketOS) are covered by diff --git a/docs/index.html b/docs/index.html index 2d56037..d4b58ce 100644 --- a/docs/index.html +++ b/docs/index.html @@ -514,7 +514,7 @@

Incident response for AI agents.

What the agent says is not what the computer did.

“I ran curl example.com” in a chat proves nothing. Every action on every timeline says how strongly it is backed, from a claim in the conversation up to the computer's own records, and a second witness (auditd, Sysmon, your EDR or MCP gateway) can confirm it or prove it false.

@@ -581,7 +581,7 @@

Seven hundred alerts are not an answer. The chain is.

The last twelve months of agent incidents, as commands.

New in v3.0. Every capability comes from a real 2025–2026 incident and is tested against a reproduction of it (agentdfir simulate --scenario list).