You already saw CloudTrail event structure in M02. This lesson goes deeper: how to configure trails for comprehensive coverage, what the common attack patterns look like in CloudTrail, and how to write detections against the API-call model.
🔍 Find your organization's CloudTrail gaps
If you have AWS access, run:
aws cloudtrail describe-trails --region us-east-1
aws cloudtrail get-trail-status --name < your-trail-nam e > Questions to answer:
Is MultiRegionTrail: true?
Is IncludeGlobalServiceEvents: true? (IAM events are global)
Is LogFileValidationEnabled: true?
Are data events enabled for any S3 buckets?
If the answer to any of these is ‘no’, document the detection gap.
Attempt this before reading on. Getting stuck is the point.
CloudTrail Configuration: What You Must Enable
1. Multi-Region Trail
aws cloudtrail create-trail \
--name org-security-trail \
--s3-bucket-name my-cloudtrail-logs \
--is-multi-region-trail \
--include-global-service-events \
--enable-log-file-validation
Multi-region (--is-multi-region-trail): Captures activity in all regions. Required because attackers use non-standard regions to evade region-specific trails.
Global service events (--include-global-service-events): Captures IAM, STS, CloudFront API calls. IAM calls are “global” — they’re not region-specific. Without this, you miss iam:CreateUser, iam:AttachRolePolicy, and most privilege escalation API calls.
Log file validation (--enable-log-file-validation): Creates SHA-256 digests for each log file delivery. Allows you to detect if someone tampered with CloudTrail log files in S3.
2. Data Events (S3 + Lambda)
# Enable data events for all S3 buckets
aws cloudtrail put-event-selectors \
--trail-name org-security-trail \
--event-selectors '[{
"ReadWriteType": "All",
"IncludeManagementEvents": true,
"DataResources": [{
"Type": "AWS::S3::Object",
"Values": ["arn:aws:s3:::"]
}]
}]'
Data events capture: s3:GetObject, s3:PutObject, s3:DeleteObject, lambda:InvokeFunction. Required for exfiltration detection.
Data events cost money — prioritize sensitive buckets
Enabling data events for all S3 objects can cost hundreds of dollars per month for organizations with millions of S3 operations. Prioritize: enable data events for buckets containing sensitive data (PII, financial records, credentials, config files) first. Log s3:GetObject (read) for exfiltration detection, s3:PutObject (write) for data tampering detection.
Attack Patterns in CloudTrail
Pattern 1: Credential Reconnaissance (T1078.004)
/* Multiple AccessDenied errors across services — permissions probing */
{ "eventTime" : "09:23:14Z" , "eventName" : "DescribeInstances" , "errorCode" : "AccessDenied" , "eventSource" : "ec2.amazonaws.com" }
{ "eventTime" : "09:23:16Z" , "eventName" : "ListBuckets" , "errorCode" : "AccessDenied" , "eventSource" : "s3.amazonaws.com" }
{ "eventTime" : "09:23:18Z" , "eventName" : "ListUsers" , "errorCode" : "AccessDenied" , "eventSource" : "iam.amazonaws.com" }
{ "eventTime" : "09:23:20Z" , "eventName" : "GetCallerIdentity" , "errorCode" : null , "eventSource" : "sts.amazonaws.com" }
GetCallerIdentity succeeds (no IAM permission required, always works). The three preceding calls failed. This is an attacker mapping what permissions their stolen credential has.
Detection: Count of distinct errorCode: 'AccessDenied' events from the same principal over a short window, especially followed by a successful call.
Pattern 2: Persistence via IAM (T1098.001)
/* Sequence: create user → attach admin policy → create access key */
{ "eventName" : "CreateUser" , "requestParameters" : { "userName" : "support-bot" }}
{ "eventName" : "AttachUserPolicy" , "requestParameters" : { "userName" : "support-bot" , "policyArn" : "arn:aws:iam::aws:policy/AdministratorAccess" }}
{ "eventName" : "CreateAccessKey" , "requestParameters" : { "userName" : "support-bot" }}
Each call alone could be legitimate. The sequence — create → grant admin → generate API key — is the attack.
Worked Example
Detection: IAM user created then immediately given admin access -- CloudTrail Lake query: find users created and given admin access within 5 minutes
SELECT
e1 . userIdentity .arn as caller ,
e1 . requestParameters .userName as new_user,
e2 . requestParameters .policyArn as policy_attached,
e1 . eventTime as create_time,
e2 . eventTime as policy_attach_time
FROM
cloudtrail_events e1
JOIN cloudtrail_events e2 ON
e1 . requestParameters .userName = e2 . requestParameters .userName
AND TIMESTAMPDIFF( MINUTE , e1 . eventTime , e2 . eventTime ) BETWEEN 0 AND 5
WHERE
e1 . eventName = 'CreateUser'
AND e2 . eventName = 'AttachUserPolicy'
AND e2 . requestParameters .policyArn LIKE '%AdministratorAccess%'
ORDER BY
e1 . eventTime DESC This query is a simplified example of temporal correlation — the key detection technique for multi-step cloud attack chains.
Pattern 3: Console Login Without MFA
{
"eventName" : "ConsoleLogin" ,
"userIdentity" : { "type" : "IAMUser" , "userName" : "admin-jdoe" },
"additionalEventData" : {
"MFAUsed" : "No" ,
"LoginTo" : "https://console.aws.amazon.com/console/home"
},
"responseElements" : { "ConsoleLogin" : "Success" },
"sourceIPAddress" : "203.0.113.55"
}
Admin account logging in without MFA from an external IP. This should alert immediately.
Reading CloudTrail userIdentity Types
type Who made the call Detection note IAMUserDirect IAM user with long-term credentials Most common legitimate auth AssumedRoleShort-term credentials via STS AssumeRole Check role chain + session duration RootAWS root account Should almost never appear — alert immediately AWSServiceAWS service acting on your behalf (e.g., Lambda calling S3) Normal; filter by service principal in exclude rules AWSAccountCross-account access Verify the source account is trusted
Root activity in CloudTrail is a top-priority alert
AWS root credentials should be protected with MFA and used only for account-level operations (billing, account closure). Any CloudTrail event with userIdentity.type: 'Root' should be an automatic high-severity alert. There is no legitimate use of root credentials for routine operations — seeing root activity means either an authorized admin doing something unusual or a complete account compromise.
AWS CloudTrail User Guide— AWS Docs
Stratus Red Team— Datadog Security Labs
Retrieval Practice
An attacker with compromised IAM credentials runs: `aws sts get-caller-identity`. This call always succeeds regardless of permissions. Why would an attacker run this as their first action, and what CloudTrail fields tell you everything about who they are?
Reveal answer
GetCallerIdentity is the cloud equivalent of 'whoami' — it returns the ARN, Account ID, and UserId of the calling principal. It requires no IAM permissions. Attackers run it immediately after obtaining credentials to confirm: (1) which account the credentials belong to, (2) what type of principal (IAM user vs role), (3) whether the credentials are still valid (if the call succeeds, they work). CloudTrail fields for attribution: userIdentity.arn (full ARN of the caller), userIdentity.accountId (which AWS account), sourceIPAddress (where the call came from — compare to known IPs for this principal), userAgent (aws-cli/2.x vs boto3 vs Postman — tool fingerprinting for threat hunting).
Retrieval Practice
You want to detect when a Lambda function is invoked with unusual parameters that could indicate it's being used for data exfiltration. Which CloudTrail configuration must be enabled, and what fields in the event would you analyze?
Reveal answer
Lambda invocation data events must be enabled in the CloudTrail event selector (DataResources with Type: 'AWS::Lambda::Function'). By default, Lambda invocations do not appear in CloudTrail. Once enabled, the event fields: eventName='Invoke', requestParameters.functionName (which Lambda), requestParameters.qualifier (version/alias), responseElements (HTTP status of invocation), userIdentity (who invoked it). The payload itself is NOT logged in CloudTrail — you'd need Lambda execution logs (CloudWatch Logs) to see what the function did. For exfiltration detection, correlate: Lambda Invoke (CloudTrail) → S3:GetObject by the Lambda's execution role (S3 data events) within the same timeframe.