AWS CloudTrail Threat Hunting with Athena: Query Patterns for Detecting IAM Abuse, Lateral Movement, and Data Exfiltration

Retool's new app builder is where AI-generated code ships safely
Building apps with AI is easy. Getting them to production safely is another story.
CloudTrail is the most complete record of what happened in an AWS account, but querying it at scale requires Athena. The console's 90-day event history is insufficient for threat hunting and too slow for bulk analysis. With Athena configured against your CloudTrail S3 bucket, you can query months of API call history in seconds using SQL. This guide covers the Athena setup and the query patterns that surface the attacker activity most commonly found in AWS compromises.
Setting Up Athena for CloudTrail Queries
Before querying, partition the CloudTrail table for performance:
-- Create the CloudTrail table in Athena
CREATE EXTERNAL TABLE cloudtrail_logs (
eventVersion STRING,
userIdentity STRUCT<
type: STRING,
principalId: STRING,
arn: STRING,
accountId: STRING,
invokedBy: STRING,
accessKeyId: STRING,
userName: STRING,
sessionContext: STRUCT<
sessionIssuer: STRUCT<
type: STRING,
principalId: STRING,
arn: STRING,
accountId: STRING,
userName: STRING
>,
attributes: STRUCT<
mfaAuthenticated: STRING,
creationDate: STRING
>
>
>,
eventTime STRING,
eventSource STRING,
eventName STRING,
awsRegion STRING,
sourceIPAddress STRING,
userAgent STRING,
errorCode STRING,
errorMessage STRING,
requestParameters STRING,
responseElements STRING,
resources ARRAY<STRUCT<arn:STRING,accountId:STRING,type:STRING>>,
recipientAccountId STRING
)
PARTITIONED BY (region STRING, year STRING, month STRING, day STRING)
ROW FORMAT SERDE 'com.amazon.emr.hive.serde.CloudTrailSerde'
STORED AS INPUTFORMAT 'com.amazon.emr.cloudtrail.CloudTrailInputFormat'
OUTPUTFORMAT 'org.apache.hadoop.hive.ql.io.HiveIgnoreKeyTextOutputFormat'
LOCATION 's3://your-cloudtrail-bucket/AWSLogs/YOUR_ACCOUNT_ID/CloudTrail/';
The quickest approach is to use the AWS-provided CloudFormation template that creates the table automatically: in the Athena console, go to Query editor > CloudTrail > Create table. Load partitions for the time range you want to query:
MSCK REPAIR TABLE cloudtrail_logs;
IAM Abuse Detection: Key Creation, Policy Attachment, and Role Assumption
The highest-priority CloudTrail events for IAM abuse detection:
Find new IAM access keys created (possible persistence):
SELECT
eventTime,
userIdentity.userName AS actor,
userIdentity.accessKeyId AS actor_key,
sourceIPAddress,
json_extract_scalar(requestParameters, '$.userName') AS target_user
FROM cloudtrail_logs
WHERE eventName = 'CreateAccessKey'
AND year = '2026' AND month = '06'
ORDER BY eventTime DESC;
New access keys created for users other than the actor are suspicious. An attacker who compromised one IAM key often creates a second key as persistence.
Find inline policy attachments (bypass SCP-controlled managed policies):
SELECT
eventTime,
userIdentity.userName AS actor,
sourceIPAddress,
json_extract_scalar(requestParameters, '$.userName') AS target_user,
json_extract_scalar(requestParameters, '$.policyName') AS policy_name
FROM cloudtrail_logs
WHERE eventName IN ('PutUserPolicy', 'PutRolePolicy', 'PutGroupPolicy')
AND year = '2026' AND month = '06'
ORDER BY eventTime DESC;
Inline policy attachments that grant AdministratorAccess or iam:* permissions are critical findings.
Find cross-account role assumptions:
SELECT
eventTime,
userIdentity.principalId,
userIdentity.arn AS source_identity,
json_extract_scalar(requestParameters, '$.roleArn') AS assumed_role,
sourceIPAddress
FROM cloudtrail_logs
WHERE eventName = 'AssumeRole'
AND userIdentity.accountId != recipientAccountId
AND year = '2026' AND month = '06'
ORDER BY eventTime DESC;
Briefings like this, every morning before 9am.
Threat intel, active CVEs, and campaign alerts, distilled for practitioners. 50,000+ subscribers. No noise.
S3 Data Exfiltration Detection Patterns
S3 GetObject events are the primary indicator of data exfiltration. The challenge is volume: legitimate applications generate millions of GetObject calls. Use anomaly-based queries rather than absolute threshold queries:
Find IAM users or roles accessing S3 buckets they have never accessed before (last 30 days vs prior 30 days):
-- Current 30 days
WITH current_period AS (
SELECT DISTINCT
userIdentity.arn,
json_extract_scalar(requestParameters, '$.bucketName') AS bucket
FROM cloudtrail_logs
WHERE eventName = 'GetObject'
AND eventSource = 's3.amazonaws.com'
AND year = '2026' AND month = '06'
),
-- Prior 30 days baseline
prior_period AS (
SELECT DISTINCT
userIdentity.arn,
json_extract_scalar(requestParameters, '$.bucketName') AS bucket
FROM cloudtrail_logs
WHERE eventName = 'GetObject'
AND eventSource = 's3.amazonaws.com'
AND year = '2026' AND month = '05'
)
SELECT c.arn, c.bucket
FROM current_period c
LEFT JOIN prior_period p ON c.arn = p.arn AND c.bucket = p.bucket
WHERE p.arn IS NULL;
Find high-volume S3 GetObject calls from a single identity in a short window:
SELECT
userIdentity.arn,
sourceIPAddress,
date_trunc('hour', from_iso8601_timestamp(eventTime)) AS hour_bucket,
COUNT(*) AS get_count,
COUNT(DISTINCT json_extract_scalar(requestParameters, '$.bucketName')) AS distinct_buckets
FROM cloudtrail_logs
WHERE eventName = 'GetObject'
AND eventSource = 's3.amazonaws.com'
AND year = '2026' AND month = '06'
GROUP BY 1, 2, 3
HAVING COUNT(*) > 1000
ORDER BY get_count DESC;
Console Login Anomaly Detection
Console logins without MFA, from unusual geolocations, or outside business hours are early indicators of credential compromise:
Find console logins without MFA:
SELECT
eventTime,
userIdentity.userName,
sourceIPAddress,
json_extract_scalar(additionalEventData, '$.MFAUsed') AS mfa_used,
awsRegion
FROM cloudtrail_logs
WHERE eventName = 'ConsoleLogin'
AND json_extract_scalar(additionalEventData, '$.MFAUsed') = 'No'
AND errorCode IS NULL
AND year = '2026' AND month = '06'
ORDER BY eventTime DESC;
Find root account console logins (should be near zero):
SELECT
eventTime,
userIdentity.type,
sourceIPAddress,
json_extract_scalar(additionalEventData, '$.MFAUsed') AS mfa_used
FROM cloudtrail_logs
WHERE eventName = 'ConsoleLogin'
AND userIdentity.type = 'Root'
AND year = '2026' AND month = '06'
ORDER BY eventTime DESC;
Any root login without a documented break-glass reason is a critical finding.
Find API calls from unusual source IPs for a given IAM user (new IP in last 7 days):
WITH baseline AS (
SELECT DISTINCT userIdentity.userName, sourceIPAddress
FROM cloudtrail_logs
WHERE year = '2026' AND month = '05'
AND userIdentity.type = 'IAMUser'
),
recent AS (
SELECT DISTINCT userIdentity.userName, sourceIPAddress
FROM cloudtrail_logs
WHERE year = '2026' AND month = '06'
AND userIdentity.type = 'IAMUser'
)
SELECT r.userName, r.sourceIPAddress AS new_ip
FROM recent r
LEFT JOIN baseline b ON r.userName = b.userName AND r.sourceIPAddress = b.sourceIPAddress
WHERE b.userName IS NULL
AND r.sourceIPAddress NOT LIKE '10.%'
AND r.sourceIPAddress NOT LIKE '172.16.%'
ORDER BY r.userName;
Correlating with GuardDuty and Building a Hunt Workflow
GuardDuty provides pre-built detection rules that surface known-bad patterns (cryptocurrency mining, Tor exit node usage, S3 bucket policy changes, IAM credential exfiltration). CloudTrail provides the full API call context that GuardDuty findings summarize.
When a GuardDuty finding fires, use CloudTrail to expand the investigation:
-- For a GuardDuty finding about a specific IAM principal:
-- Retrieve all API calls by that principal in the surrounding 4-hour window
SELECT
eventTime,
eventName,
eventSource,
sourceIPAddress,
awsRegion,
errorCode,
json_extract_scalar(requestParameters, '$.bucketName') AS s3_bucket,
json_extract_scalar(requestParameters, '$.roleName') AS iam_role
FROM cloudtrail_logs
WHERE userIdentity.arn = 'arn:aws:iam::123456789012:user/compromised-user'
AND eventTime BETWEEN '2026-06-10T14:00:00Z' AND '2026-06-10T18:00:00Z'
ORDER BY eventTime ASC;
Build a threat hunt workflow:
- Weekly: run the high-volume S3 GetObject query and new IAM access key query, review results
- Daily: run the root login and console login without MFA queries, alert on any result
- On-demand for GuardDuty findings: run the principal timeline query for the flagged identity
- Monthly: run the new S3 access query (new buckets accessed by existing identities) as a low-and-slow exfiltration hunt
For automation, use AWS Lambda triggered by an EventBridge schedule to run these queries and send results to a Slack channel or SIEM via SNS.
The bottom line
CloudTrail contains the full timeline of every AWS compromise, but most organizations only look at it after they already know something went wrong. Running the IAM key creation, cross-account role assumption, and high-volume S3 queries weekly surfaces attacker activity that evades real-time alerting. Set up Athena against your CloudTrail S3 bucket, run the eight queries in this guide on a schedule, and treat any result from the root login or console-without-MFA queries as an immediate incident.
Frequently asked questions
How much does it cost to run Athena queries against CloudTrail logs?
Athena charges $5 per terabyte of data scanned. CloudTrail logs are typically 1-5 GB per month per active account. For most organizations, running daily threat hunt queries against 90 days of CloudTrail logs costs under $5 per month. Use partitioning (year/month/day) in your WHERE clause to avoid scanning the entire log history for every query. A query without a date filter against 12 months of logs can scan 10x more data and cost 10x more than a properly partitioned query.
What is the difference between CloudTrail management events and data events?
Management events record control-plane API calls: creating and deleting resources, modifying IAM policies, configuring security groups. They are enabled by default in CloudTrail and cover the IAM abuse and privilege escalation patterns. Data events record data-plane API calls: S3 GetObject, PutObject, Lambda invocations. Data events are not enabled by default and generate much higher volume (potentially billions of events per day for active S3 buckets). Enable data events for your highest-sensitivity S3 buckets and Lambda functions, not for all resources, to keep costs manageable.
Can I use AWS Security Lake instead of Athena for CloudTrail threat hunting?
AWS Security Lake (built on Amazon S3 with OCSF schema normalization) can ingest CloudTrail logs alongside other security data sources and is queryable via Athena. The advantage over raw CloudTrail in Athena is the normalized schema (OCSF) that allows cross-source correlation: you can join CloudTrail events with VPC Flow Logs and Route 53 query logs in a single query. For organizations already using Security Lake, it is the preferred approach. For those who are not, raw CloudTrail in Athena is faster to set up and sufficient for the queries in this guide.
How do I detect if an attacker disabled or modified CloudTrail to cover their tracks?
CloudTrail modifications are themselves logged in CloudTrail (as long as at least one trail was active when the change was made). Query for StopLogging, DeleteTrail, UpdateTrail, and PutEventSelectors events. GuardDuty also has a built-in finding (Stealth:IAMUser/CloudTrailLoggingDisabled) that alerts when logging is stopped. For defense in depth: enable CloudTrail log file integrity validation (detects log file tampering after delivery to S3), use an SNS topic to alert on StopLogging events via a CloudWatch Events rule, and ensure your CloudTrail S3 bucket has MFA delete enabled and Object Lock configured.
What is the most efficient Athena query structure for hunting across months of CloudTrail data?
Partition your CloudTrail S3 data using Athena partition projection on the year/month/day/region path components. Without partitioning, every query scans all objects in the bucket regardless of the time range filter. With partition projection configured correctly, a query with `WHERE year='2026' AND month='06'` scans only June 2026 data. Use CREATE TABLE with `PARTITIONED BY (year string, month string, day string, region string)` and enable partition projection in the table properties. For cost control, always include partition predicates in WHERE clauses and use `LIMIT` during development to avoid scanning full result sets unnecessarily.
How do I detect IAM credential theft via CloudTrail when the attacker uses legitimate credentials?
Credential theft with valid credentials leaves behavioral anomalies rather than outright errors. Key indicators in CloudTrail: API calls from a geographic location or ASN never seen for that principal (use the userAgent and sourceIPAddress fields), API calls at unusual times for the account (create a baseline of normal access hours per principal), API enumeration patterns (GetCallerIdentity followed by ListBuckets, DescribeInstances, ListRoles in rapid succession within minutes of first use), and new access key usage combined with account enumeration. Athena query template: SELECT useridentity.arn, sourceipaddress, eventtime, eventname FROM cloudtrail_logs WHERE eventname IN ('GetCallerIdentity','ListBuckets','ListRoles','DescribeInstances') AND eventtime > '2026-01-01' ORDER BY useridentity.arn, eventtime. Cluster results by ARN and look for enumeration bursts.
Sources & references
Free resources
Critical CVE Reference Card 2025–2026
25 actively exploited vulnerabilities with CVSS scores, exploit status, and patch availability. Print it, pin it, share it with your SOC team.
Ransomware Incident Response Playbook
Step-by-step 24-hour IR checklist covering detection, containment, eradication, and recovery. Built for SOC teams, IR leads, and CISOs.
Get threat intel before your inbox does.
50,000+ security professionals read Decryption Digest for early warnings on zero-days, ransomware, and nation-state campaigns. Free, daily, no spam.
Unsubscribe anytime. We never sell your data.

Founder & Cybersecurity Evangelist, Decryption Digest
Cybersecurity professional with expertise in threat intelligence, vulnerability research, and enterprise security. Covers zero-days, ransomware, and nation-state operations for 50,000+ security professionals every morning.
Win a $2,495 Black Hat pass.
Full-access to Black Hat USA 2026 in Las Vegas. Subscribe free to enter.
