Correlating GuardDuty Findings with CloudTrail: The Signal Gap Most Teams Miss

GuardDuty raises the alert. CloudTrail contains the chain. Most teams never connect the two in time. Here is how Ira closes that gap automatically.

GuardDuty is good at pattern matching. It watches DNS query logs, VPC Flow Logs, and CloudTrail management events, and surfaces findings when behaviour matches a known threat signature. What it does not do — by design — is hand you the full attack chain. That is CloudTrail’s job.

The problem is that most security teams treat these two data sources as separate workflows. GuardDuty findings go to a SIEM or a Slack alert. CloudTrail queries happen separately, later, when someone has time. The gap between the alert and the context window is where attackers complete their objectives.

The anatomy of the gap

Take UnauthorizedAccess:IAMUser/InstanceCredentialExfiltration.OutsideAWS — one of the more actionable GuardDuty findings. It tells you that credentials from an EC2 instance profile were used from an IP address outside AWS. That is a confirmed exfiltration signal.

What the finding does not tell you:

  • Which API calls were made using the exfiltrated credentials
  • Whether those calls succeeded
  • What resources were accessed or modified
  • Whether lateral movement occurred before or after the exfiltration event

All of that is in CloudTrail. The question is which events, across which accounts, in which time window.

A GuardDuty finding has a service.eventLastSeen timestamp and a resource.accessKeyDetails.accessKeyId. Those two values are the join key. Every CloudTrail management event in the surrounding window that references that access key ID is part of the same incident context — regardless of which AWS account it appears in.

What the correlation looks like in practice

In a typical exfiltration scenario, the CloudTrail events that follow a GuardDuty finding look like this:

sts:GetCallerIdentity       — attacker confirms they have working credentials
ec2:DescribeInstances       — attacker enumerates the environment
ec2:DescribeSecurityGroups  — attacker maps network exposure
s3:ListBuckets              — attacker inventories storage
s3:GetObject                — attacker begins exfiltration

These five events, issued from an external IP within minutes of the GuardDuty finding timestamp, paint a clear picture. They also appear individually benign — DescribeInstances is called thousands of times a day in active AWS environments. Single-event alerting would generate noise or miss the sequence entirely. Correlation finds the pattern.

The time window matters. A 15-minute pre-finding window captures setup activity (the attacker testing credentials before triggering the GuardDuty threshold). A 60-minute post-finding window captures what happened next. Tuning this window to your environment’s typical human response time determines how much blast radius context you surface before containment.

How Ira handles this

When a GuardDuty finding lands in the orchestrator, Ira immediately dispatches two sub-agents: one to pull the full finding detail from GuardDuty, including the principal identifiers and the last-seen timestamp, and one to query CloudTrail Insights and management events for that principal across the configured account scope.

The CloudTrail sub-agent filters by:

  • userIdentity.accessKeyId matching the compromised key
  • eventTime within the pre- and post-finding windows
  • errorCode to separate successful API calls from failed ones (failed calls indicate the attacker was probing; successful ones indicate they succeeded)

The results are scored against a risk rubric. A GetCallerIdentity from an unknown IP is low-risk in isolation — it is what any credential test looks like. But a GetCallerIdentity followed by ListBuckets followed by GetObject from the same IP within five minutes scores high, because the sequence indicates deliberate reconnaissance and access, not an accidental credential leak.

This correlation happens automatically, within seconds of the GuardDuty finding. The output is not a raw list of CloudTrail events — it is a scored incident summary with the events ranked by relevance to the finding, separated into reconnaissance activity, resource access, and any configuration modifications.

The decision the operator sees

By the time a human looks at the incident, the question has changed. It is no longer “what happened?” — Ira has already answered that. The question is “do I approve the containment recommendation?”

In the exfiltration scenario, the containment recommendation is typically: revoke the compromised access key, quarantine the EC2 instance by attaching a deny-all security group, and flag any S3 objects accessed for a data impact assessment. All three are staged as AWS API calls waiting for human approval before execution.

That is the workflow the gap was costing. Not the alert — the twenty minutes of manual CloudTrail querying between the alert and the moment someone understood what to do next.