On July 19, 2019, an attacker exploited a misconfigured web application firewall to reach the EC2 instance metadata service at Capital One, retrieved temporary credentials from an over-permissioned IAM role, and used them to pull data on 106 million customers from S3. The bank paid an $80 million OCC penalty and settled a class action for $190 million. The root cause wasn't a zero-day — it was an IAM role allowed to list and read far more S3 buckets than the application needed. That pattern repeats constantly: Unit 42's 2023 Cloud Threat Report found that 99% of cloud identities across surveyed AWS, Azure, and GCP accounts were granted permissions they never used in a 60-day window. IAM misconfiguration isn't an edge case in cloud security — it's the default state, and attackers know exactly which patterns to look for.
What is the most common AWS IAM misconfiguration pattern?
The most common pattern is wildcard permissions — policies that grant "Action": "*" or "Resource": "*" instead of scoping access to specific services, actions, or ARNs. A policy statement like:
{
"Effect": "Allow",
"Action": "s3:*",
"Resource": "*"
}
is functionally indistinguishable from full S3 admin access, regardless of what the role's name or intended purpose implies. Datadog's State of Cloud Security research has repeatedly found that a large share of IAM roles in production AWS accounts carry at least one unused, over-broad permission, and that AWS managed policies like AdministratorAccess and PowerUserAccess remain attached to CI/CD service roles long after the pipeline that needed them was decommissioned. Wildcards persist because they're the fastest way to unblock a deploy under time pressure, and nobody circles back to tighten them. In a supply chain attack, that means a single compromised build token or leaked access key doesn't just expose one service — it exposes everything the wildcard touches.
How do attackers escalate privilege through IAM misconfigurations?
Attackers escalate privilege by chaining together individually low-risk IAM permissions until they reach AdministratorAccess. Rhino Security Labs cataloged this systematically in a 2018 research release, "AWS IAM Privilege Escalation – Methods and Mitigation," documenting 21 distinct escalation paths that are still relevant today. A representative chain: a user with only iam:CreatePolicyVersion and iam:SetDefaultPolicyVersion on their own attached policy can create a new policy version granting themselves full admin rights, then set it as the default version — no iam:CreateUser or iam:AttachUserPolicy required. Another well-known chain combines iam:PassRole with lambda:CreateFunction and lambda:InvokeFunction: the attacker passes a high-privilege execution role to a new Lambda function, invokes it, and inherits that role's permissions inside the function's runtime. Neither chain looks dangerous in isolation during a policy review — each permission is narrow. The danger is combinatorial, which is exactly why manual policy audits miss it and why these chains show up in nearly every published IAM privilege escalation cheat sheet since 2018.
Why do overly permissive trust policies enable cross-account attacks?
Overly permissive trust policies enable cross-account attacks because a wildcard Principal or a missing ExternalId/aws:SourceAccount condition lets a role be assumed by parties it was never meant to trust. A trust policy such as:
{
"Effect": "Allow",
"Principal": {"AWS": "*"},
"Action": "sts:AssumeRole"
}
allows any AWS account on the planet to call sts:AssumeRole against that role, subject only to the target account's own IAM policy — and in practice, that secondary check is often just as loose. This is the AWS variant of the "confused deputy" problem: a service or account is tricked into using its own broad permissions on an attacker's behalf. AWS's own documentation flags this explicitly for cross-service scenarios like S3 event notifications to SNS/SQS and CloudTrail log delivery, recommending aws:SourceArn and aws:SourceAccount condition keys to pin trust to a specific resource rather than an entire service namespace. Third-party SaaS integrations that request sts:AssumeRole access are a frequent source of this pattern: an organization grants a vendor's account ID trust with no ExternalId condition, and if that vendor's own AWS account is later compromised, the attacker inherits a direct path into every customer environment that trusted it — a variant of the confused-deputy risk that drove AWS to require ExternalId for third-party role assumption in the first place.
What real-world breaches trace back to IAM misconfiguration?
Real-world breaches trace back to IAM misconfiguration repeatedly because the pattern is cheap to introduce and expensive to detect after the fact. Beyond Capital One, Imperva disclosed in August 2019 that an AWS API key with broad access had been exposed, allowing an attacker to reach a database snapshot containing customer emails and hashed passwords for its Cloud WAF product — the key had permissions far beyond what the compromised service actually required. In the December 2021 Codecov supply chain incident, attackers modified a Bash Uploader script to exfiltrate CI environment variables, including AWS credentials with permissions broad enough to reach customer S3 buckets and downstream secrets at companies like HashiCorp and Twilio — a single overprivileged token turned one compromised build tool into a multi-company breach. And in the September 2022 Uber breach, an attacker who gained initial access through MFA fatigue found hardcoded credentials in an internal PowerShell script that granted access to the company's PAM vault, which in turn held AWS and GCP administrative credentials — a privilege chain identical in shape to the IAM escalation paths Rhino Security Labs documented four years earlier, just executed against a different credential store.
Which IAM condition keys most often prevent misconfiguration-driven breaches?
The condition keys that most often prevent these breaches are aws:SourceArn, aws:SourceAccount, aws:PrincipalOrgID, and aws:MultiFactorAuthPresent. aws:SourceArn and aws:SourceAccount close the confused-deputy gap by pinning a resource policy's trust to one specific calling resource or account rather than an entire AWS service — the difference between "any S3 bucket in any account can invoke this Lambda" and "only bucket X in account Y can." aws:PrincipalOrgID scopes cross-account and resource-based access to a specific AWS Organization, which stops an otherwise-correct policy from being usable by an attacker who stands up a lookalike account outside the org. aws:MultiFactorAuthPresent on sensitive actions like iam:DeleteRole, sts:AssumeRole into break-glass roles, or s3:DeleteObject on backup buckets means a stolen long-lived credential still isn't enough to complete a destructive action. None of these are exotic — they've all been in the IAM condition key reference since well before 2020 — but Datadog's cloud security research has consistently found that the large majority of policies capable of using them omit them entirely, which is the gap between a policy that looks scoped on paper and one that's actually enforced at request time.
How Safeguard Helps
Safeguard's Griffin AI parses IAM policy documents, trust relationships, and resource policies together as a graph, surfacing the exact privilege-escalation chains and confused-deputy paths described above instead of flagging each wildcard or missing condition key in isolation. Reachability analysis correlates that policy graph with what your workloads actually call at runtime and what code paths are deployed, so a role with 40 unused permissions gets prioritized differently than one where every permission maps to live, exploitable application code. SBOM generation and ingest extend that same graph to the packages and infrastructure-as-code modules that provisioned the roles in the first place, so a vulnerable dependency and an overprivileged execution role tied to it show up as one connected finding, not two disconnected alerts. When Safeguard identifies an over-permissioned policy, missing condition key, or wildcard trust relationship, it opens an auto-fix pull request with the least-privilege replacement policy already written, so your team reviews a scoped diff instead of hand-authoring IAM JSON under pressure.