Skip to main content

IAM Custom Policy Permissive Role Assumption

Overview

This check identifies custom IAM policies that grant overly broad sts:AssumeRole permissions. Specifically, it flags policies that allow assuming any role (*) rather than restricting access to specific role ARNs. The principle of least privilege requires that IAM policies only grant the minimum permissions needed.

Risk

When a policy allows sts:AssumeRole on wildcard resources (*), the associated principal (user, group, or role) can potentially assume any role in your account or even roles in other accounts (if those roles have permissive trust policies). This creates several serious risks:

  • Privilege escalation: Users can assume roles with higher privileges than their own
  • Lateral movement: Attackers who compromise one identity can move to other roles
  • Cross-account access: If external accounts have misconfigured trust policies, your users could access those accounts
  • Audit trail confusion: Activities spread across many assumed roles are harder to track
  • Compliance violations: Many security frameworks require explicit role restrictions

Remediation Steps

Prerequisites

You need:

  • AWS Console access with permissions to view and modify IAM policies
  • Knowledge of which specific roles your users or applications actually need to assume
Required IAM permissions (for administrators)

Your IAM user or role needs these permissions:

  • iam:GetPolicy
  • iam:GetPolicyVersion
  • iam:ListPolicyVersions
  • iam:CreatePolicyVersion
  • iam:DeletePolicyVersion
  • iam:ListEntitiesForPolicy

AWS Console Method

Step 1: Identify the Problematic Policy

  1. Open IAM in the AWS Console

  2. Find your custom policies

    • Click Policies in the left sidebar
    • Filter by Type: Customer managed to see only your custom policies
  3. Search for AssumeRole permissions

    • Click on each policy to view its details
    • Look for statements that contain:
      • Action: sts:AssumeRole, sts:*, or *
      • Resource: * (wildcard)

Step 2: Determine Required Roles

Before modifying the policy, identify which roles are legitimately needed:

  1. Review application requirements

    • Check documentation or speak with developers about which roles the application assumes
  2. Check CloudTrail logs

    • Search for AssumeRole events to see which roles have been assumed recently
    • Go to CloudTrail > Event history and filter by Event name: AssumeRole
  3. List the specific role ARNs

    • Note down each role ARN that is actually required
    • Format: arn:aws:iam::<account-id>:role/<role-name>

Step 3: Update the Policy

  1. Edit the policy

    • In the IAM Console, select your policy
    • Click Edit (or Edit policy)
    • Switch to the JSON tab
  2. Replace the wildcard resource

    • Find the statement with sts:AssumeRole and Resource: "*"
    • Replace "*" with a list of specific role ARNs:

    Before (problematic):

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

    After (secure):

    {
    "Effect": "Allow",
    "Action": "sts:AssumeRole",
    "Resource": [
    "arn:aws:iam::123456789012:role/ApplicationRole",
    "arn:aws:iam::123456789012:role/DeploymentRole"
    ]
    }
  3. Save the policy

    • Click Next
    • Review the changes
    • Click Save changes
AWS CLI (optional)

Step 1: Get the current policy document

First, find the policy ARN and get its current version:

# List your custom policies
aws iam list-policies \
--scope Local \
--query 'Policies[*].[PolicyName,Arn]' \
--output table \
--region us-east-1

# Get the default version ID
aws iam get-policy \
--policy-arn arn:aws:iam::<account-id>:policy/<policy-name> \
--query 'Policy.DefaultVersionId' \
--output text \
--region us-east-1

# Get the current policy document
aws iam get-policy-version \
--policy-arn arn:aws:iam::<account-id>:policy/<policy-name> \
--version-id v1 \
--query 'PolicyVersion.Document' \
--output json \
--region us-east-1 > /tmp/current-policy.json

Step 2: Create the updated policy document

Edit the policy to replace wildcard resources with specific role ARNs:

cat > /tmp/updated-policy.json << 'EOF'
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowSpecificRoleAssumption",
"Effect": "Allow",
"Action": "sts:AssumeRole",
"Resource": [
"arn:aws:iam::123456789012:role/ApplicationRole",
"arn:aws:iam::123456789012:role/DeploymentRole"
]
}
]
}
EOF

Replace the role ARNs with your actual required roles.

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:///tmp/updated-policy.json \
--set-as-default \
--region us-east-1

Note: IAM policies can have up to 5 versions. If you hit the limit, delete an old version first:

# List versions
aws iam list-policy-versions \
--policy-arn arn:aws:iam::<account-id>:policy/<policy-name> \
--region us-east-1

# Delete an old non-default version
aws iam delete-policy-version \
--policy-arn arn:aws:iam::<account-id>:policy/<policy-name> \
--version-id v1 \
--region us-east-1
CloudFormation (optional)

This template creates an IAM policy with properly scoped sts:AssumeRole permissions:

AWSTemplateFormatVersion: '2010-09-09'
Description: IAM policy with least-privilege role assumption

Parameters:
PolicyName:
Type: String
Description: Name for the IAM policy
Default: scoped-role-assumption-policy

AllowedRoleArns:
Type: CommaDelimitedList
Description: Comma-separated list of role ARNs that can be assumed
Default: arn:aws:iam::123456789012:role/ApplicationRole,arn:aws:iam::123456789012:role/DeploymentRole

Resources:
ScopedAssumeRolePolicy:
Type: AWS::IAM::ManagedPolicy
Properties:
ManagedPolicyName: !Ref PolicyName
Description: Policy that allows assuming specific roles only
PolicyDocument:
Version: '2012-10-17'
Statement:
- Sid: AllowSpecificRoleAssumption
Effect: Allow
Action: sts:AssumeRole
Resource: !Ref AllowedRoleArns

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

Deploy with:

aws cloudformation deploy \
--template-file scoped-assume-role.yaml \
--stack-name scoped-role-assumption \
--parameter-overrides \
PolicyName=scoped-role-assumption-policy \
AllowedRoleArns="arn:aws:iam::123456789012:role/ApplicationRole,arn:aws:iam::123456789012:role/DeploymentRole" \
--capabilities CAPABILITY_NAMED_IAM \
--region us-east-1
Terraform (optional)
# Variables
variable "policy_name" {
description = "Name for the IAM policy"
type = string
default = "scoped-role-assumption-policy"
}

variable "allowed_role_arns" {
description = "List of role ARNs that can be assumed"
type = list(string)
default = [
"arn:aws:iam::123456789012:role/ApplicationRole",
"arn:aws:iam::123456789012:role/DeploymentRole"
]
}

# IAM policy with scoped AssumeRole permissions
resource "aws_iam_policy" "scoped_assume_role" {
name = var.policy_name
description = "Policy that allows assuming specific roles only"

policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Sid = "AllowSpecificRoleAssumption"
Effect = "Allow"
Action = "sts:AssumeRole"
Resource = var.allowed_role_arns
}
]
})
}

# Output
output "policy_arn" {
description = "ARN of the created policy"
value = aws_iam_policy.scoped_assume_role.arn
}

Deploy with:

terraform init
terraform plan -var='allowed_role_arns=["arn:aws:iam::123456789012:role/ApplicationRole","arn:aws:iam::123456789012:role/DeploymentRole"]'
terraform apply -var='allowed_role_arns=["arn:aws:iam::123456789012:role/ApplicationRole","arn:aws:iam::123456789012:role/DeploymentRole"]'

Verification

After updating the policy, verify it no longer has permissive role assumption:

  1. In the AWS Console:

    • Go to IAM > Policies and select your policy
    • Click the Permissions tab
    • Expand the policy and check that sts:AssumeRole actions have specific role ARNs listed under Resource (not *)
  2. Test the change:

    • Have the affected users or applications test their normal workflows
    • Confirm they can still assume the roles they need
    • Verify they cannot assume roles outside the allowed list
CLI verification commands

Check the policy document for wildcard resources:

# Get the current policy version
VERSION=$(aws iam get-policy \
--policy-arn arn:aws:iam::<account-id>:policy/<policy-name> \
--query 'Policy.DefaultVersionId' \
--output text \
--region us-east-1)

# View the policy document
aws iam get-policy-version \
--policy-arn arn:aws:iam::<account-id>:policy/<policy-name> \
--version-id $VERSION \
--query 'PolicyVersion.Document' \
--output json \
--region us-east-1

The output should show specific role ARNs in the Resource field, not "*":

{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowSpecificRoleAssumption",
"Effect": "Allow",
"Action": "sts:AssumeRole",
"Resource": [
"arn:aws:iam::123456789012:role/ApplicationRole",
"arn:aws:iam::123456789012:role/DeploymentRole"
]
}
]
}

Additional Resources

Notes

  • Impact assessment: Before modifying the policy, identify all users, groups, and roles that have this policy attached. Use IAM > Policies > [Your Policy] > Entities attached to see the full list.

  • Cross-account roles: If your users need to assume roles in other AWS accounts, include those external role ARNs in your allowed list (e.g., arn:aws:iam::987654321098:role/ExternalRole). The external account must also have a trust policy that allows your users.

  • MFA requirement: For sensitive roles, consider adding a condition requiring MFA:

    "Condition": {
    "Bool": {
    "aws:MultiFactorAuthPresent": "true"
    }
    }
  • External ID for third parties: When allowing third-party vendors to assume roles, require an ExternalId:

    "Condition": {
    "StringEquals": {
    "sts:ExternalId": "your-unique-external-id"
    }
    }
  • Service Control Policies (SCPs): In AWS Organizations, you can use SCPs as an additional guardrail to prevent assuming certain roles, even if IAM policies allow it.

  • Permission boundaries: Consider attaching permission boundaries to limit what actions an assumed role can perform, providing defense in depth.

  • Regular audits: Periodically review which roles are being assumed using CloudTrail and remove any role ARNs from the policy that are no longer needed.