Skip to main content

IAM Inline Policy Does Not Allow Administrative Privileges

Overview

This check verifies that IAM inline policies attached to users, roles, or groups do not grant unrestricted administrative access by combining Action: "*" with Resource: "*". Such policies give full control over your entire AWS account, which violates the principle of least privilege.

Risk

If an inline policy grants full administrative privileges (*:*):

  • Complete account takeover: A compromised identity gains unrestricted access to all AWS services and resources
  • Data exfiltration: Attackers can access, copy, or delete any data in your account
  • Audit trail manipulation: Malicious actors can disable CloudTrail logging to hide their activities
  • Resource destruction: Critical infrastructure, backups, and encryption keys can be deleted
  • Privilege escalation: Attackers can create new admin users or roles to maintain persistent access

Remediation Steps

Prerequisites

  • AWS Console access with IAM permissions to view and edit policies
  • Knowledge of what permissions the affected identity actually needs
Required IAM permissions for remediation

To remediate this issue, you need the following IAM permissions:

  • iam:ListUsers, iam:ListRoles, iam:ListGroups - to find affected identities
  • iam:ListUserPolicies, iam:ListRolePolicies, iam:ListGroupPolicies - to list inline policies
  • iam:GetUserPolicy, iam:GetRolePolicy, iam:GetGroupPolicy - to view policy contents
  • iam:PutUserPolicy, iam:PutRolePolicy, iam:PutGroupPolicy - to update policies
  • iam:DeleteUserPolicy, iam:DeleteRolePolicy, iam:DeleteGroupPolicy - to remove policies

AWS Console Method

Step 1: Find the affected identity

  1. Sign in to the AWS Management Console
  2. Navigate to IAM (Identity and Access Management)
  3. Review your Prowler findings to identify which user, role, or group has the overprivileged inline policy

Step 2: Locate the inline policy

For a User:

  1. Click Users in the left navigation
  2. Select the affected user
  3. Click the Permissions tab
  4. Look under Permissions policies for policies with a badge showing "Inline"

For a Role:

  1. Click Roles in the left navigation
  2. Select the affected role
  3. Click the Permissions tab
  4. Look for policies marked as "Inline"

For a Group:

  1. Click User groups in the left navigation
  2. Select the affected group
  3. Click the Permissions tab
  4. Look for policies marked as "Inline"

Step 3: Edit or replace the policy

  1. Click the inline policy name to expand it
  2. Click Edit to modify the policy
  3. Replace the overprivileged statement with specific, scoped permissions

Example: Replace this dangerous policy:

{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "*",
"Resource": "*"
}
]
}

With a scoped policy like this (adjust to actual needs):

{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"s3:GetObject",
"s3:PutObject",
"s3:ListBucket"
],
"Resource": [
"arn:aws:s3:::my-application-bucket",
"arn:aws:s3:::my-application-bucket/*"
]
}
]
}
  1. Click Save changes

Alternative: Use AWS Managed Policies

Instead of editing the inline policy, consider:

  1. Delete the overprivileged inline policy
  2. Attach an appropriate AWS managed policy (e.g., AmazonS3ReadOnlyAccess)
  3. Or create a customer managed policy with proper scoping
AWS CLI (optional)

List inline policies for a user, role, or group:

# For a user
aws iam list-user-policies --user-name <username> --region us-east-1

# For a role
aws iam list-role-policies --role-name <rolename> --region us-east-1

# For a group
aws iam list-group-policies --group-name <groupname> --region us-east-1

View the policy content:

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

# For a role
aws iam get-role-policy \
--role-name <rolename> \
--policy-name <policy-name> \
--region us-east-1

# For a group
aws iam get-group-policy \
--group-name <groupname> \
--policy-name <policy-name> \
--region us-east-1

Update the policy with scoped permissions:

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

{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"s3:GetObject",
"s3:PutObject",
"s3:ListBucket"
],
"Resource": [
"arn:aws:s3:::my-application-bucket",
"arn:aws:s3:::my-application-bucket/*"
]
}
]
}

Then apply it:

# For a user
aws iam put-user-policy \
--user-name <username> \
--policy-name <policy-name> \
--policy-document file://scoped-policy.json \
--region us-east-1

# For a role
aws iam put-role-policy \
--role-name <rolename> \
--policy-name <policy-name> \
--policy-document file://scoped-policy.json \
--region us-east-1

# For a group
aws iam put-group-policy \
--group-name <groupname> \
--policy-name <policy-name> \
--policy-document file://scoped-policy.json \
--region us-east-1

Or delete the inline policy entirely:

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

# For a role
aws iam delete-role-policy \
--role-name <rolename> \
--policy-name <policy-name> \
--region us-east-1

# For a group
aws iam delete-group-policy \
--group-name <groupname> \
--policy-name <policy-name> \
--region us-east-1
CloudFormation (optional)

If the inline policy is managed through CloudFormation, update your template to use scoped permissions.

Before (overprivileged):

AWSTemplateFormatVersion: '2010-09-09'
Description: IAM Role with overprivileged inline policy (DO NOT USE)

Resources:
MyRole:
Type: AWS::IAM::Role
Properties:
RoleName: my-application-role
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service: ec2.amazonaws.com
Action: sts:AssumeRole
Policies:
- PolicyName: AdminPolicy
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action: '*'
Resource: '*'

After (scoped permissions):

AWSTemplateFormatVersion: '2010-09-09'
Description: IAM Role with properly scoped inline policy

Resources:
MyRole:
Type: AWS::IAM::Role
Properties:
RoleName: my-application-role
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service: ec2.amazonaws.com
Action: sts:AssumeRole
Policies:
- PolicyName: ScopedS3Policy
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- s3:GetObject
- s3:PutObject
- s3:ListBucket
Resource:
- arn:aws:s3:::my-application-bucket
- arn:aws:s3:::my-application-bucket/*

Better approach - use managed policies:

AWSTemplateFormatVersion: '2010-09-09'
Description: IAM Role using managed policies instead of inline

Resources:
MyRole:
Type: AWS::IAM::Role
Properties:
RoleName: my-application-role
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service: ec2.amazonaws.com
Action: sts:AssumeRole
ManagedPolicyArns:
- arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccess

# Or create a customer managed policy
CustomPolicy:
Type: AWS::IAM::ManagedPolicy
Properties:
ManagedPolicyName: my-custom-s3-policy
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- s3:GetObject
- s3:PutObject
Resource: arn:aws:s3:::my-application-bucket/*

Deploy the updated template:

aws cloudformation deploy \
--template-file iam-role.yaml \
--stack-name my-iam-role-stack \
--capabilities CAPABILITY_NAMED_IAM \
--region us-east-1
Terraform (optional)

If managing IAM with Terraform, update your configuration to use scoped permissions.

Before (overprivileged):

# DO NOT USE - overprivileged inline policy
resource "aws_iam_role" "my_role" {
name = "my-application-role"

assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Action = "sts:AssumeRole"
Effect = "Allow"
Principal = {
Service = "ec2.amazonaws.com"
}
}
]
})

inline_policy {
name = "admin-policy"
policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Action = "*"
Effect = "Allow"
Resource = "*"
}
]
})
}
}

After (scoped permissions):

resource "aws_iam_role" "my_role" {
name = "my-application-role"

assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Action = "sts:AssumeRole"
Effect = "Allow"
Principal = {
Service = "ec2.amazonaws.com"
}
}
]
})

inline_policy {
name = "scoped-s3-policy"
policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Action = [
"s3:GetObject",
"s3:PutObject",
"s3:ListBucket"
]
Effect = "Allow"
Resource = [
"arn:aws:s3:::my-application-bucket",
"arn:aws:s3:::my-application-bucket/*"
]
}
]
})
}
}

Better approach - use managed policies:

# Use AWS managed policy
resource "aws_iam_role" "my_role" {
name = "my-application-role"

assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Action = "sts:AssumeRole"
Effect = "Allow"
Principal = {
Service = "ec2.amazonaws.com"
}
}
]
})
}

resource "aws_iam_role_policy_attachment" "s3_readonly" {
role = aws_iam_role.my_role.name
policy_arn = "arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccess"
}

# Or create a customer managed policy
resource "aws_iam_policy" "custom_s3_policy" {
name = "my-custom-s3-policy"
description = "Scoped S3 access for my application"

policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Action = [
"s3:GetObject",
"s3:PutObject"
]
Effect = "Allow"
Resource = "arn:aws:s3:::my-application-bucket/*"
}
]
})
}

resource "aws_iam_role_policy_attachment" "custom_s3" {
role = aws_iam_role.my_role.name
policy_arn = aws_iam_policy.custom_s3_policy.arn
}

Verification

After updating the policy:

  1. Go to IAM in the AWS Console
  2. Navigate to the user, role, or group you modified
  3. Click Permissions and expand the inline policy
  4. Verify it no longer contains "Action": "*" with "Resource": "*"

You can also re-run the Prowler check:

prowler aws --checks iam_inline_policy_no_administrative_privileges
CLI verification commands

Verify the policy has been updated correctly:

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

# For a role
aws iam get-role-policy \
--role-name <rolename> \
--policy-name <policy-name> \
--region us-east-1

# For a group
aws iam get-group-policy \
--group-name <groupname> \
--policy-name <policy-name> \
--region us-east-1

The output should show specific actions and resources, not wildcards.

Additional Resources

Notes

  • Determine actual needs first: Before removing admin access, understand what permissions the identity actually requires. Use CloudTrail logs or IAM Access Analyzer to identify used permissions.
  • Test changes in non-production: If possible, test policy changes in a development environment before applying to production identities.
  • Consider permissions boundaries: For additional protection, implement permissions boundaries to limit the maximum permissions an identity can have.
  • Prefer managed policies over inline: AWS managed policies and customer managed policies are easier to audit, version, and reuse across identities.
  • Break-glass procedures: If removing admin access from a role used for emergencies, ensure you have an alternative break-glass procedure documented.
  • Service disruption warning: Removing permissions may break applications. Coordinate with application owners before making changes.