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:PassRolewith 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:GetPolicyiam:GetPolicyVersioniam:ListPolicyVersionsiam:CreatePolicyVersioniam:DeletePolicyVersion(if you need to remove old versions)
AWS Console Method
- Sign in to the AWS Console and open IAM
- In the left navigation, click Policies
- Find and click on the flagged customer-managed policy
- Click the JSON tab to view the policy document
- 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
- Click Edit to modify the policy
- Apply least-privilege fixes (see "What to Change" below)
- Click Next, then Save changes
What to Change
| Problematic Pattern | Secure 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 conditions | Add iam:PassedToService condition |
| Unrestricted policy attachment | Restrict 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:
- Re-run the Prowler check:
prowler aws --check iam_policy_allows_privilege_escalation - Confirm the previously failing policy now passes
Manual verification via Console
- Go to IAM > Policies and open the modified policy
- Review the JSON and confirm:
- No broad
iam:*wildcards exist - Resources are scoped to specific ARNs where possible
iam:PassRolehas appropriate conditions- No unrestricted policy attachment permissions
- No broad
Additional Resources
- AWS IAM Best Practices
- IAM Access Analyzer - helps identify overly permissive policies
- Permissions Boundaries for IAM Entities
- Rhino Security Labs: AWS IAM Privilege Escalation Methods
- Bishop Fox: IAM Privilege Escalation
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.