Amazon Detective: Investigate Compromised Coding-Agent Credentials After GuardDuty

0 views

Amazon GuardDuty just paged you: UnauthorizedAccess:IAMUser/InstanceCredentialExfiltration on a Fargate task role named coding-agent-tool-prod. You revoked the key. Good. Now security asks: which tenants’ artifacts did the attacker touch, which Bedrock models were invoked, and is the same principal still hopping via AssumeRole? Raw CloudTrail answers that in six hours of Athena. Amazon Detective answers it in a graph — entity profiles, finding groups, and behavioral baselines built automatically from your GuardDuty + CloudTrail + VPC Flow telemetry. Pair with CloudTrail Lake for custom SQL and Macie if the exfil path was an artifact bucket.

⚡ TL;DR: Enable Detective in every agent account (Org delegated admin). Open GuardDuty findings from Detective so you get the IAM user/role profile, related findings, and unusual API sequences pre-joined. Scope investigations by agent role name prefixes and session tags. Export timelines to tickets; revoke with AWSRevokeOlderSessions. Related: GuardDuty, CloudTrail Lake, SCPs, Budgets.

Why GuardDuty alone is not an investigation

GuardDuty is a detector. It scores a finding and points at a resource. Coding-agent incidents are multi-hop by design:

  1. Task role credentials leave the sandbox (SSRF, log leak, prompt injection)
  2. Attacker calls sts:AssumeRole into a wider tool role
  3. S3 List/Get on artifact prefixes, Bedrock InvokeModel, maybe EC2 in a new region
  4. Optional: disable logging / create persistence keys

Each hop is a separate finding or a quiet CloudTrail row. Detective’s job is to group those into one investigation story and show you the baseline (“this role usually only calls S3 + Bedrock in us-east-1”) versus the anomaly (“sudden CreateAccessKey + RunInstances in ap-south-1”).

Layer Question it answers Agent use
GuardDuty Is this bad? Page + auto-revoke
Detective How did it spread? Graph + timeline
CloudTrail Lake Exact API autopsy Custom SQL
Macie / Security Hub What data / org-wide view Secrets + aggregation

Enable Detective for agent OUs

Use Organizations so every sandbox and prod agent account feeds the same Detective behavior graph. Detective needs GuardDuty enabled in the account (it consumes findings) plus CloudTrail management events and VPC Flow Logs for richer profiles.

bash
# ✅ enable Detective (member accounts via Org preferred)
aws detective create-graph --tags Key=workload,Value=coding-agent

# ✅ list graphs / confirm member accounts
aws detective list-graphs
aws detective list-members --graph-arn "$GRAPH_ARN"
typescript
// ✅ CDK sketch: enable Detective + tag for agent fleet ownership
import * as detective from "aws-cdk-lib/aws-detective";

const graph = new detective.CfnGraph(this, "AgentDetectiveGraph", {
  tags: [{ key: "workload", value: "coding-agent" }],
});
// Prefer Organizations auto-enable in production — do not hand-roll per account forever

❌ Standing up Detective in only the “security” account while agent sandboxes stay dark: you will investigate the wrong blast radius.

From GuardDuty finding → Detective investigation

The unfair advantage is the pivot: open the finding in Detective (console deep-link or API), not in a generic SIEM dump.

python
# ✅ start from a GuardDuty finding ID and pull Detective entity profile hints
import boto3

gd = boto3.client("guardduty")
det = boto3.client("detective")

def investigate_agent_finding(detector_id: str, finding_id: str, graph_arn: str):
    finding = gd.get_findings(DetectorId=detector_id, FindingIds=[finding_id])["Findings"][0]
    # Entity of interest for credential exfil is usually the IAM principal / access key
    ak = finding.get("Resource", {}).get("AccessKeyDetails", {})
    user_name = ak.get("UserName")
    access_key_id = ak.get("AccessKeyId")
    # ❌ Do not stop at revoke — collect the related finding group first
    print({
        "type": finding["Type"],
        "severity": finding["Severity"],
        "userName": user_name,
        "accessKeyId": access_key_id,
        "account": finding["AccountId"],
        "graph": graph_arn,
        "next": "Open IAM role/user profile in Detective; review Finding groups + New behavior",
    })
    return finding

In the Detective console for that role:

  • Overall API call volume vs baseline
  • New geolocations / user agents (attacker laptop vs Fargate UA)
  • Related findings in the same finding group (S3 exfil + anomalous EC2)
  • Resource interactions — which buckets/prefixes, which KMS keys

For agent fleets, filter mental model by role name prefix (coding-agent-*) and by aws:PrincipalTag/tenant_id if you stamp session tags on AssumeRole (IAM condition keys pattern).

Playbook: compromised coding-agent credentials

  1. Contain — delete access keys / invalidate role sessions (AWSRevokeOlderSessions on role trust); kill tool via AppConfig
  2. Investigate in Detective — open GuardDuty finding → role profile → finding group → note first anomalous API time
  3. Confirm with Lake — SQL for that principal’s 24h API set (CloudTrail Lake)
  4. Data impact — Macie jobs on touched prefixes; check Object Lock / versioning for overwrite attempts
  5. Hardening — tighten SCPs, reduce task-role permissions, enable Network Firewall egress allowlists
sql
-- ✅ CloudTrail Lake follow-up after Detective shows first bad eventTime
SELECT eventTime, eventName, sourceIPAddress, userAgent,
       requestParameters, errorCode
FROM agent_trail_lake
WHERE userIdentity.sessionContext.sessionIssuer.arn LIKE '%coding-agent-tool%'
  AND eventTime BETWEEN timestamp '2026-09-26 08:00:00' AND timestamp '2026-09-26 12:00:00'
ORDER BY eventTime ASC
LIMIT 1000
json
// ✅ force session invalidation on the agent tool role trust policy
{
  "Version": "2012-10-17",
  "Statement": [{
    "Effect": "Deny",
    "Action": "sts:AssumeRole",
    "Principal": {"AWS": "*"},
    "Condition": {
      "StringEquals": {"aws:PrincipalTag/workload": "coding-agent"},
      "DateLessThan": {"aws:TokenIssueTime": "2026-09-26T10:15:00Z"}
    }
  }]
}

(Use the console/API “revoke older sessions” pattern — the AWSRevokeOlderSessions inline deny — rather than inventing fragile hand-rolled trusts.)

Finding groups vs single findings

Detective finding groups are where agent incidents shine. A lone GuardDuty informational finding is noise. A group that ties:

  • credential exfil
  • anomalous S3 GetObject volume
  • CreateKeyPair / RunInstances

…is a campaign. Train on-call to open the group, not acknowledge findings one-by-one in Jira.

Symptom in Detective Likely agent story Next action
New UA + geo on task role Stolen keys used from laptop Revoke + rotate forge secrets
Spike S3 Get on many prefixes Artifact bucket dump Quarantine bucket; Macie scan
Bedrock InvokeModel surge Prompt abuse / cost attack Kill switch + Budgets alarm
IAM CreateUser from agent role Persistence attempt SCP deny; page security

Production checklist

  • [ ] Detective graph enabled Org-wide; agent accounts are members
  • [ ] GuardDuty findings openable into Detective (same Region)
  • [ ] On-call runbook: GuardDuty HIGH → contain → Detective group → Lake SQL
  • [ ] Agent roles tagged (workload=coding-agent, tenant_id=…) for filterability
  • [ ] Session revoke tested quarterly on a canary role
  • [ ] SCPs deny standing CreateAccessKey / logging disable in agent OUs (SCPs)
  • [ ] Findings + investigation notes retained ≥ 90 days for audit
  • [ ] Dual-signal: Detective graph + Budgets/Cost Anomaly when spend spikes with findings

FAQ

Q: Is Detective a replacement for a SIEM?
A: No. It is the AWS-native investigation graph for GuardDuty-centric incidents. Export to your SIEM; use Detective when the finding is AWS-entity-heavy (IAM, S3, EC2, EKS).

Q: Do I need Detective if I already have CloudTrail Lake?
A: Lake is powerful but cold-start. Detective precomputes baselines and finding groups so you do not invent joins under pager pressure. Use both.

Q: How long until baselines are useful?
A: Detective needs a short warm-up of telemetry (days). Enable it before the incident — enabling mid-breach does not rewrite history.

Related reading

Detective is the investigation layer that makes GuardDuty pages actionable for coding-agent fleets: graph the hops, revoke the right sessions, and prove what the attacker touched — then harden so the next finding never becomes a week-long mystery.

Last updated on September 26, 2026

Deep-dive PDF

Get the expanded guide for this post — extra diagrams-style checklists, failure modes, and a production walkthrough. Free when you subscribe to CheatCoders.

Already subscribed? or open the subscribe page.


Discover more from CheatCoders

Subscribe to get the latest posts sent to your email.

Comments

No comments yet. Why don’t you start the discussion?

Leave a comment

No account needed. Name and email are optional.