AWS IAM Best Practices to Stop Privilege Escalation
A build agent’s credential should be able to upload artifacts and read its own configuration. Nothing more. On paper that is a short, auditable list.
In practice the policy attached to it often reads "Action": "*", "Resource": "*", because a wildcard was close enough to what the pipeline needed and nobody came back to narrow it. That credential is now an administrator wearing a boring name, and the person who granted it never has to know.
The attacker who finds it does not need to hold the privileged role directly. They only need to pass it to something that will run with it, which is why this class of failure survives a policy review that only checks who holds the admin role.
What is AWS IAM security?
Section titled “What is AWS IAM security?”AWS IAM security is the practice of granting cloud identities, users, roles, and services, only the permissions their actual job requires, then verifying that scope holds even after a role, a key, or an account is compromised. It covers policy scope, key lifecycle, instance credential exposure, function permissions, and the trust that lets one account assume a role in another.
The CIS AWS Foundations Benchmark treats identity and access management as its own control domain for exactly this reason. Most cloud incidents are not a new exploit against AWS itself. They are a permission that was granted correctly and used by someone it was never meant for.
What are the main IAM privilege escalation risks?
Section titled “What are the main IAM privilege escalation risks?”Grouped by where the trust actually breaks, so each one maps to a different owner and a different fix.
Policy scope: wildcards and role-passing
Section titled “Policy scope: wildcards and role-passing”A policy that grants every action on every resource is unauditable by construction. Worse, iam:PassRole lets a caller hand a privileged role to a service that will execute with it, so the caller escalates without ever assuming that role themselves. The over-permissive IAM exercise has you read a build agent’s policy, pass a break-glass administrator role into a function you create, and then rewrite the policy down to named actions on named resources with a permission boundary underneath it.
Key lifecycle: long-lived and leaked credentials
Section titled “Key lifecycle: long-lived and leaked credentials”A static access key has no expiry, so the gap between exposure and abuse is bounded only by whoever notices first. Keys leak into build logs, client-side bundles, and old commits, and they still authenticate months later because nothing about a static key changes with time. The long-lived access keys exercise has you replay a key pulled from a spring build log against an autumn production account, then replace it with federation that issues short-lived credentials per run instead of a fresher static one.
Instance identity: metadata service credential theft
Section titled “Instance identity: metadata service credential theft”Any process running on a cloud instance can ask the instance metadata service for that instance’s credentials, and by default the service does not check who is asking. A request-forging bug that would otherwise be minor becomes a way to steal a role’s full credential set. The instance metadata abuse exercise walks a URL-fetching feature into exactly that theft, then closes it with a session-token requirement and a hop limit of one.
Function identity: serverless over-privilege
Section titled “Function identity: serverless over-privilege”A function is not a script. It is an identity, and whatever its execution role allows, anyone who can trigger the function can do.
An image resizer with account-wide storage access is a much larger blast radius than the thumbnail bucket it actually touches. The serverless over-privilege exercise has you reach an unauthenticated function URL, pull a database credential out of its environment variables, and then scope the role down to the one bucket the function needs and move the secret into a managed store.
Account boundaries: cross-account trust
Section titled “Account boundaries: cross-account trust”An account is the strongest blast-radius boundary the cloud gives you, but only if the trust policies pointing into it name a specific role rather than an entire account. A sandbox credential with no valuable data of its own becomes dangerous the moment it can assume a production role, because sandbox accounts are rarely watched as closely as production. The multi-account boundaries exercise has you walk that exact path, then put a service control policy above the account so the ceiling holds even against a production administrator trying to widen it back.
Which AWS IAM security best practices should you apply first?
Section titled “Which AWS IAM security best practices should you apply first?”Ordered by how much escalation risk each one removes, not by how it reads in a compliance checklist.
- Write policies by enumerated action and resource, never wildcard both. A list you can read in full is a list you can audit. A wildcard is a promise nobody can verify.
- Add a permission boundary on top of every enumerated policy. It catches the wildcard someone reaches for under deadline pressure, which is when the original mistake usually happens again.
- Replace static keys with federated, short-lived credentials wherever a workload supports it. A key that expires on its own cannot be replayed months after it leaked.
- Require IMDSv2 and set the hop limit to one. A session-token exchange stops a single forged request from reaching the metadata service at all.
- Scope every serverless execution role to the specific resource the function touches. Account-wide storage or database access is almost never what the function itself needs.
- Move secrets out of environment variables and into a managed secret store the function reads at runtime. That puts every access in the audit trail and makes rotation possible without a redeploy.
- Name a specific role in every cross-account trust policy, and require an external ID. A trust policy that names an entire account hands over production to anything that account ever runs.
- Put a service control policy above the account for anything a compromised administrator should never be able to undo. Fixes made inside the account can be reversed from inside the account. An SCP evaluated above it cannot.
How do you train engineers on AWS IAM security?
Section titled “How do you train engineers on AWS IAM security?”A checklist teaches compliance. It does not teach the judgment call at 6pm when a deploy is failing on a permission error and the fastest fix is a wildcard. Someone who has never seen what that wildcard grants will reach for it anyway.
What changes that is running the escalation path yourself. An engineer who has passed a role into their own function and watched it become an administrator writes a narrower policy afterward, without being told to.
That is the shape of our Cloud Infrastructure Security exercises, the nine we shipped alongside the existing container course under the same Cloud Security category. Each one puts you on the attacking side first, then has you ship the fix and prove the same path no longer works.
Our container security best practices guide covers the adjacent surface, where the same over-broad permissions show up as a container running with more access than its image needs. If you would rather start from the exposure side of cloud security, AWS cloud security misconfigurations to fix first covers the buckets, networks, and audit trails that IAM fixes alone will not close.
Frequently asked questions
Section titled “Frequently asked questions”Is a wildcard IAM policy ever acceptable?
Rarely, and never on both the action and the resource at once. A policy that grants one wildcarded action against a specifically named resource is auditable. A policy that wildcards both is an unwritten administrator grant, and it should always carry a permission boundary underneath it.
Why does rotating a leaked access key matter if it is already revoked?
Revocation stops the specific key. It does nothing about what that key already read before you noticed, and a static replacement key just resets the same clock. Federated, short-lived credentials remove the class of problem instead of resetting it.
Does requiring IMDSv2 fully close instance metadata abuse?
It closes the single-request version, which is most of what request-forging bugs can reach. A hop limit of one is the other half, because it stops a response from a chained request leaving the instance. Neither change requires touching the application code that had the original bug.
How is a serverless execution role different from a normal IAM role?
The trigger is different, not the risk. Anyone who can invoke the function inherits whatever the role allows, so an unauthenticated function URL with a broad role is equivalent to a broad role with no login screen in front of it at all.
Why does cross-account trust need a service control policy on top of a scoped trust policy?
Because a scoped trust policy is a rule inside the account, and a rule inside the account can be changed from inside the account, including by an administrator who was themselves compromised. A service control policy is evaluated above the account, so the same change is refused regardless of who inside the account tries to make it.
Start with one policy
Section titled “Start with one policy”Pull the policy attached to your CI or build agent’s credential and read every action and resource in it. If either side is a wildcard, you have found the same bug that opens this article, and it is probably attached to more than one identity.
Our Cloud Infrastructure Security exercises are free to try, and you can talk to us about rolling them out across an engineering team.