Safeguard
Cloud Security

AWS IAM: common vulnerabilities and fixes

Rhino Security Labs catalogs 21+ IAM privilege-escalation paths to full admin — most start with one over-scoped policy nobody remembers writing.

Safeguard Research Team
Research
6 min read

AWS Identity and Access Management underpins every permission decision in an AWS account, and it fails in the same handful of ways across almost every environment. Rhino Security Labs' widely cited research documents more than 21 distinct paths a low-privileged IAM identity can use to escalate to full administrative control, none of which require exploiting a bug in AWS itself — they abuse permissions that were simply granted too broadly. A single Action: "*", Resource: "*" statement, left over from a Terraform module someone copied from a blog post, is often the base condition that makes every one of those chains possible. Meanwhile, IAM Access Analyzer's "unused access" findings — a feature AWS announced in November 2023 and has continued expanding through 2024 and 2025 — exist specifically because most organizations accumulate access keys, roles, and permissions nobody remembers granting and nobody has gotten around to removing. This post walks through the misconfiguration classes that show up most often in real AWS accounts: privilege-escalation permission combinations, wildcard policies, stale credentials, and overly trusting cross-account roles, with concrete remediation steps for each.

What are the most common IAM privilege escalation paths?

Privilege escalation happens when a low-privileged identity holds a permission — or combination of permissions — that lets it grant itself more access than it started with. Rhino Security Labs' catalog (published on GitHub at RhinoSecurityLabs/AWS-IAM-Privilege-Escalation) lists concrete examples: iam:CreatePolicyVersion lets an attacker set a new default version of a policy attached to their own identity, granting *:* without ever touching iam:SetDefaultPolicyVersion; iam:CreateAccessKey on another IAM user lets an attacker mint new long-lived credentials for a more-privileged principal; and iam:PassRole paired with lambda:CreateFunction and lambda:CreateEventSourceMapping lets an attacker pass a privileged execution role to a new Lambda function they control. None of these require compromising AWS — they only require an over-scoped policy attached weeks or months earlier. Fix this by treating every iam:* action as sensitive by default, denying it in permissions boundaries for non-admin roles, and running a tool like Cloudsplaining (which implements checks for exactly these permission combinations) against every policy before it merges.

Why are wildcard policies the root cause behind most of these chains?

Wildcard policies are the root cause because nearly every privilege-escalation and lateral-movement path Rhino Security Labs documented depends on a policy that granted more than the task required — iam:*, s3:*, or the ultimate case, Action: "*" with Resource: "*". A developer given iam:* to "manage their own team's roles" also gets iam:CreatePolicyVersion and iam:AttachUserPolicy, either of which is a direct escalation path with no additional permissions needed. AWS's own IAM Access Analyzer addresses this with "policy checks" — validation and custom policy-check APIs that flag overly permissive statements before they're attached, a capability AWS's News Blog documented rolling out through 2023 and 2024. Fix wildcard policies by enumerating the specific actions and resource ARNs a role actually needs, attaching a permissions boundary that caps the ceiling regardless of what gets added later, and running Access Analyzer's policy validation as a pre-deployment gate rather than a periodic audit.

How much risk comes from unused credentials and permissions?

A meaningful share of an account's exposed attack surface is simply access nobody is using: IAM users with access keys that haven't signed a request in months, roles nobody assumes anymore, and permissions granted "just in case" that were never exercised. AWS built IAM Access Analyzer's "unused access" findings specifically to surface this — announced in November 2023 and expanded through 2024–2025 — and it centralizes three categories in an org-wide dashboard: unused IAM roles, unused user passwords and access keys beyond a configurable inactivity window, and unused service- or action-level permissions granted to a role that its actual usage never touches. Because these findings integrate with EventBridge and Security Hub, they can drive automated rightsizing rather than a manual quarterly spreadsheet review. The fix is straightforward but requires discipline: rotate or delete access keys past your inactivity threshold, deprovision roles with no recent AssumeRole activity, and use the unused-permissions findings to shrink policies down to what a role's CloudTrail history shows it actually calling.

What makes cross-account trust policies a common misconfiguration?

Cross-account trust policies go wrong when the Principal in an sts:AssumeRole trust policy is scoped too broadly, or when the policy omits a condition that ties the assumption to a specific external party. A trust policy that lists another AWS account as principal without an sts:ExternalId condition creates a confused-deputy risk: if that account also grants role-assumption to a third party (a SaaS vendor, for instance), anyone who can convince the third party to act on their behalf can pivot into your account through the original role. This is a long-documented pattern — AWS's own confused-deputy guidance recommends adding an sts:ExternalId condition on any role trust policy shared with a third party, precisely to prevent this kind of transitive access. Fix cross-account trust by always scoping Principal to specific account IDs (never a wildcard), requiring sts:ExternalId on any role assumable by a third-party service, and reviewing trust policies with the same rigor as inline permission policies — a role's trust document is itself an access grant.

What does a practical IAM hardening baseline look like?

A practical baseline starts with permissions boundaries on every role capable of creating or modifying IAM resources, so that even a successful escalation attempt caps out below admin. Layer on IAM Access Analyzer for both policy validation at write-time and unused-access findings on a recurring schedule — AWS ships both as native, no-additional-agent capabilities. Add Cloudsplaining or an equivalent policy-linting tool to CI so a pull request introducing iam:CreatePolicyVersion or iam:PassRole alongside a compute-creation permission gets flagged before merge, not after an incident. Finally, treat access keys as a liability by default: prefer role assumption and short-lived credentials over long-lived IAM user keys wherever a workload supports it, since a key that doesn't exist can't be leaked, stolen, or left unused for a Rhino-style escalation chain to exploit months later.

Where this fits alongside software supply chain security

Safeguard's platform is built around software supply chain security — SBOM generation, dependency and container vulnerability scanning, license compliance, and secrets detection across source and build pipelines. IAM policy analysis and cloud identity posture management are a distinct discipline (CSPM/CIEM) that sits outside that scope today, and it's worth saying so plainly rather than stretching a product fit that isn't there. The two areas are complementary in practice: a hardcoded AWS access key caught by Safeguard's secrets scanning is often the same class of credential an IAM Access Analyzer unused-key finding would have flagged as dormant, and an SBOM that tracks which build systems pull cloud SDKs is a useful input to any team also auditing the IAM roles those pipelines assume. Teams running both a supply chain security platform and a native IAM hardening program cover more of the real attack surface than either does alone.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.