How to audit AWS IAM and resource policies


RSS feed

AWS IAM and resource policies can be difficult to write and understand. AWS themselves periodically get them wrong, so I’ll be using current and past examples from the AWS documentation of places where they’ve made mistakes and I’ve contacted AWS to have them corrected (some still unfixed). I also often find incorrect policies when I do assessments for clients, sometimes due to policies they’ve written themselves, and sometimes, unfortunately, because they copied the policies from AWS documentation that were incorrect or that they misunderstood the impact of.

AWS IAM policies describe what actions principals (users and roles) are allowed to take on what resources and under what conditions. These IAM policies are applied to the principals directly or indirectly via Groups. Resource policies look very similar, but are applied to resources, and additionally describe a Principal that is allowed to act on the resource. The Principal in a resource policy can end up being anyone which means any other AWS account or even anonymous public access.

You see resource policies frequently with S3 buckets, but they can also be applied to ECR (Elastic Container Registry), Lambda functions, API Gateway, Mediastore, ElasticSearch, Glue, SNS, SQS, SES, Step Functions, Glacier storage, Cloud9, KMS, Secrets Manager, and the new AWS Backup and Worklink services (see AWS’s list here). There are additional places with things similar to these policies, but often with other restrictions on what can be in these policies, such as CloudWatch Events Bus policies, AWS Organization SCPs, IAM Role assumption policies, and more.

Here are some general rules:

  1. Beware that anything with Allow and Principal “*” is public
  2. Never use Allow with NotPrincipal
  3. Never use Allow with NotAction

Beware that anything with Allow and Principal “*” is public

This example comes from the current AWS docs for Glacier at Overview of Managing Access Permissions to Your Amazon S3 Glacier Resources. This does explain that this policy is making the resource public, but I believe AWS should remove this example, as you likely do not ever want your backups to be publicly accesible. I’ve seen this on client assessments, and I mentioned this previously in my post Beyond S3: Exposed Resources on AWS a year ago.

Glacier public policy

Glacier is a storage service, similar to S3, that is primarily used for making long-term backups of infrequently accessed data. The impact of the policy might not be understood because of the use of the actions InitiateJob and GetJobOutout, but these translate to meaning full read access.

Similarly, for ECR (a service for storing Docker repos) the docs have an example like this (here). There may be cases for making an ECR public, but again, I’ve seen this policy in assessments where the client viewed it as a high severity finding.

ECR public policy

I use a policy like this in one of the levels of the free AWS security training

Understand your conditions

Sometimes you’ll see a policy with a Principal of “*” that is restricted via conditions, such as an IP restriction (this comes from here with some simplification):

  "Version": "2012-10-17",
  "Id": "S3PolicyId1",
  "Statement": [
      "Sid": "IPAllow",
      "Effect": "Allow",
      "Principal": "*",
      "Action": "s3:*",
      "Resource": "arn:aws:s3:::examplebucket/*",
      "Condition": {
         "IpAddress": {"aws:SourceIp": ""},

This will correctly enforce that only the specified IP is allowed access to this S3 bucket, but no authentication will be used, so this is still risky and not ideal.

Here is a more dangerous example that is currently in the AWS docs here, again for Glacier.

Glacier conditioned policy

This policy is bad because it allows the backups to be deleted by anyone as long as they are less than a year old and do not have the tag LegalHold on them. Someone would need to guess the name of the backups and the account ID, but this is still dangerous.

Never use Allow with NotPrincipal

NotPrincipal + Allow always results in anonymous access. It may be possible to use this combination safely, but I’ve never seen it. People incorrectly assume it is better restricted than it is. This is basically the same as Allow with a Principal of “*”.

Never use Allow with NotAction

NotAction with Allow almost always results in admin privileges. You can see AWS using NotAction with Allow in their Managed policy PowerUserAccess. They allow all actions except some limited use of iam, organizations, and the new accounts service. Although this might not seem like the full access of the AdministratorAccess policy, ask yourself what is this protecting against? If this user is compromised, they can do nearly anything they want in the account, such as delete all resources, exfil all data, decrypt any encrypted with KMS, etc. Further, realize that these policies may need to be updated regularly as new services are added. For example, this policy had to be updated last week for the new accounts service that AWS added.

When is someone an admin?

A similar problem I see is an IAM user will be given something like s3:* and ec2:* in an account, and this user will be thought of as a restricted user. In this account though, the only resources that exist are S3 buckets and EC2 instances, so this effectively granted them full privileges over all of the resources in the account, which in my opinion, makes them an admin.

Other things to watch out for

Beware of how policies interact with each other

AWS used to recommend an MFA policy that was supposed to allow a limited number of IAM actions for a user in order to change their password and add their MFA device, but then anything else was denied. This was accomplished with the following section at the end of their MFA policy.

MFA policy

The problem was, most people used this policy to secure their admins, that had Action * access. This meant that the iam:* actions were not restricted by the MFA requirement, which meant that if an access key of these admins was compromised, the attacker could simply remove this restrictive policy from themselves so they could perform any action without MFA.

Beware of who can access what principals

A problem I’ve seen a few times is IAM roles with full admin privileges being used by EC2 instances. This means that anyone that can access that EC2 instance has full admin privileges in the account. There is a security strategy people use of having a single EC2 that people perform all their AWS work from them, but in that case you need to strongly control who has access to that EC2.

Beware of privilege escalation

A simple rule of thumb is any ability to modify IAM will result in a privilege escalation. In AWS’s blog post Easier way to control access to AWS regions using IAM policies, the presented policy originally included this section which was referred to in the article as being “read-only”.

Privilege escalation

The danger here is the inclusion of iam:AttachRolePolicy. This policy allowed for someone to attach the AdministratorAccess policy to the role, thereby escalating their privileges. The folks at Rhino Security have an exhaustive list with examples of how any ability to change IAM can result in privilege escalation in their write-up AWS IAM Privilege Escalation – Methods and Mitigation.


The above issues are the main gotchas I notice with AWS IAM and resource policies. In order to apply an effective Least Privilege strategy, you will still want to restrict access down to specific actions and resources.

To audit your own environments, I recommend using CloudMapper, which I maintain. CloudMapper’s find_admins command identifies any effective admins, along with any places where privileges can be escalated, any compute resources such as EC2 instances that have admin privileges, and some other IAM issues. Eventually these checks will be moved into the auditing code so that when you generate a report with findings, these will be listed there. This report currently already checks many of the resources (such as Glacier, ECR, and more) to see if they are public. CloudMapper also has an iam_report command to create a report of all the IAM information in the account, including Access Advisor information, which can be used to manually check for issues and give you some of the information you need, with generated charts, to implement a Least Privilege strategy.