Tech Blog

Escalator to the Cloud: 5 Privesc Attack Vectors in AWS

In 2018, Spencer Gietzen identified 21 methods across various AWS services that could lead to privilege escalation. Since then, I’ve often referred to Spencer’s article during engagements as I look for privilege escalation paths in client environments. I recently put each of these 21 methods into practice to test their validity. In doing so, I found that the techniques can be sorted into five rough categories. This article discusses those categories at a high level, and what to look out for in each category if you’re the one setting up user permissions.

Get the AWS Cheat Sheet

1. IAM Permissions on Other Users

Most AWS administrators realize that it’s inherently dangerous to grant users permissions to take actions on other users. I rarely see policies that allow a low-privileged user to explicitly change a password for a high-privileged user. But nonetheless, these types of misconfigurations occur, often because poorly defined policies have unexpected consequences.

For instance, it’s not uncommon for policies to allow NotActions. Using NotActions in policy Allow statements is like creating a blacklist. All permissions not included on the blacklist are implicitly allowed. Unfortunately for administrators, there are many dangerous permissions out there (including the 21 methods summarized here) so it’s easy to forget some. The following user account and group permissions can all be used to escalate privileges:  

iam:CreateAccessKey
iam:CreateLoginProfile
iam:UpdateLoginProfile
iam:AddUserToGroup

The permission policy below shows an attempt to prevent users from escalating privileges, but fails because it leaves out dangerous permissions:

{ "Version": "2012-10-17", "Statement": [ { "Sid": "MyPolicy", "NotAction": [ "iam:CreateAccessKey", "iam:CreateLoginProfile", "iam:AddUserToGroup" ], "Effect": "Allow", "Resource": "*" } ] }

Using a blacklist instead of a whitelist approach could leave the door open for users to escalate their privileges. This is especially true if a wildcard is used under the Resource section, because that means the user can take these actions for any group, policy, role, or other user account. Some ways that these permissions could allow privilege escalation include:

  • A user adding their own account to an Admin group
  • A user creating a new API key for a more privileged user account
  • A user updating the account password for a more privileged user account

2. Permissions on Policies

Just as important as limiting permissions that users have over their own and other user accounts is limiting permissions on policies. In the end, policies are what provide users with permissions. Users may be part of a group, or able to assume roles, but the policies applied to those groups and roles are how permissions are assigned. Therefore, administrators should be very careful when assigning users permissions on policies. Specifically, the following policy permissions can lead to privilege escalation:

iam:CreatePolicyVersion
iam:SetDefaultPolicyVersion
iam:AttachUserPolicy
iam:AttachGroupPolicy
iam:AttachRolePolicy
iam:PutUserPolicy
iam:PutGroupPolicy
iam:PutRolePolicy

The permission to create a new policy version allows a user to completely replace the permissions in a policy. If this policy applies to them, this opens the door for the user to just assign themselves full AWS administrative privileges. Attaching a policy or creating a new policy for a user, group, or role can similarly increase the permissions applied to the user.

3. Updating an AssumeRolePolicy

An AssumeRolePolicy (also referred to as a trust policy) defines which entities are allowed to assume a role. The permission to update an AssumeRolePolicy technically falls into the “Permissions on Policies” category of privilege escalation. However, the AssumeRolePolicy deserves a separate category due to its importance in AWS environments, plus the fact that it’s often misunderstood. An example of an AssumeRolePolicy  is the following:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "MyAssumeRolePolicy",
      "Effect": "Allow",
      "Principal": {
        "Service": "ec2.amazonaws.com”
      },
      "Action": "sts:AssumeRole"
    }
  ]
}

This AssumeRolePolicy specifies that the role it’s applied to can only be assumed by the EC2 service, as indicated by the “Service”: “ec2.amazonaws.com” statement. When another entity in the environment (such as a user or another service) tries to assume this role, AWS will deny the request. This is an important protection against attempts to escalate privileges by assuming a high-privileged role. 

The iam:UpdateAssumeRolePolicy permission provides an entity with the ability to change a role’s AssumeRolePolicy. So, if a user is not included in this policy but they have the ability to update it, they can just add their user account to the AssumeRolePolicy and consequently assume the role. Depending on the permissions associated with the role, this could provide a path for privilege escalation. 

4. iam:PassRole:*

The iam:PassRole permission allows a user to pass a role to an AWS entity. Passing roles is a crucial element in AWS permissions and resource management. For instance, when deploying an application to AWS, the application may need to perform certain actions on the back end, such as accessing databases or running Lambda functions. In order to allow the application to access these AWS services, you pass a role to it that contains the necessary permissions.

Problems with the iam:PassRole permission occur when there is no defined role or set of roles that the principal is allowed to pass. For instance, I often see policies that allow the use of iam:PassRole on wildcard resources (iam:PassRole:*). In effect, this allows the user to pass any role that exists in the environment, including any existing privileged roles. This may open up the possibility for a user to pass a privileged role to an AWS resource or service, and then use that resource or service to perform privileged actions.

One scenario where this would occur is when a user is granted the iam:PassRole permission with a wildcard, and also has the ec2:RunInstances permission. The user can create a new EC2 instance, pass a privileged role to the instance, and then SSH into it to be able to perform privileged actions. Similar privilege escalation techniques exist when iam:PassRole:* is combined with Lambda, Glue, CloudFormation, and Data Pipeline permissions. (Privesc methods related to each of those are covered in my in-depth exploration of 21 methods).

In that example, though, I glossed over an important detail; the ability for the service to assume the role passed to it. As discussed in the previous section about AssumeRolePolicies, just because I pass a role to a service doesn’t mean the service can use that role. I can pass a role to an EC2 instance, but the instance can only assume the role if the AssumeRolePolicy explicitly allows it. This makes privilege escalation through iam:PassRole:* significantly more difficult, but that doesn’t make it okay to use wildcards. The use of iam:PassRole:* in policies creates an AWS environment where privilege escalation is more likely to occur, so make sure to define specific roles when using iam:PassRole in a permissions policy. 

5. Privilege Escalation Using AWS Services

Thus far, all of the privilege escalation techniques discussed have focused around IAM permissions. But it’s also possible in some cases to escalate privileges using an AWS service. Lambda and Glue are two services that may allow users to execute commands that they should not be able to execute, and CloudFormation and Data Pipeline may be used for privesc as well if the user also has the iam:PassRole permission.

For Lambda, just having the permission to modify functions can lead to privilege escalation because a user can update a Lambda function to perform privileged actions on their behalf. The risk here depends on the permissions with which the Lambda function operates.

Similarly, the permission to modify a Glue development endpoint can lead to privilege escalation because a user might, for example, change the SSH key associated with an endpoint so they can then log into the system and use it to perform privileged actions. Again, the risk depends on the permissions assigned to the endpoint.

Conclusion

For all of the categories discussed above, the risk of privilege escalation can be mitigated or removed entirely by carefully defining permissions, principals, and resources in policies.

Rather than writing policies that provide the iam:PassRole permission on all roles in the environment, carefully list out the roles that an entity is allowed to pass. Similarly, instead of allowing a user to update the code of any Lambda function, determine which Lambda functions the user should be able to update based on their job requirements and permissions in the AWS environment. Guarding against privilege escalation in AWS (as in most other environments) comes down to following the principle of least privilege. The difference is that AWS is relatively unfamiliar to most administrators, and new AWS services are made available continuously. Through my testing of these methods and the five category summarization here, I hope I’ve provided insight into where administrators need to focus efforts to prevent users from performing unauthorized actions in AWS.