"Action": "*"
The most dangerous three characters in AWS
41
Documented AWS IAM privilege escalation paths (Rhino Security Labs)
PassRole
The most commonly abused IAM permission for privilege escalation
IAM Access Analyzer
AWS-native tool that validates policies against least privilege

AWS IAM is the access control system that determines what every user, application, and service in your AWS environment can do. It is also where most cloud security failures originate: overly permissive policies, wildcard actions, and missing resource restrictions create privilege escalation paths that attackers exploit to move from a limited initial access point (a compromised Lambda function, a leaked CI/CD service account key) to full administrative control of an AWS account. Writing correct IAM policies is a precision skill -- the difference between a policy that grants exactly what is needed and one that grants the ability to create new admin users and cover tracks is often a single missing resource restriction or a misplaced wildcard. This guide covers IAM policy mechanics, common dangerous patterns, privilege escalation paths, and practical policy templates for common use cases.

IAM Policy Types and Evaluation Logic

Understanding how AWS evaluates multiple policies is essential for writing policies that produce the intended behavior.

Policy types:

  • Identity-based policies: Attached to IAM users, groups, or roles. Define what that identity is allowed to do.
  • Resource-based policies: Attached to AWS resources (S3 buckets, SQS queues, KMS keys, Lambda functions). Define who can access the resource and what they can do with it.
  • Permission boundaries: An IAM managed policy set as a boundary on a user or role. The effective permissions are the intersection of the boundary and the identity-based policy -- permissions outside the boundary are denied even if granted by an identity policy.
  • Service control policies (SCPs): Applied to AWS accounts or OUs in AWS Organizations. Define the maximum permissions available to all principals in the account -- even the root user cannot exceed SCP limits.
  • Session policies: Passed when assuming a role or creating a federated session via AssumeRole. Limit the permissions of the resulting session to a subset of the role's policies.

Policy evaluation order: When a request is made, AWS evaluates all applicable policies in this order:

  1. If an explicit Deny exists in any policy, the request is denied (Deny always overrides Allow)
  2. If an Allow exists in an identity-based or resource-based policy, the request is allowed (in the same account)
  3. If no Allow exists, the request is denied by default (implicit deny)

The critical implication: you cannot grant access that has been explicitly denied by an SCP or permission boundary. SCPs and boundaries are the authoritative limit on what any principal in an account or with a boundary can do, regardless of what other policies grant.

Why SCPs are the most important IAM security control: SCPs applied to AWS Organization OUs enforce account-wide guardrails that cannot be overridden by any identity policy, including the root user. Common SCP guardrails: prevent disabling CloudTrail, prevent removing GuardDuty, prevent creating IAM users (force federation), prevent regions outside approved regions. SCPs are the control that protects against compromised administrator credentials doing catastrophic damage.

The Least Privilege Spectrum: From AdministratorAccess to Minimal Scope

IAM policies range from the maximally permissive to the precisely scoped. Understanding what each level permits helps choose the right scope for each use case.

AdministratorAccess (never use for service accounts):

{
  "Effect": "Allow",
  "Action": "*",
  "Resource": "*"
}

This grants complete control of the AWS account. Appropriate only for break-glass emergency access with tight controls and mandatory MFA. Never assigned as a standing permission for service accounts, CI/CD roles, or developer roles.

PowerUser (service control, not administrator): Grants all AWS service actions except IAM management. Better than Administrator but still grants the ability to access all data in the account, create resources without budget controls, and perform nearly any action short of creating new IAM principals.

Service-specific wildcard (common but often overpermissive):

{
  "Effect": "Allow",
  "Action": "s3:*",
  "Resource": "*"
}

Grants all S3 actions on all S3 resources in the account. If this policy is attached to a Lambda function, a compromised function can read every S3 bucket in the account. Scope to specific buckets and actions.

Resource-scoped policy (the target for most use cases):

{
  "Effect": "Allow",
  "Action": [
    "s3:GetObject",
    "s3:PutObject"
  ],
  "Resource": "arn:aws:s3:::my-application-bucket/*"
}

Grants only read and write (not delete, not list, not policy management) on a specific bucket's objects. A compromise of this role cannot access other buckets, cannot list bucket contents, and cannot modify bucket policies.

The principle of minimal scope: When writing a policy for a new service or role, start by denying everything and add only the specific actions the service needs to perform its documented function. Use CloudTrail logs (with IAM access advisor) to identify what actions the service actually calls after a week of operation, then trim to only those actions.

Free daily briefing

Briefings like this, every morning before 9am.

Threat intel, active CVEs, and campaign alerts, distilled for practitioners. 50,000+ subscribers. No noise.

IAM Privilege Escalation: The 41 Paths and How to Block Them

Rhino Security Labs documented 41 IAM privilege escalation paths -- ways that a user or role with seemingly limited permissions can grant themselves or others additional permissions. Understanding the most common paths informs which IAM permissions require the most careful scoping.

iam:PassRole -- the most abused permission: iam:PassRole allows a principal to pass an IAM role to an AWS service (EC2 instance, Lambda function, ECS task). If a developer role has iam:PassRole without resource restriction, they can create a Lambda function and pass it a role with higher permissions than their own -- then invoke the Lambda to perform actions they could not perform directly.

Correctly scoped:

{
  "Effect": "Allow",
  "Action": "iam:PassRole",
  "Resource": "arn:aws:iam::123456789012:role/specific-lambda-execution-role"
}

Never grant iam:PassRole with "Resource": "*".

iam:CreatePolicyVersion: If a principal can create a new version of an existing managed policy and set it as the default, they can modify any policy they have this permission on -- potentially adding full administrator access to a policy attached to their own role.

iam:CreateLoginProfile / iam:UpdateLoginProfile: Allows creating or changing the console password for an IAM user. A principal with this permission on another user account can set a new password and log in as that user.

iam:AttachUserPolicy / iam:AttachRolePolicy: Allows attaching any managed policy to a user or role. A principal with this permission and access to the AdministratorAccess managed policy can attach it to themselves.

ec2:AssociateIamInstanceProfile: Allows associating an IAM instance profile with an EC2 instance. If a principal can launch EC2 instances and associate a high-privilege instance profile, they can access EC2 instance metadata to retrieve the role's credentials.

Blocking escalation paths in practice: Use IAM Access Analyzer to scan all policies for overly permissive IAM management permissions. Apply permission boundaries on developer roles: a boundary policy that excludes all iam:* actions prevents developers from escalating privileges even if they can write their own IAM policies. Use SCPs to prevent granting AdministratorAccess in non-administrative accounts.

Condition Keys: MFA, IP Restrictions, and Temporal Controls

IAM condition keys allow adding contextual requirements to Allow and Deny statements -- the policy only applies when the specified condition is met.

MFA requirement for sensitive operations:

{
  "Effect": "Deny",
  "Action": [
    "iam:*",
    "s3:DeleteBucket",
    "ec2:TerminateInstances"
  ],
  "Resource": "*",
  "Condition": {
    "BoolIfExists": {
      "aws:MultiFactorAuthPresent": "false"
    }
  }
}

This Deny statement blocks destructive and IAM management actions unless the session was authenticated with MFA. Use BoolIfExists (not Bool) to handle API calls using access keys (where MFA context is absent) -- BoolIfExists evaluates to true (condition met) when the key is missing, so the Deny applies to API key sessions that lack MFA context.

IP-based restrictions (for static egress environments):

{
  "Effect": "Deny",
  "Action": "*",
  "Resource": "*",
  "Condition": {
    "NotIpAddress": {
      "aws:SourceIp": ["203.0.113.0/24", "198.51.100.0/24"]
    }
  }
}

Restricts API calls to originate from specific IP ranges -- useful for service accounts with known, static source IPs (data center NAT gateways, office egress IPs). Not appropriate for developer roles where users access AWS from variable locations.

Principal-level conditions (for cross-account trust): When a resource-based policy allows cross-account access, use aws:PrincipalOrgID to restrict access to principals within your AWS Organization, preventing public access even if the ARN pattern is overly broad:

{
  "Condition": {
    "StringEquals": {
      "aws:PrincipalOrgID": "o-exampleorgid"
    }
  }
}

Time-based restrictions: Condition keys like aws:CurrentTime can restrict API access to business hours -- useful for service accounts that should only be active during maintenance windows. Combined with automated rotation, this limits the window an attacker can use a compromised key.

Practical Policy Patterns for Common Use Cases

CI/CD deployment role (least-privilege for typical application deployment):

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "s3:PutObject",
        "s3:GetObject",
        "cloudfront:CreateInvalidation"
      ],
      "Resource": [
        "arn:aws:s3:::my-app-assets-bucket/*",
        "arn:aws:cloudfront::123456789012:distribution/EDFDVBD6EXAMPLE"
      ]
    },
    {
      "Effect": "Allow",
      "Action": [
        "lambda:UpdateFunctionCode",
        "lambda:GetFunction"
      ],
      "Resource": "arn:aws:lambda:us-east-1:123456789012:function:my-app-*"
    }
  ]
}

This CI/CD role can deploy to a specific S3 bucket, invalidate a specific CloudFront distribution, and update Lambda functions matching a naming pattern. It cannot create new IAM roles, access other buckets, or modify infrastructure.

Read-only audit role: Attach AWS managed policy ReadOnlyAccess as the base. Add a Deny for actions that expose sensitive data even with read-only access: secretsmanager:GetSecretValue, ssm:GetParameter with WithDecryption, and kms:Decrypt. A read-only role should be able to see that a secret exists but not read its value.

Developer role with permission boundary: Grant developers a broad set of service creation permissions (EC2, RDS, Lambda, S3) but attach a permission boundary that excludes: iam:CreateRole, iam:AttachRolePolicy, iam:PassRole on anything other than pre-approved roles, and organizations:*. The boundary prevents privilege escalation even if developers can write their own IAM policies within their allowed scope.

IAM Access Analyzer for continuous policy validation: Enable IAM Access Analyzer in every AWS account. It continuously scans resource-based policies (S3 bucket policies, SQS policies, KMS key policies, Lambda function policies) for external access grants -- any resource accessible from outside your account generates a finding. Archive expected findings (public S3 buckets for static website hosting) and treat any new external access finding as a security incident requiring immediate investigation.

The bottom line

AWS IAM policy writing is where cloud security is won or lost at the foundational level. The most common failure is not malicious misconfiguration -- it is the convenience of wildcards. Action: * and Resource: * are written because they work immediately without the friction of researching exact action names and resource ARN formats. That convenience transfers every data access, infrastructure modification, and IAM privilege escalation path to anyone who compromises the identity holding that policy. Invest the upfront time to scope policies to exact actions and resource ARNs, use permission boundaries on developer roles to prevent escalation regardless of what policies they can write, and deploy IAM Access Analyzer to catch external access grants before they become incidents.

Frequently asked questions

What is the difference between an IAM role and an IAM user?

An IAM user is a permanent identity with long-term credentials (access key ID and secret, or console password) associated with a specific person or application. An IAM role is a temporary identity that is assumed by a trusted entity (an EC2 instance, a Lambda function, another AWS account, or a federated identity from an identity provider like Okta or Entra ID) and provides short-lived credentials that expire automatically. AWS security best practices recommend avoiding IAM users for service accounts and using IAM roles instead, because role credentials are temporary (cannot be permanently leaked) and roles can be assumed by multiple entities without credential sharing. For human access, use IAM Identity Center (formerly SSO) with federation from your identity provider rather than IAM users.

What is iam:PassRole and why is it dangerous?

iam:PassRole allows a principal to attach an IAM role to an AWS service when creating or modifying that service. For example, when creating a Lambda function, you specify an execution role -- this is a PassRole action. The danger is that PassRole without resource restriction allows a principal to pass any role in the account to any service they can create. If a developer with limited permissions has unrestricted PassRole, they can create a Lambda function, pass it an Administrator role, invoke the Lambda, and perform actions as that Administrator role -- escalating their own privileges to Administrator. Always restrict PassRole to specific role ARNs: the roles the principal legitimately needs to assign to services they create.

How does IAM Access Analyzer help with least privilege?

IAM Access Analyzer provides two capabilities relevant to least privilege. First, external access analysis: it continuously scans resource-based policies and generates findings for any resource accessible from outside your AWS account or organization -- identifying unintentionally public resources. Second, policy validation and generation: the IAM console's policy editor integrates Access Analyzer to flag policy errors and suggest improvements. The Access Analyzer policy generation feature can analyze CloudTrail logs for a specific role over a defined period and generate a least-privilege policy based on the actions that role actually called -- dramatically reducing the effort of writing precise policies for established roles.

What are Service Control Policies (SCPs) and when should I use them?

SCPs are AWS Organizations policies applied to accounts or organizational units (OUs) that define the maximum permissions available to all principals in those accounts -- including the root user. SCPs do not grant permissions; they limit what identity-based policies can grant. Use SCPs to enforce account-wide guardrails: prevent disabling CloudTrail or GuardDuty, restrict deployments to approved AWS regions, prevent creation of IAM users (force federation through IAM Identity Center), block public S3 bucket creation, and prevent removal of security services. SCPs are the most powerful IAM security control because they cannot be overridden by any identity in the account, making them appropriate for enforcing organizational security policies that must not be circumventable.

How do I audit what IAM permissions are actually being used?

Use two AWS tools in combination. IAM Access Advisor (in the IAM console under any user or role) shows the last time each service was accessed by that principal -- services not accessed in 90+ days are candidates for permission removal. IAM Access Analyzer policy generation analyzes CloudTrail logs for a specific role over a defined period (up to 90 days) and generates a policy containing only the actions that role actually invoked. This generated policy is the empirical least-privilege baseline for that role. Review and approve the generated policy with a security engineer before replacing the existing policy, as some infrequently-used actions (disaster recovery procedures, quarterly audit processes) may not appear in a 90-day CloudTrail window.

What is a permission boundary and when should I use it?

A permission boundary is an IAM managed policy attached to a user or role that defines the maximum permissions that identity can have -- the intersection of the boundary and the identity-based policies determines effective permissions. Use permission boundaries on developer roles and CI/CD roles that have broad service creation permissions but should not be able to escalate privileges or modify IAM policies. For example, a developer role can have PowerUser-level permissions for creating application resources, but a permission boundary that excludes all iam:Create*, iam:Attach*, and iam:PassRole actions prevents that developer from using their broad permissions to grant themselves or others additional access. Permission boundaries are the primary mechanism for safely delegating broad resource creation permissions without enabling privilege escalation.

Sources & references

  1. AWS: IAM Policy Reference
  2. AWS: IAM Access Analyzer Policy Validation
  3. AWS: Security Best Practices in IAM
  4. Rhino Security Labs: AWS IAM Privilege Escalation Methods

Free resources

25
Free download

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.

No spam. Unsubscribe anytime.

Free download

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.

No spam. Unsubscribe anytime.

Free newsletter

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, weekly, no spam.

Unsubscribe anytime. We never sell your data.

Eric Bang
Author

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 weekly.

Free Brief

The Mythos Brief is free.

AI that finds 27-year-old zero-days. What it means for your security program.

Joins Decryption Digest. Unsubscribe anytime.

Daily Briefing

Get briefings like this every morning

Actionable threat intelligence for working practitioners. Free. No spam. Trusted by 50,000+ SOC analysts, CISOs, and security engineers.

Unsubscribe anytime.

Mythos Brief

Anthropic's AI finds zero-days your scanners miss.