Back to Checks
aws

Custom IAM policy does not allow STS role assumption on wildcard resources

iam_no_custom_policy_permissive_role_assumption
Service: iamSeverity: high
OfficialBuilt-inVerified
Categories:identity-access
Resource Type:AwsIamPolicy

Custom IAM policies with Allow statements that grant sts:AssumeRole (or sts:*/*) to a wildcard Resource.

Risk

Broad AssumeRole rights let principals obtain temporary credentials for many roles, enabling privilege escalation, lateral movement, and cross-account access where trusts allow. This jeopardizes confidentiality and integrity of data and the control plane.

Run this check with Prowler

Run in Prowler Cloud
Run
prowler aws --checks iam_no_custom_policy_permissive_role_assumption

Recommendation

Apply least privilege to sts:AssumeRole:

  • Scope Resource to exact role ARNs
  • Require MFA and, for third parties, ExternalId
  • Enforce permissions boundaries and SCPs to block wildcards
  • Regularly remove unused role-assumption rights and separate duties

Remediation

CLI

aws iam create-policy-version --policy-arn <example_policy_arn> --policy-document '{"Version":"2012-10-17","Statement":[{"Effect":"Allow","Action":"sts:AssumeRole","Resource":"arn:aws:iam::<example_account_id>:role/<example_resource_name>"}]}' --set-as-default

Terraform

# Terraform: Replace wildcard resource with a specific role ARN
resource "aws_iam_policy" "<example_resource_name>" {
  name   = "<example_resource_name>"
  policy = jsonencode({
    Version   = "2012-10-17"
    Statement = [{
      Effect   = "Allow"
      Action   = "sts:AssumeRole"
      Resource = "arn:aws:iam::<example_account_id>:role/<example_resource_name>" // CRITICAL: restrict to a specific role ARN to remove wildcard
    }]
  })
}

NativeIaC

# CloudFormation: Replace wildcard resource with a specific role ARN
Resources:
  <example_resource_name>:
    Type: AWS::IAM::ManagedPolicy
    Properties:
      PolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Effect: Allow
            Action: sts:AssumeRole
            Resource: arn:aws:iam::<example_account_id>:role/<example_resource_name>  # CRITICAL: restrict to a specific role ARN to remove wildcard

Other

1Open the AWS Console and go to IAM > Policies
2Select the custom policy that FAILED and click Edit policy (JSON)
3Find any statement with Effect: Allow and Action including sts:AssumeRole (or sts:* or *) where Resource is "*"
4Change Resource to the specific role ARN(s), e.g.: arn:aws:iam::<example_account_id>:role/<example_resource_name>
5Save changes to create the new default version

Check Code

from prowler.lib.check.models import Check, Check_Report_AWS
from prowler.providers.aws.services.iam.iam_client import iam_client


class iam_no_custom_policy_permissive_role_assumption(Check):
    def execute(self) -> Check_Report_AWS:
        findings = []

        def resource_has_wildcard(resource):
            if isinstance(resource, str):
                return "*" in resource
            if isinstance(resource, list):
                return any("*" in r for r in resource)
            return False

        for policy in iam_client.policies.values():
            # Check only custom policies
            if policy.type == "Custom":
                if not policy.attached and not iam_client.provider.scan_unused_services:
                    continue
                report = Check_Report_AWS(metadata=self.metadata(), resource=policy)
                report.region = iam_client.region
                report.status = "PASS"
                report.status_extended = f"Custom Policy {policy.name} does not allow permissive STS Role assumption."

                if policy.document:
                    if not isinstance(policy.document["Statement"], list):
                        policy_statements = [policy.document["Statement"]]
                    else:
                        policy_statements = policy.document["Statement"]
                    for statement in policy_statements:
                        if (
                            statement.get("Effect") == "Allow"
                            and "Action" in statement
                            and "Resource" in statement
                            and resource_has_wildcard(statement["Resource"])
                        ):
                            actions = (
                                statement["Action"]
                                if isinstance(statement["Action"], list)
                                else [statement["Action"]]
                            )
                            for action in actions:
                                if action in ["sts:AssumeRole", "sts:*", "*"]:
                                    report.status = "FAIL"
                                    report.status_extended = f"Custom Policy {policy.name} allows permissive STS Role assumption."
                                    break
                            if report.status == "FAIL":
                                break

                findings.append(report)

        return findings