Correlating GuardDuty Findings with CloudTrail: The Signal Gap Most Teams Miss
4 min readGuardDuty tells you something happened. CloudTrail tells you what happened next. Most teams treat these as separate workflows — and that gap is where attackers complete their objectives.
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.
Automating the correlation
When a GuardDuty finding lands, the correlation workflow needs two things immediately: the full finding detail from GuardDuty (principal identifiers, last-seen timestamp), and a CloudTrail query for that principal across the relevant account scope.
The CloudTrail query filters by:
userIdentity.accessKeyIdmatching the compromised keyeventTimewithin the pre- and post-finding windowserrorCodeto separate successful API calls from failed ones (failed calls indicate the attacker was probing; successful ones indicate they succeeded)
The results should be 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 should happen automatically, within seconds of the GuardDuty finding. The output should not be a raw list of CloudTrail events — it should be 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 should see
By the time a human looks at the incident, the question should have changed. It is no longer “what happened?” — the correlation has already answered that. The question is “what do I do about it?”
In the exfiltration scenario, the containment actions are 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.
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.
Building this correlation into your detection pipeline — whether through automation tooling, a SOAR platform, or even a well-structured Lambda function triggered by EventBridge — is the difference between GuardDuty being a useful signal and GuardDuty being a starting point for a manual investigation that may or may not happen before the attacker finishes what they started.
