avatarTeri Radichel

Summarize

To NotResource or To Not NotResource In an AWS IAM Policy

ACM.131 Prevent our IAM Users from changing their own role, policies, and group deployed by the ROOT CloudFormation stacks

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

⚙️ Check out my series on Automating Cybersecurity Metrics. The Code.

🔒 Related Stories: AWS Security | Cloud Security Architecture | IAM

💻 Free Content on Jobs in Cybersecurity | ✉️ Sign up for the Email List

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

In our last post we started exploring how to prevent IAM administrators from changing their own policies and how they might escalate privileges.

One of the IAM Policy elements we may be able to use to limit permissions in an IAM policy is NotResource:

With NotResource, we create a policy statement and indicate that it applies only if the resource the IAM principal is acting on is not the specified resource (like our IAM Administrator policy, role or group).

White Lists vs. Black Lists

We could have used the NotResource to disallow our IAM user to create or modify CloudFormation stacks with ROOT in the name. The stack we don’t want the user to modify would be the resource in the NotResource portion of the statement.

Disallowing something by blocking access to a specific resource is known as a black list. You specify what a user cannot do or access.

Alternatively we can provide a specific list of only the resources the user is allowed to access or modify. That type of security rule is known as a white list.

I covered those topics a bit more in my book at the bottom of this post.

If we only block access to the stacks with ROOT in the name (a black list), someone could simply create a new stack with a different name to get around that block. Additionally, the IAM Admins would be blocked from updating the ROOT stacks but there are other stacks we also don’t want them to update, like KMS, AppSec, etc.

A better approach is to use a white list to specify exactly which stacks a our IAM administrators are allowed to change. In our case, we don’t need a NotResource to limit access to the cloud formation stacks used to create the IAM user, policy, and group, because we added an Allow statement and specifically allow our IAM user to only change stacks with the IAM-* name. Since the stacks used to create the IAM policy, user, and group don’t start with that name, our IAM administrators cannot modify them.

This approach also disallows the user from changing other stacks such as our KMS stacks, EC2 stacks, and so on, not just the ROOT stacks.

Avoid Double Negatives

Where else could we use a NotResource? We could Deny changes to user passwords if the resource is NotResource: the user that is changing the password. However, in that case we have a double negative and overly complicated logic.

We can reduce that logic to allow a user to only change the password for the current user.

The NotResource may be useful but it also may come with loopholes if you don’t think it through carefully. It also may create overly complicated logic. In many cases, it is not the best option.

When Black Lists are Useful

There is one thing I can use this NotResource for in my quest to prevent IAM users from changing their own policies. I want IAM users to have access to create any role, policy, user, or group. I just don’t want them to be able to change their own. There is a very short list of resources the IAM users shouldn’t access and I can use the NotResource along with that list.

I can prevent IAM changes to the IAM admin user, group or policy created by the Root CloudFormation stacks by using a black list to prevent them from modifying the IAM administrator policy or group created by the root stack. (I’ll address users and role assumption separately.)

I can change this in the IAM Admins policy:

 Statement:
          - Effect: Allow
            Action: 'iam:*'
            Resource: '*'

To this (pseudocode):

 Statement:
          - Effect: Allow
            Action: 'iam:*'
            NotResource: '*IamAdmin*'

Consider future policy changes

One caveat here is that we have to make sure the name of the IAM policies don’t change if we use this approach. Let’s say for some reason our ROOT admin decided to change the name of the IAM Policies somewhere down the line to something other than IamAdmin like IdentityAdminRole and IdentityAdminPolicy. At that point the above policy would no longer prevent IAM administrators from modifying their own role and policy.

It is important to understand document nuances like this for all involved when future updates come into the picture. Perhaps we could add some comments to each policy that the name has to include IamAdmin for the policies to work correctly.

Another option: Referencing policy exports

Another option would be to use the outputs of the ROOT stacks to formulate our statement leveraging NotResource.

The first thing I need to do is add the resource ARNs to my CloudFormation scripts where they are missing. Let’s see what we can get back from a policy:

Let’s add that to the policy outputs for GroupPolicy:

Now take a look at the CloudFormation output:

What you get back is not the policy name but rather some logical ID Root-Grou-XXXXXXXXX.

Now, there may be some way to turn that back into a policy name, but I’m just going to go ahead and hard code the policy name at this point.

Also note that if I tried to translate that logical ID back to a policy name, there seems to be a bug related to CloudFormation and stacks and logical IDs for Policies.

I hope those logical IDs are not used in the actual policy evaluation logic in such a way that the wrong policy ID gets applied under some circumstance in AWS. But that is up to AWS to test as end users don’t generally work directly with the logical IDs. I’m not sure how AWS code uses these logical IDs behind the scenes or if they are meant to be unique.

To get around the logical ID Problem I could hard code the policy name into the output instead. For my group policy I can use the NameParam:

For my other stacks I have to hardcode the policy name or pass in an unnecessary parameter. These hardcoded values cloud lead to a typo or mismatch so they are not ideal.

Then I would need to edit the IAM policy creation template and reference each specific output that is required and formulate an ARN from those values in the NotResource resource list.

Reflecting on Our Options ~ Considering Complexity and Potential Gaps

If we take a look at our Root CloudFormation stacks, I’m only concerned with limiting access to change the Group, Role, and Policy right now. As mentioned I will deal with users later.

If I wanted to explicitly get the outputs of each stack and add them to our policy I could do that. I’d have to manually add the policy names as outputs as shown, which could lead to a typo somewhere. If new stacks are added, I would have to take the following steps each time we add a new IAM admin resource created by our ROOT stacks:

  1. Add the appropriate output to the stack.
  2. Make sure the output resource name matches the policy name for policies.
  3. Add the output to the CloudFormation template that creates the IAM Admin Role policy and redeploy it.

Sorry for taking you down that rabbit hole, but that all seems a bit convoluted in the end and probably error prone. I’m going to go ahead and stick with the simpler solution that does not require re-deploying the IAM admin role policy if a new IAM administrator resource is added (presuming it has the correct naming format matching the IAM policy.)

Maintaining the Integrity of Your Security Architecture and Design

Whomever maintains the IAM administrator policies and root stacks needs to understand the design and maintain the integrity of the solution if and when they make changes.

Therein lies the problem in many organizations when it comes to security. You might have some stellar team develop the initial, robust security design. Then that team hands it over to another team to maintain who “tweaks” the code in various ways that breaks down the integrity and introduces security gaps. This is why I prefer to have some consistency in who maintains code and oversight by security professionals for critical changes.

The trick is to understand what is critical and hopefully by naming our stacks “ROOT” and if our organization enforces a policy of only deploying changes from source control through a secure automated process, an understanding that this is critical code should exist. For critical code, enforce any and all changes through an automated deployment process with code only coming from your source code repository after a security review by the appropriate people. Document the design and architecture and any caveats when making changes (as I am doing in this blog, but I can also do in the code if this is a critical piece of the architecture.)

Ensure that no manual changes can impact your critical code or weaken your security design. If someone in your organization can go in and make manual changes to add new IAM permissions outside of your automated, secured deployment process without oversight, review, and complete understanding of what they are doing and why, then you don’t really have a secure cloud environment.

Testing Our Change

The only place where IAM permissions exist at the time of this writing in my GitHub repository outside of the ROOT admin profile is the IAMAdminsGroupRolePolicy at this point. We can add the NotResource here.

However we can’t formulate it as I have it above or we will get this error:

We do have to formulate the resources in the form of an ARN but we can still use a *. So our policy ends up looking like this:

Deploy the policy changes using CloudFormation as explained in prior posts. Verify the changes show up in the AWS console or through a CLI query if you prefer.

Testing Our Policy with the AWS IAM Policy Simulator

Now we need to test our policy — both that our policy still allows our IAM administrators to do their jobs — and that they cannot perform the actions we do not want them to perform.

One way to test your policies is though the AWS console IAM Policy Simulator tool:

Well, unfortunately we cannot test our IAM policy here since it no longer exists. Did it exist before? I can’t remember. I usually test IAM through other means. If you choose from the list of available services, only the new IAM Identity Center exists:

Searching through the list of actions, I can’t find anything to simulate changing a policy or changing the role for policy and so on.

Policies are still used by AWS IAM Identity center, and so are roles, so I hope they allow for testing through this tool in the future so we can test policies like the one I’m trying to test here. #awswishlist

Manual Testing

We can simply use our AWS CLI profile named IAM that we configured in prior posts to test our policy. That role profile assumes our IAM role and is governed by the IAM policy we want to test.

First, try to run these commands with your ROOT profile to verify they succeed:

aws iam list-policies | grep IAMAdmin
aws iam list-roles | grep IAMAdmin
aws iam list-groups | grep IAMAdmin

You will get a list of resources as expected and the grep portion of the command limits the list to the names of resources that contain IAMAdmin.

Now test those same commands with your IAM profile:

aws iam list-policies --profile IAM | grep IAMAdmin
aws iam list-roles --profile IAM | grep IAMAdmin
aws iam list-groups --profile IAM | grep IAMAdmin

What do you think will happen?

You get back the same list of roles. Why?

Let’s take a look at the AWS IAM precedence chart.

Hmm. There is an explicit Deny in the AWS policy that should apply to this user disallowing actions on the resources we are querying. According to the above chart, if an explicit Deny exists the action should be denied, but we are still able to query the resources. Why is that?

Well, as it turns out, the list functionality is not specific to a resource. If a user has permission to aws iam list-groups, that user will get back all groups. Any resource specific conditions or exclusions do not apply. This exposes some information you may or may not want to expose, but that’s how it works.

Test a resource-specific action deny

Now let’s test a resource-specific action with our NotResource command and some asterisks. I returned to my original policy and tried to delete the IAMAdmins group:

Now the restrictions seem to be working.

Test that the allowed actions still work as expected

The only thing left to do is to test that our deployment scripts still work. That’s easy to do. We can delete stacks the IAM admin deploys and redeploy them.

I have this Security Metrics Operators role, group, and policy I created and I’m not really using yet. I’m going to go ahead and delete all these stacks.

Done.

Now I can re-run my test.sh script in the IAM/test folder and verify these stacks get recreated. My script is running, and yes the resources are getting recreated.

Next Steps

We’ve prevented IAM admins from modifying their own group, role, and policy in this case. Now we need to consider other escalation paths such as:

  • Creating a new role, creating a compute resource, assigning a role, and running commands from that compute resource that the IAM user should not be able to do.
  • Creating a new user, assigning a policy, and changing the password to operate as that user.

Follow for updates.

Teri Radichel | © 2nd Sight Lab 2023

About Teri Radichel:
~~~~~~~~~~~~~~~~~~~~
⭐️ Author: Cybersecurity Books
⭐️ Presentations: Presentations by Teri Radichel
⭐️ Recognition: SANS Award, AWS Security Hero, IANS Faculty
⭐️ Certifications: SANS ~ GSE 240
⭐️ Education: BA Business, Master of Software Engineering, Master of Infosec
⭐️ Company: Penetration Tests, Assessments, Phone Consulting ~ 2nd Sight Lab
Need Help With Cybersecurity, Cloud, or Application Security?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
🔒 Request a penetration test or security assessment
🔒 Schedule a consulting call
🔒 Cybersecurity Speaker for Presentation
Follow for more stories like this:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
❤️ Sign Up my Medium Email List
❤️ Twitter: @teriradichel
❤️ LinkedIn: https://www.linkedin.com/in/teriradichel
❤️ Mastodon: @teriradichel@infosec.exchange
❤️ Facebook: 2nd Sight Lab
❤️ YouTube: @2ndsightlab
Aws Security
Cloud Security
Iam
Root
Privilege Escalation
Recommended from ReadMedium