Cloud Security Wire
AWS Azure GCP RSS
AWS Hardening Guide high

AWS CloudTrail Threat Hunting: Key Events for Detecting Credential Theft and Lateral Movement

CloudTrail logs every API call against your AWS account — but knowing which events signal an active attack versus routine operations is the hard part. This guide covers the CloudTrail event patterns that matter most for detecting credential theft, privilege escalation, persistence, and lateral movement.

By Cloud Security Wire · ·
#cloudtrail#threat-hunting#aws#credential-theft#lateral-movement#privilege-escalation#detection#iam#siem#kql#athena
High Severity

This issue has been assessed as high severity. Review affected configurations immediately.

CloudTrail records every API call made against your AWS account, across every service and region. That’s an enormous volume of data — and most of it is routine. The skill in AWS cloud threat hunting is knowing which events, combinations, and sequences indicate an attacker operating in your environment rather than a legitimate workload.

This guide covers the specific CloudTrail event patterns that matter most for detecting the phases of an AWS attack chain: initial access via credential theft, privilege escalation, persistence, and lateral movement.

Setting Up for Effective Hunting

Before hunting, ensure CloudTrail is actually capturing what you need:

# Verify CloudTrail is enabled across all regions with S3 data events
aws cloudtrail describe-trails --include-shadow-trails \
  --query 'trailList[*].[Name,IsMultiRegionTrail,IncludeGlobalServiceEvents,S3BucketName]'

# Check if data events (S3 object-level, Lambda invoke) are enabled
aws cloudtrail get-event-selectors --trail-name <trail-name>

Without data events enabled, CloudTrail captures management events (API calls) but misses object-level S3 reads and Lambda invocations — both of which are critical for detecting exfiltration.

For serious threat hunting, route CloudTrail to Athena or a SIEM. Athena lets you run SQL against logs stored in S3 without ingesting them into a paid logging tier:

-- Athena setup: partition projection for CloudTrail logs
CREATE EXTERNAL TABLE cloudtrail_logs (
  eventversion STRING, useridentity STRUCT<...>, eventtime STRING,
  eventsource STRING, eventname STRING, awsregion STRING,
  sourceipaddress STRING, errorcode STRING, errormessage STRING,
  requestparameters STRING, responseelements STRING
)
PARTITIONED BY (account STRING, region STRING, year INT, month INT, day INT)
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/';

Phase 1: Detecting Credential Compromise and Initial Access

Unusual GetCallerIdentity Calls

sts:GetCallerIdentity is frequently the first API call an attacker makes after obtaining credentials — it’s how they verify the credentials work and determine what account they’re in. Legitimate automation rarely calls this without purpose.

SELECT eventtime, sourceipaddress, useridentity.arn, useragent
FROM cloudtrail_logs
WHERE eventname = 'GetCallerIdentity'
  AND sourceipaddress NOT IN (known_corporate_ips)
  AND useragent NOT LIKE '%aws-sdk%'
ORDER BY eventtime DESC;

ConsoleLogin from Unexpected Locations

SELECT eventtime, sourceipaddress, useridentity.username,
       json_extract_scalar(additionaleventdata, '$.MFAUsed') AS mfa_used,
       errorcode
FROM cloudtrail_logs
WHERE eventname = 'ConsoleLogin'
  AND eventsource = 'signin.amazonaws.com'
  AND errorcode IS NULL  -- successful logins
ORDER BY eventtime DESC;

Flag successful logins without MFA, from unfamiliar IPs, or at unusual hours.

Secrets Manager and SSM Parameter Access

Credential harvesting often targets Secrets Manager and SSM Parameter Store — attackers know that’s where databases, API keys, and service credentials live.

SELECT eventtime, useridentity.arn, eventname, sourceipaddress,
       json_extract_scalar(requestparameters, '$.secretId') AS secret_accessed
FROM cloudtrail_logs
WHERE eventsource = 'secretsmanager.amazonaws.com'
  AND eventname IN ('GetSecretValue', 'ListSecrets', 'DescribeSecret')
ORDER BY eventtime DESC;

A single principal listing all secrets followed by rapid GetSecretValue calls against multiple secrets is a high-confidence indicator of credential harvesting.

Phase 2: Detecting Privilege Escalation

IAM Policy Modifications

SELECT eventtime, useridentity.arn, eventname, sourceipaddress,
       requestparameters
FROM cloudtrail_logs
WHERE eventsource = 'iam.amazonaws.com'
  AND eventname IN (
    'CreatePolicy', 'CreatePolicyVersion', 'PutUserPolicy', 'AttachUserPolicy',
    'PutRolePolicy', 'AttachRolePolicy', 'AddUserToGroup', 'CreateRole',
    'UpdateAssumeRolePolicy', 'PassRole'
  )
ORDER BY eventtime DESC;

Pay particular attention to CreatePolicyVersion with setAsDefault=true — this is the canonical privilege escalation path where an attacker with iam:CreatePolicyVersion on a policy attached to a privileged role can silently add iam:* or AdministratorAccess to themselves.

Detecting PassRole Abuse

SELECT eventtime, useridentity.arn, requestparameters,
       json_extract_scalar(requestparameters, '$.roleName') AS role_passed
FROM cloudtrail_logs
WHERE eventname = 'PassRole'
  AND eventsource = 'iam.amazonaws.com'
ORDER BY eventtime DESC;

Cross-reference the role being passed against a list of highly-privileged roles. PassRole of an AdminRole to a new EC2 instance or Lambda function is a reliable privilege escalation indicator.

AdministratorAccess Policy Attachments

SELECT eventtime, useridentity.arn, eventname,
       json_extract_scalar(requestparameters, '$.policyArn') AS policy_arn
FROM cloudtrail_logs
WHERE eventname IN ('AttachUserPolicy', 'AttachRolePolicy', 'AttachGroupPolicy')
  AND requestparameters LIKE '%AdministratorAccess%'
ORDER BY eventtime DESC;

Any attachment of arn:aws:iam::aws:policy/AdministratorAccess outside of a known provisioning workflow is critical.

Phase 3: Detecting Persistence

New IAM Users, Keys, and Access Tokens

SELECT eventtime, useridentity.arn, eventname, sourceipaddress,
       json_extract_scalar(requestparameters, '$.userName') AS new_user
FROM cloudtrail_logs
WHERE eventsource = 'iam.amazonaws.com'
  AND eventname IN ('CreateUser', 'CreateAccessKey', 'CreateLoginProfile',
                    'CreateGroup', 'AddUserToGroup')
ORDER BY eventtime DESC;

Lambda Backdoor Deployment

Attackers persist via Lambda functions triggered by events, which run with IAM roles they’ve granted themselves. Detecting new Lambda deployments from unexpected principals:

SELECT eventtime, useridentity.arn, eventname, awsregion,
       json_extract_scalar(requestparameters, '$.functionName') AS function_name,
       json_extract_scalar(requestparameters, '$.role') AS execution_role
FROM cloudtrail_logs
WHERE eventsource = 'lambda.amazonaws.com'
  AND eventname IN ('CreateFunction20150331', 'UpdateFunctionCode20150331v2',
                    'AddPermission20150331v2')
ORDER BY eventtime DESC;

CloudShell and Systems Manager Session Manager

Interactive shell access via CloudShell or SSM Session Manager creates a foothold that doesn’t require SSH keys or open security group ports:

SELECT eventtime, useridentity.arn, sourceipaddress, eventname
FROM cloudtrail_logs
WHERE (eventsource = 'cloudshell.amazonaws.com' AND eventname = 'CreateEnvironment')
   OR (eventsource = 'ssm.amazonaws.com' AND eventname = 'StartSession')
ORDER BY eventtime DESC;

Phase 4: Detecting Exfiltration and Lateral Movement

S3 Bulk Downloads

Data events must be enabled in your trail for this query:

SELECT eventtime, useridentity.arn, sourceipaddress,
       json_extract_scalar(requestparameters, '$.bucketName') AS bucket,
       count(*) AS download_count
FROM cloudtrail_logs
WHERE eventname = 'GetObject'
  AND eventsource = 's3.amazonaws.com'
  AND errorcode IS NULL
GROUP BY eventtime, useridentity.arn, sourceipaddress,
         json_extract_scalar(requestparameters, '$.bucketName')
HAVING count(*) > 100
ORDER BY download_count DESC;

Cross-Account Role Assumptions

Lateral movement in AWS often involves chaining role assumptions across accounts in the same organisation:

SELECT eventtime, useridentity.arn AS source_identity,
       json_extract_scalar(requestparameters, '$.roleArn') AS assumed_role,
       json_extract_scalar(requestparameters, '$.roleSessionName') AS session_name,
       sourceipaddress
FROM cloudtrail_logs
WHERE eventname = 'AssumeRole'
  AND eventsource = 'sts.amazonaws.com'
  AND errorcode IS NULL
  -- Flag cross-account assumptions (different account ID in the role ARN)
  AND json_extract_scalar(requestparameters, '$.roleArn') NOT LIKE '%:ACCOUNT_ID:%'
ORDER BY eventtime DESC;

High-Priority Alert Combinations

The individual queries above help with investigation. For alerting, prioritise these combinations:

CombinationWhy it matters
GetCallerIdentityListRolesCreatePolicyVersion within 5 minClassic recon → escalation sequence
ConsoleLogin from new IP without MFA + IAM changes within 30 minCompromised credential with immediate escalation
CreateAccessKey on an existing high-privilege userBackdoor access key creation
PutBucketPolicy making bucket public + GetObject spikeExfiltration via S3 public exposure
AssumeRole from Lambda or EC2 to unrelated account + ListSecretsCross-account lateral movement to credential harvest

Operationalising the Queries

For a team without a full SIEM, the minimum viable setup:

  1. CloudTrail to S3 — enabled globally, multi-region, including global services
  2. Athena on the CloudTrail S3 bucket — free tier covers moderate query volumes
  3. CloudWatch Alarms on the highest-priority events (new access key, policy attachment, public bucket) with SNS notification to a security email or Slack channel
  4. AWS Security Hub — aggregates GuardDuty, Config, and IAM Access Analyzer findings; provides a single pane for common detection logic without custom queries

AWS GuardDuty covers several of these patterns natively (credential use from unusual locations, enumeration calls, S3 exfiltration patterns). CloudTrail hunting complements GuardDuty by enabling custom logic, longer-horizon correlation, and investigation of events GuardDuty doesn’t flag.

← All Analysis Subscribe via RSS