Skip to main content

IAM Policy Allows Privilege Escalation

Overview

This check identifies customer-managed IAM policies that contain permissions enabling privilege escalation. Privilege escalation occurs when a user or role can grant themselves (or others) more permissions than originally intended.

Common problematic permissions include:

  • Creating or updating IAM policies
  • Attaching policies to users, groups, or roles
  • Modifying role trust relationships
  • Using iam:PassRole with broad scope

Risk

If this check fails, an attacker who compromises a principal with these permissions could:

  • Access sensitive data by assuming higher-privilege roles
  • Modify security controls by editing policies or configurations
  • Cover their tracks by tampering with logs
  • Cause outages by deleting resources or disabling protections

This is a high-severity finding because it can turn a limited compromise into full account takeover.

Remediation Steps

Prerequisites

You need permission to view and edit IAM policies in your AWS account. Typically this means IAM administrator access or equivalent.

Required IAM permissions

Your user or role needs these permissions:

  • iam:GetPolicy
  • iam:GetPolicyVersion
  • iam:ListPolicyVersions
  • iam:CreatePolicyVersion
  • iam:DeletePolicyVersion (if you need to remove old versions)

AWS Console Method

  1. Sign in to the AWS Console and open IAM
  2. In the left navigation, click Policies
  3. Find and click on the flagged customer-managed policy
  4. Click the JSON tab to view the policy document
  5. Look for overly permissive statements - common red flags:
    • "Action": "iam:*" (full IAM access)
    • "Action": ["iam:CreatePolicy*", "iam:AttachRolePolicy", ...]
    • "Resource": "*" combined with IAM actions
    • "Action": "iam:PassRole" without conditions
  6. Click Edit to modify the policy
  7. Apply least-privilege fixes (see "What to Change" below)
  8. Click Next, then Save changes

What to Change

Problematic PatternSecure Alternative
"Action": "iam:*"List only the specific IAM actions needed
"Resource": "*"Scope to specific ARNs (e.g., arn:aws:iam::123456789012:role/MyApp*)
iam:PassRole without conditionsAdd iam:PassedToService condition
Unrestricted policy attachmentRestrict to specific policies or use permissions boundaries
AWS CLI (optional)

Step 1: Get the current policy document

# Get policy metadata to find the default version
aws iam get-policy \
--policy-arn arn:aws:iam::<ACCOUNT_ID>:policy/<POLICY_NAME> \
--region us-east-1

# Get the actual policy document (replace v1 with your default version)
aws iam get-policy-version \
--policy-arn arn:aws:iam::<ACCOUNT_ID>:policy/<POLICY_NAME> \
--version-id v1 \
--region us-east-1 \
--query 'PolicyVersion.Document' \
--output json > current-policy.json

Step 2: Edit the policy document

Open current-policy.json and remove or scope down the privilege escalation permissions.

Step 3: Create a new policy version

aws iam create-policy-version \
--policy-arn arn:aws:iam::<ACCOUNT_ID>:policy/<POLICY_NAME> \
--policy-document file://updated-policy.json \
--set-as-default \
--region us-east-1

Example: Secure policy with scoped permissions

{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowSpecificS3Actions",
"Effect": "Allow",
"Action": [
"s3:GetObject",
"s3:PutObject",
"s3:ListBucket"
],
"Resource": [
"arn:aws:s3:::my-specific-bucket",
"arn:aws:s3:::my-specific-bucket/*"
]
},
{
"Sid": "AllowPassRoleToSpecificService",
"Effect": "Allow",
"Action": "iam:PassRole",
"Resource": "arn:aws:iam::123456789012:role/MyLambdaRole",
"Condition": {
"StringEquals": {
"iam:PassedToService": "lambda.amazonaws.com"
}
}
}
]
}
CloudFormation (optional)

This template creates a secure IAM policy with explicit deny statements for privilege escalation actions:

AWSTemplateFormatVersion: '2010-09-09'
Description: >
Secure IAM policy with least privilege - no privilege escalation paths

Parameters:
PolicyName:
Type: String
Description: Name for the IAM policy
Default: SecureLeastPrivilegePolicy

Resources:
SecureIAMPolicy:
Type: AWS::IAM::ManagedPolicy
Properties:
ManagedPolicyName: !Ref PolicyName
Description: Secure policy following least privilege principle
PolicyDocument:
Version: '2012-10-17'
Statement:
- Sid: AllowSpecificS3Actions
Effect: Allow
Action:
- s3:GetObject
- s3:PutObject
- s3:ListBucket
Resource:
- arn:aws:s3:::my-specific-bucket
- arn:aws:s3:::my-specific-bucket/*
- Sid: DenyPrivilegeEscalation
Effect: Deny
Action:
- iam:CreatePolicyVersion
- iam:SetDefaultPolicyVersion
- iam:AttachUserPolicy
- iam:AttachGroupPolicy
- iam:AttachRolePolicy
- iam:PutUserPolicy
- iam:PutGroupPolicy
- iam:PutRolePolicy
- iam:CreatePolicy
- iam:UpdateAssumeRolePolicy
- iam:PassRole
Resource: '*'

Outputs:
PolicyArn:
Description: ARN of the created IAM policy
Value: !Ref SecureIAMPolicy

Deploy with:

aws cloudformation deploy \
--template-file template.yaml \
--stack-name secure-iam-policy \
--capabilities CAPABILITY_NAMED_IAM \
--region us-east-1
Terraform (optional)
# Secure IAM policy with least privilege - no privilege escalation paths

variable "policy_name" {
description = "Name for the IAM policy"
type = string
default = "SecureLeastPrivilegePolicy"
}

# Example: Secure policy with specific, scoped permissions
resource "aws_iam_policy" "secure_policy" {
name = var.policy_name
description = "Secure policy following least privilege principle"

policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Sid = "AllowSpecificS3Actions"
Effect = "Allow"
Action = [
"s3:GetObject",
"s3:PutObject",
"s3:ListBucket"
]
Resource = [
"arn:aws:s3:::my-specific-bucket",
"arn:aws:s3:::my-specific-bucket/*"
]
},
{
Sid = "DenyPrivilegeEscalation"
Effect = "Deny"
Action = [
"iam:CreatePolicyVersion",
"iam:SetDefaultPolicyVersion",
"iam:AttachUserPolicy",
"iam:AttachGroupPolicy",
"iam:AttachRolePolicy",
"iam:PutUserPolicy",
"iam:PutGroupPolicy",
"iam:PutRolePolicy",
"iam:CreatePolicy",
"iam:UpdateAssumeRolePolicy",
"iam:PassRole"
]
Resource = "*"
}
]
})
}

output "policy_arn" {
description = "ARN of the created IAM policy"
value = aws_iam_policy.secure_policy.arn
}

Apply with:

terraform init
terraform apply

Verification

After making changes, verify the fix:

  1. Re-run the Prowler check:
    prowler aws --check iam_policy_allows_privilege_escalation
  2. Confirm the previously failing policy now passes
Manual verification via Console
  1. Go to IAM > Policies and open the modified policy
  2. Review the JSON and confirm:
    • No broad iam:* wildcards exist
    • Resources are scoped to specific ARNs where possible
    • iam:PassRole has appropriate conditions
    • No unrestricted policy attachment permissions

Additional Resources

Notes

  • Audit before changing: Before modifying any policy, understand what applications and users depend on it. Removing permissions may break functionality.
  • Use permissions boundaries: For delegated administration scenarios, permissions boundaries prevent users from escalating beyond defined limits.
  • Service Control Policies (SCPs): In AWS Organizations, SCPs provide account-wide guardrails that even administrators cannot bypass.
  • Policy versions: AWS keeps up to 5 versions of managed policies. After creating a new version, you may need to delete old versions if you hit the limit.
  • Inline vs managed policies: This check focuses on customer-managed policies. Inline policies attached directly to users/groups/roles should also be reviewed separately.