Skip to main content

Ensure No IAM Inline Policies Allow Privilege Escalation

Overview

This check identifies IAM inline policies that contain permission combinations enabling privilege escalation. Privilege escalation occurs when a user can leverage certain IAM permissions to gain higher access levels than originally intended, potentially reaching administrator-level access.

The check flags dangerous actions such as:

  • iam:CreatePolicyVersion - Create new versions of policies
  • iam:AttachUserPolicy / iam:AttachRolePolicy - Attach policies to users or roles
  • iam:PassRole - Pass roles to AWS services
  • sts:AssumeRole - Assume other roles
  • iam:UpdateAssumeRolePolicy - Modify who can assume a role
  • iam:PutUserPolicy / iam:PutRolePolicy - Add inline policies

Risk

When inline policies contain these dangerous permission combinations, attackers or compromised accounts can escalate their privileges to administrator level. This can lead to:

  • Unauthorized data access: Reading secrets, databases, and sensitive files
  • System compromise: Modifying application code, configurations, and security settings
  • Service disruption: Deleting resources, disabling logging, or causing outages
  • Persistent access: Creating backdoor credentials or roles for future access

Severity: High

Remediation Steps

Prerequisites

You need IAM permissions to view and modify policies. Specifically, you need permissions like iam:ListUserPolicies, iam:GetUserPolicy, iam:DeleteUserPolicy, and similar permissions for roles and groups.

Required IAM permissions

To perform this remediation, your IAM principal needs:

{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"iam:ListUsers",
"iam:ListRoles",
"iam:ListGroups",
"iam:ListUserPolicies",
"iam:ListRolePolicies",
"iam:ListGroupPolicies",
"iam:GetUserPolicy",
"iam:GetRolePolicy",
"iam:GetGroupPolicy",
"iam:DeleteUserPolicy",
"iam:DeleteRolePolicy",
"iam:DeleteGroupPolicy",
"iam:PutUserPolicy",
"iam:PutRolePolicy",
"iam:PutGroupPolicy",
"iam:CreatePolicy",
"iam:AttachUserPolicy",
"iam:AttachRolePolicy",
"iam:AttachGroupPolicy"
],
"Resource": "*"
}
]
}

AWS Console Method

Step 1: Identify the problematic inline policy

  1. Open the IAM Console
  2. From the Prowler finding, note which IAM entity (user, role, or group) has the flagged inline policy
  3. In the left navigation, click Users, Roles, or Groups depending on the entity type
  4. Click on the entity name to open its details

Step 2: Review the inline policy

  1. Click the Permissions tab
  2. Under "Permissions policies," look for policies with a type of Inline
  3. Click the policy name to expand and view its JSON
  4. Look for dangerous actions listed in the Overview section above

Step 3: Remove or replace the inline policy

Option A: Delete the inline policy (if no longer needed)

  1. Click the X or Remove button next to the inline policy
  2. Confirm the deletion

Option B: Replace with a least-privilege managed policy

  1. Delete the inline policy (click X or Remove)
  2. Click Add permissions > Attach policies
  3. Either:
    • Select an appropriate AWS managed policy that provides only the needed access
    • Create a new customer managed policy with only the required permissions (see below)

Step 4: Create a secure replacement policy (if needed)

  1. In the IAM console, go to Policies in the left navigation
  2. Click Create policy
  3. Use the Visual editor or JSON editor to define permissions
  4. Follow these principles:
    • Grant only the specific actions needed (avoid wildcards like *)
    • Restrict resources to specific ARNs when possible
    • Add conditions to further limit access
    • Do NOT include any of the dangerous escalation actions unless absolutely required
  5. Give the policy a descriptive name and create it
  6. Attach this new managed policy to your user, role, or group
AWS CLI (optional)

List inline policies for users, roles, and groups

# List inline policies for a specific user
aws iam list-user-policies \
--user-name <username> \
--region us-east-1

# List inline policies for a specific role
aws iam list-role-policies \
--role-name <role-name> \
--region us-east-1

# List inline policies for a specific group
aws iam list-group-policies \
--group-name <group-name> \
--region us-east-1

View the contents of an inline policy

# Get user inline policy
aws iam get-user-policy \
--user-name <username> \
--policy-name <policy-name> \
--region us-east-1

# Get role inline policy
aws iam get-role-policy \
--role-name <role-name> \
--policy-name <policy-name> \
--region us-east-1

# Get group inline policy
aws iam get-group-policy \
--group-name <group-name> \
--policy-name <policy-name> \
--region us-east-1

Delete the problematic inline policy

# Delete user inline policy
aws iam delete-user-policy \
--user-name <username> \
--policy-name <policy-name> \
--region us-east-1

# Delete role inline policy
aws iam delete-role-policy \
--role-name <role-name> \
--policy-name <policy-name> \
--region us-east-1

# Delete group inline policy
aws iam delete-group-policy \
--group-name <group-name> \
--policy-name <policy-name> \
--region us-east-1

Create a secure managed policy as a replacement

First, create a policy document file (secure-policy.json):

{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowS3ReadAccess",
"Effect": "Allow",
"Action": [
"s3:GetObject",
"s3:ListBucket"
],
"Resource": [
"arn:aws:s3:::your-bucket-name",
"arn:aws:s3:::your-bucket-name/*"
]
}
]
}

Then create and attach the policy:

# Create the managed policy
aws iam create-policy \
--policy-name SecureLeastPrivilegePolicy \
--policy-document file://secure-policy.json \
--description "Least privilege policy without escalation permissions" \
--region us-east-1

# Attach to a user (replace account ID and user name)
aws iam attach-user-policy \
--user-name <username> \
--policy-arn arn:aws:iam::<account-id>:policy/SecureLeastPrivilegePolicy \
--region us-east-1

# Or attach to a role
aws iam attach-role-policy \
--role-name <role-name> \
--policy-arn arn:aws:iam::<account-id>:policy/SecureLeastPrivilegePolicy \
--region us-east-1
CloudFormation (optional)

Use this CloudFormation template to create a secure managed policy that follows least privilege principles. This replaces problematic inline policies.

AWSTemplateFormatVersion: '2010-09-09'
Description: Secure IAM managed policy following least privilege principles

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

S3BucketArn:
Type: String
Description: ARN of the S3 bucket to grant access to
Default: arn:aws:s3:::my-example-bucket

Resources:
SecureManagedPolicy:
Type: AWS::IAM::ManagedPolicy
Properties:
ManagedPolicyName: !Ref PolicyName
Description: Least privilege policy without escalation permissions
PolicyDocument:
Version: '2012-10-17'
Statement:
- Sid: AllowS3ReadAccess
Effect: Allow
Action:
- s3:GetObject
- s3:ListBucket
Resource:
- !Ref S3BucketArn
- !Sub '${S3BucketArn}/*'
- Sid: AllowCloudWatchLogsRead
Effect: Allow
Action:
- logs:DescribeLogGroups
- logs:DescribeLogStreams
- logs:GetLogEvents
Resource: !Sub 'arn:aws:logs:${AWS::Region}:${AWS::AccountId}:log-group:*'

Outputs:
PolicyArn:
Description: ARN of the created managed policy
Value: !Ref SecureManagedPolicy

Deploy this template:

aws cloudformation deploy \
--template-file secure-policy.yaml \
--stack-name secure-iam-policy \
--capabilities CAPABILITY_NAMED_IAM \
--region us-east-1

After creating the managed policy, manually delete the inline policies and attach this new policy to your IAM entities.

Terraform (optional)

Use this Terraform configuration to create a secure managed policy and attach it to IAM entities.

terraform {
required_version = ">= 1.0"
required_providers {
aws = {
source = "hashicorp/aws"
version = ">= 4.0"
}
}
}

provider "aws" {
region = "us-east-1"
}

variable "s3_bucket_arn" {
description = "ARN of the S3 bucket to grant access to"
type = string
default = "arn:aws:s3:::my-example-bucket"
}

# Create a managed policy instead of inline policy
resource "aws_iam_policy" "secure_least_privilege" {
name = "SecureLeastPrivilegePolicy"
description = "Least privilege policy without escalation permissions"

policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Sid = "AllowS3ReadAccess"
Effect = "Allow"
Action = [
"s3:GetObject",
"s3:ListBucket"
]
Resource = [
var.s3_bucket_arn,
"${var.s3_bucket_arn}/*"
]
}
]
})
}

# Attach the managed policy to a user (instead of inline policy)
resource "aws_iam_user_policy_attachment" "attach_secure_policy" {
user = "example-user"
policy_arn = aws_iam_policy.secure_least_privilege.arn
}

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

Important: Before applying, manually remove the existing inline policies or use terraform import to manage them.

Apply the configuration:

terraform init
terraform plan
terraform apply

Verification

After remediation, verify the fix:

  1. In the IAM console, check that the user/role/group no longer has inline policies with dangerous actions
  2. Re-run the Prowler check to confirm it passes:
prowler aws --check iam_inline_policy_allows_privilege_escalation
Advanced verification with AWS CLI

List all inline policies and verify none contain escalation permissions:

# Check a specific user has no inline policies
aws iam list-user-policies \
--user-name <username> \
--region us-east-1

# Expected output for a remediated user:
# {
# "PolicyNames": []
# }

# Verify attached managed policies instead
aws iam list-attached-user-policies \
--user-name <username> \
--region us-east-1

Additional Resources

Notes

  • Prefer managed policies over inline policies: Managed policies are reusable, versioned, and easier to audit. AWS recommends using managed policies for most use cases.

  • Use IAM Access Analyzer: AWS IAM Access Analyzer can help generate least-privilege policies based on actual access patterns. This is a great way to right-size permissions.

  • Consider permissions boundaries: For users or roles that need some IAM permissions, use permissions boundaries to limit the maximum permissions they can have, even if they try to escalate.

  • Service Control Policies (SCPs): For multi-account environments, use SCPs to prevent dangerous IAM actions at the organization level.

  • Audit regularly: Use AWS Config rules or Prowler scheduled scans to continuously monitor for privilege escalation risks.

  • Test before removing permissions: Before deleting inline policies, ensure you understand what access the entity needs and have replacement policies in place to avoid breaking applications.