Prevent privilege escalation in AWS IAM by enforcing least‑privilege policies, using condition keys, and regularly auditing policy changes.
Step‑by‑step mitigation
1. Identify all IAM entities – Run aws iam list-users, list-roles, list-groups and export to CSV.
2. Enable IAM Access Analyzer – aws accessanalyzer create-analyzer --type ACCOUNT --analyzer-name org-analyzer.
3. Apply permission boundaries – Attach a boundary that caps actions to the minimal set, e.g. arn:aws:iam::aws:policy/ReadOnlyAccess.
4. Use explicit deny with condition keys – Deny iam: unless aws:PrincipalTag/role=admin and request originates from a trusted VPC endpoint.
5. Restrict wildcard resources – Replace "Resource": "" with ARN‑scoped values; for S3 use arn:aws:s3:::my-bucket/.
6. Automate drift detection – Set up Config rule iam-policy-no-statements-with-admin-access and trigger a Lambda remediation.
7. Schedule periodic reviews – Use Security Hub custom insight IAM Privilege Escalation and require approval for any policy change via CodeCommit pull‑request checks.
Quick comparison
| Technique | Scope | Typical Cost |
|-------------------------|----------------|--------------|
| Permission boundary | Role/User | Free |
| Service control policy | Org unit | Free |
| Condition‑based deny | Policy statement| Free |
Example policy snippet
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Deny",
"Action": "iam:*",
"Resource": "*",
"Condition": {
"StringNotEquals": {
"aws:PrincipalTag/role": "admin"
},
"StringNotEqualsIfExists": {
"aws:SourceVpce": "vpce-0abcd1234efgh5678"
}
}
}
]
}Gotcha: A role that can sts:AssumeRole on any ARN combined with iam:PassRole on creates an unchecked escalation path; always pair AssumeRole permissions with a resource‑restricted PassRole condition.