Most AWS breaches do not need an exploit: they use a valid access key, an overly permissive role or a trust policy that lets the wrong principal in, all of which are IAM configuration choices someone made and nobody revisited. AWS publishes clear guidance on root user protection, temporary credentials, least privilege and guardrails, yet the same AWS IAM mistakes appear in account after account because convenience wins at build time and nobody owns cleanup. This top 10 AWS IAM mistakes list ranks common AWS IAM mistakes and AWS IAM anti patterns by our judgement of how often they appear and how much access they hand an attacker, with the ATT&CK v20 technique, the fix AWS recommends, the CIS AWS Foundations Benchmark check that catches each AWS IAM misconfiguration, and what to watch in CloudTrail. It is for cloud security engineers, platform teams and anyone auditing an AWS organisation. For policy types, evaluation logic and trust policy syntax, read the AWS IAM Policies and Roles Guide; this page is about what goes wrong.
The Top 10 at a glance
- Using the root user, and root user access keys
- Long-lived access keys where a role would work
- IAM users and passwords for people, without MFA
- Wildcard and admin-equivalent policies on workloads
- Never right-sizing AWS managed policies and unused access
- Weak role trust policies
- Unrestricted iam:PassRole and IAM write permissions
- Public and cross-account resource policies nobody checks
- No organisation guardrails or permissions boundaries
- Instance role credentials exposed through IMDSv1
| # | Mistake | How attackers use it (ATT&CK v20) | First fix |
|---|---|---|---|
| 1 | Root user in use | Full account takeover with root credentials (T1078.004) | Delete root keys; MFA; centralise root access |
| 2 | Long-lived access keys | Find keys in code and files, then use them (T1552.001, T1078.004) | Roles and federation; deactivate unused keys |
| 3 | Human IAM users | Phish or guess passwords for console users (T1078.004) | IAM Identity Center with phishing-resistant MFA |
| 4 | Wildcard policies | One stolen credential reaches everything | Scope actions and resources; validate policies |
| 5 | Unused permissions | Dormant rights widen the blast radius of any compromise | Access Analyzer unused access and policy generation |
| 6 | Weak trust policies | Assume a role from outside the organisation (T1199) | External ID, source conditions, OIDC sub limits |
| 7 | PassRole and IAM write | Grant themselves keys or roles (T1098.001, T1098.003) | Scope PassRole by role ARN and service |
| 8 | Unchecked resource policies | Read data from public or cross-account buckets (T1530) | Access Analyzer external access; Block Public Access |
| 9 | No guardrails | Disable logging or act in unused Regions (T1685) | SCPs, RCPs and permissions boundaries |
| 10 | IMDSv1 | Steal role credentials from the metadata service (T1552.005) | Require IMDSv2 at account level |
1. Using the root user, and root user access keys
The mistake: the AWS root account usage pattern of the account's early days survives: root signs in for routine tasks, its password sits with one person, MFA is a single device or missing, and sometimes root user access keys exist for an old script.
Why it matters: root cannot be limited by IAM policies in its own account, and AWS warns against root keys "because the root user has full access to all AWS services and resources in the account, including billing information". A root key in code is a whole-account key.
The fix: AWS's root user guidance: do not create root access keys; register multiple MFA devices (up to eight are supported); use a business-managed group email address for the root user; and "Centralize root access and remove root user credentials from member accounts", after which member accounts cannot sign in as root or recover its password. Use root only for the tasks that require it, and protect the management account's root as a break-glass credential. See the Break-Glass Account Guide.
Detect: CloudTrail ConsoleLogin and any API call where userIdentity.type is Root; GuardDuty Policy:IAMUser/RootCredentialUsage. Security Hub controls IAM.4 (no root access key), IAM.9 (root MFA) and IAM.6 (hardware MFA for root) map to CIS AWS Foundations v5.0.0 requirements 1.3, 1.4 and 1.5.
2. Long-lived access keys where a role would work
The mistake: IAM users created for applications, CI pipelines, scripts and on-premises tools, with access keys in configuration files, environment variables and repositories, often years old and shared between systems.
Why it matters: a static key works from anywhere until someone deactivates it. Attackers search code, images and files for keys (T1552.001 Credentials In Files) and use them as valid cloud accounts (T1078.004). The Uber 2016 breach began with an AWS key in a private repository, the Toyota T-Connect key sat on public GitHub for five years, and the Codefinger S3 ransomware used stolen keys to encrypt buckets.
The fix: AWS says to "Require workloads to use temporary credentials with IAM roles", noting "there is no need to distribute long lived credentials for an IAM user to your workloads running on AWS". Use instance profiles, task and function roles, IAM Roles Anywhere or OIDC federation for external systems. Where a key must remain, give it an owner, store it in a secrets manager, rotate it and use "IAM access last used information to update and remove access keys safely". See the Cloud Workload Identity Guide.
Detect: the IAM credential report for key age and last use; Security Hub IAM.3 (keys rotated every 90 days or less, CIS 1.13) and IAM.22 (credentials unused for 45 days removed, CIS 1.11); CloudTrail CreateAccessKey events, alerting on any outside your provisioning pipeline.
3. IAM users and passwords for people, without MFA
The mistake: engineers and administrators have IAM users with console passwords in each account instead of signing in through the corporate identity provider, so leavers keep access until someone remembers, and MFA depends on each user enrolling.
Why it matters: these accounts sit outside joiner-mover-leaver processes and conditional access, and a phished or reused password plus a missing second factor gives a direct console session.
The fix: "Require your human users to use temporary credentials when accessing AWS", through IAM Identity Center or federation with your identity provider. For the few IAM users that remain, AWS recommends "phishing-resistant MFA such as passkeys and security keys wherever possible". Attach permissions through groups or permission sets, not to users directly.
Detect: Security Hub IAM.5 (MFA for every IAM user with a console password, CIS 1.9) and IAM.2 (no policies attached directly to IAM users, CIS 1.14); GuardDuty UnauthorizedAccess:IAMUser/ConsoleLoginSuccess.B; CloudTrail CreateLoginProfile and UpdateLoginProfile.
4. Wildcard and admin-equivalent policies on workloads
The mistake: the overly permissive role: "Action": "*" with "Resource": "*", AdministratorAccess attached to an application or pipeline role "to get it working", or service-wide wildcards such as s3:* and iam:* on roles that need three actions on one bucket.
Why it matters: least privilege only limits damage if it is real. A wildcard policy turns any credential theft, from a leaked key to a compromised container, into an account-wide incident.
The fix: "grant only the permissions required to perform a task": name actions and resource ARNs, and add conditions (AWS gives requiring TLS as an example). Run IAM Access Analyzer policy validation, which "provides more than 100 policy checks and actionable recommendations", on every policy in your pipeline, not only in the console. Keep administrator roles for people, behind just-in-time elevation. See the Cloud PAM and CIEM Guide.
Detect: scan customer managed and inline policies for wildcard actions on write-capable services; alert on AttachRolePolicy with AdministratorAccess to a role that is not an approved administrator role.
5. Never right-sizing AWS managed policies and unused access
The mistake: roles start on broad AWS managed policies, which is reasonable, and stay on them forever; roles, users and keys for finished projects are never removed.
Why it matters: AWS itself warns that managed policies "might not grant least-privilege permissions for your specific use cases". Unused rights and identities are pure risk: they deliver no value and are rarely monitored.
The fix: use IAM Access Analyzer to generate a policy from a role's CloudTrail activity, test it, then replace the managed policy. Turn on an organisation-scope unused access analyzer, which reports "Unused roles", "Unused IAM user access keys and passwords" and "Unused permissions" over a usage window you set. AWS notes external access findings are free but unused access findings are charged per role and user analysed, so budget for it. Remove what is unused through change control, deactivating before deleting.
Detect: trend open unused access findings per account; last accessed information per role and service.
6. Weak role trust policies
The mistake: trust policies that trust a whole external account when one role was meant, roles for third-party suppliers with no external ID, service principals trusted without source conditions, and OIDC roles for CI systems that accept any repository.
Why it matters: the trust policy decides who can become the role. A loose one hands the role to someone outside your organisation (T1199 Trusted Relationship). AWS describes the confused deputy problem as one where "an entity that doesn't have permission to perform an action can coerce a more-privileged entity to perform the action".
The fix: for third parties, require sts:ExternalId; AWS says the value "must be unique among Example Corp's customers and controlled by Example Corp, not its customers". Where an AWS service principal is granted access to your resources, add aws:SourceArn, aws:SourceAccount, aws:SourceOrgID or aws:SourceOrgPaths. For GitHub Actions OIDC roles, AWS warns that if token.actions.githubusercontent.com:sub is not limited to your organisation or repository, "GitHub Actions from organizations or repositories outside of your control are able to assume roles"; IAM now rejects a GitHub trust policy without that key or with a wildcard-only value, but older broad values such as a whole organisation still deserve review. See the Third-Party Access Guide and the CI/CD Pipeline Identity Security Guide.
Detect: Access Analyzer external access findings on roles; CloudTrail UpdateAssumeRolePolicy and AssumeRole calls from accounts outside your organisation.
7. Unrestricted iam:PassRole and IAM write permissions
The mistake: developers and automation hold iam:PassRole on *, or IAM write actions such as CreatePolicyVersion, AttachRolePolicy, PutRolePolicy, UpdateAssumeRolePolicy or CreateAccessKey on other users, without anyone treating those as administrator rights.
Why it matters: each is a privilege escalation path. AWS's example: a user without S3 rights who "could pass a role to a service that allows Amazon S3 actions" gets those actions through the service. IAM write access lets an attacker add credentials or roles to keep access (T1098.001 Additional Cloud Credentials, T1098.003 Additional Cloud Roles).
The fix: scope PassRole with the Resource element to named role ARNs or a prefix, and add iam:PassedToService to limit which service can receive the role. Treat any IAM write permission as administrative, and delegate role creation only inside a permissions boundary (mistake 9). The AWS IAM Policies and Roles Guide covers the policy patterns.
Detect: PassRole is a permission check, not its own CloudTrail event, so watch the calls that pass roles (RunInstances, CreateFunction and similar) with high-privilege role ARNs, plus CreatePolicyVersion, SetDefaultPolicyVersion and CreateAccessKey for another user.
8. Public and cross-account resource policies nobody checks
The mistake: S3 bucket policies, KMS key policies, Lambda, SNS and SQS resource policies granting access to other accounts or to everyone, added for a migration, an analytics partner or a test, and never reviewed because they are not in IAM.
Why it matters: resource policies grant access on their own; an attacker does not need a credential in your account to read a bucket your policy opens (T1530 Data from Cloud Storage).
The fix: AWS says "Before you grant permissions for public or cross-account access in AWS, we recommend that you verify if such access is required." Run an IAM Access Analyzer external access analyzer with your organisation as the zone of trust, so every grant to a principal outside it becomes a finding to resolve or archive with a reason. Enable S3 Block Public Access at account level, and add resource control policies for organisation-wide data perimeters.
Detect: Security Hub IAM.28 (external access analyzer enabled, CIS 1.19); CloudTrail PutBucketPolicy, PutKeyPolicy and DeleteBucketPublicAccessBlock.
9. No organisation guardrails or permissions boundaries
The mistake: everything runs in one or two accounts, or in many accounts with no service control policies, so any administrator in any account can stop CloudTrail, leave the organisation, create IAM users or work in Regions nobody watches.
Why it matters: without guardrails a single compromised administrator role can blind you first (T1685 Disable or Modify Tools) and act second. Without boundaries, teams that create roles can create roles more powerful than their own.
The fix: separate workloads into accounts in AWS Organizations, following the AWS Security Reference Architecture, and "use AWS Organizations service control policies (SCPs) to establish permissions guardrails to control access for all principals": deny CloudTrail and GuardDuty changes, leaving the organisation, unused Regions and IAM user key creation where policy forbids it. Remember AWS's caveat that "No permissions are granted by SCPs and RCPs", so they complement least privilege rather than replace it. Use permissions boundaries "to set the maximum permissions that you delegate". See the AWS Security Guide.
Detect: Security Hub CloudTrail.1 (multi-Region trail with read and write management events, CIS 3.1); GuardDuty Stealth:IAMUser/CloudTrailLoggingDisabled; CloudTrail StopLogging, DeleteTrail and DeleteDetector.
10. Instance role credentials exposed through IMDSv1
The mistake: EC2 instances still allow version 1 of the instance metadata service, and the instance role attached to a web-facing server has broad S3 or IAM permissions.
Why it matters: a server-side request forgery flaw can make the instance fetch its own role credentials and hand them to the attacker (T1552.005 Cloud Instance Metadata API), as in the Capital One breach, where a firewall's role credentials exposed data on 106 million people.
The fix: require IMDSv2 (HttpTokens set to required). Set the account default so new instances launch with IMDSv2 required, then use account-level enforcement, under which "instances can only launch if they're configured to require IMDSv2", or a declarative policy across the organisation. The account default "does not reset existing instances", so update those separately after testing software that reads metadata. Scope instance roles to the instance's own resources.
Detect: GuardDuty UnauthorizedAccess:IAMUser/InstanceCredentialExfiltration.OutsideAWS for instance credentials used from outside AWS; an inventory of instances with HttpTokens set to optional.
Mapping to OWASP NHI Top 10, ATT&CK and CIS AWS Foundations
The OWASP Top 10 for web applications does not fit cloud IAM configuration well, so this table maps each mistake to the OWASP Non-Human Identities Top 10 (2025), which covers the machine credentials and workload roles at the centre of most of these mistakes.
| Mistake | OWASP NHI Top 10 (2025) | ATT&CK v20 | Security Hub control (CIS AWS Foundations v5.0.0) |
|---|---|---|---|
| 1. Root user | NHI7 Long-Lived Secrets (root keys) | T1078.004 | IAM.4 (1.3); IAM.9 (1.4); IAM.6 (1.5) |
| 2. Long-lived keys | NHI2 Secret Leakage; NHI7 Long-Lived Secrets | T1552.001, T1078.004 | IAM.3 (1.13); IAM.22 (1.11) |
| 3. Human IAM users | NHI4 Insecure Authentication; NHI10 Human Use of NHI (shared IAM users) | T1078.004 | IAM.5 (1.9); IAM.2 (1.14) |
| 4. Wildcard policies | NHI5 Overprivileged NHI | T1078.004 | No v5.0.0 mapping (IAM.1 maps only to CIS 1.2.0 and 1.4.0) |
| 5. Unused access | NHI1 Improper Offboarding; NHI5 Overprivileged NHI | T1078.004 | IAM.22 (1.11) |
| 6. Weak trust policies | NHI3 Vulnerable Third-Party NHI; NHI4 Insecure Authentication | T1199 | IAM.28 (1.19) |
| 7. PassRole and IAM write | NHI5 Overprivileged NHI | T1098.001, T1098.003 | None |
| 8. Resource policies | NHI6 Insecure Cloud Deployment Configurations | T1530 | IAM.28 (1.19) |
| 9. No guardrails | NHI8 Environment Isolation | T1685 | CloudTrail.1 (3.1) |
| 10. IMDSv1 | NHI6 Insecure Cloud Deployment Configurations | T1552.005 | None in the IAM section |
In NIST SP 800-53 Rev. 5 terms, mistakes 4, 5 and 7 sit under AC-6 Least Privilege, 2 and 3 under IA-2 and IA-5, and 9 under AC-6 and AU-9 Protection of Audit Information. AWS Security Hub CSPM supports CIS AWS Foundations Benchmark versions 5.0.0, 3.0.0, 1.4.0 and 1.2.0; the requirement numbers above are from the v5.0.0 mapping.
Where to start
Most of these AWS IAM pitfalls are cheap to find and slow to fix, so start with exposure, not elegance. If you can do only three things this quarter: delete any root access keys and centralise root access (mistake 1), turn on organisation-wide Access Analyzer external access findings and resolve them (mistakes 6 and 8), and stop new long-lived keys by moving people to IAM Identity Center and workloads to roles (mistakes 2 and 3). Then work through the rest:
- Baseline (weeks 1 to 2): pull the credential report from every account, enable Security Hub CIS AWS Foundations v5.0.0 and an organisation trail, and list every IAM user, access key, role trust policy naming an external account, and instance with IMDSv1 allowed.
- Quick wins (weeks 2 to 6): root keys deleted and root MFA confirmed; IAM users with console passwords moved to Identity Center; keys unused for 45 days deactivated; external access findings triaged.
- Guardrails in audit first (months 2 to 3): write SCPs and IMDSv2 enforcement, apply them to a sandbox organisational unit, watch CloudTrail for AccessDenied errors for [two weeks], then extend OU by OU. Rollback is detaching the SCP from the OU.
- Least privilege (months 3 to 9): generate policies from activity for the busiest workload roles, scope PassRole, and work down unused permission findings account by account.
- Sustain: policy validation in the pipeline, unused access reviewed monthly, and a cloud IAM review each half year. See the Access Reviews Guide.
Small organisations with a few accounts can reach most of this with native services and a quarterly review. Large organisations need delegated administration of Access Analyzer and Security Hub, a CIEM-style view across accounts, and named owners for every workload role. Track five numbers monthly: root access keys (target zero), IAM user access keys older than 90 days, roles with wildcard admin policies outside approved admin roles, open external access findings, and instances not requiring IMDSv2.
Standards and references
- AWS: Security best practices in IAM
- AWS: Root user best practices for your AWS account
- AWS: Findings for external, internal and unused access in IAM Access Analyzer
- AWS: The confused deputy problem
- AWS: Security Reference Architecture (AWS SRA)
- AWS: CIS AWS Foundations Benchmark in Security Hub CSPM
- OWASP: Non-Human Identities Top 10 (2025)
- MITRE ATT&CK: T1078.004 Valid Accounts: Cloud Accounts
Related NHI Mgmt Group resources: AWS IAM Policies and Roles Guide · AWS Security Guide · Cloud PAM and CIEM Guide · Top 10 NHI Issues · Service Account Security Guide · Cloud IAM Review Checklist