Bad actors can take advantage of any system if they know the right techniques. Still, with common configuration issues and other vulnerabilities becoming commonplace in AWS architecture, it’s important to understand how bad actors could exploit your environment by understanding the most common AWS privilege escalations used.
Privilege escalation is one common practice used by bad actors to gain entry into your most sensitive systems. In this guide, we explore the most recent tactics that bad actors use to escalate from a ‘low privilege’ account in your AWS environment up to full administrative permissions, where they can wreak havoc in your environment. Why is it important to understand these risks? Well, it is the first step in working towards achieving least privilege in AWS.
Controlling Privilege Escalation to Achieve Least Privilege in AWS
Privilege escalation is a classic technique used by bad actors to infiltrate systems. They may start with a low-level user account (a non-person identity), but they exploit permissions and pathways to work themselves up to an intimidating level of privilege where they’re poised to cause irreparable damage.
If you believe privilege escalation is an old threat, think again. Forrester estimates that 80% of security breaches involve privileged credentials. With the move to cloud infrastructure, it’s gaining attention due to not only the risks involved but the fact that many organizations have adopted cloud with such enthusiasm that they’ve failed to cover the fundamentals in security—leaving many gaps for bad actors to find their way in.
Just like other forms of attacks, privilege escalation can go unnoticed, especially in a complex cloud environment where companies already have difficulty gaining visibility into their internal users, identities, and actions. A bad actor could spend days, if not weeks, inside your systems and you may not even know it. They could even expose sensitive data and, like in 50% of cases, you might be completely unaware of the breach until a third party informs you of it.
To summarize, privilege escalation is a very real and pressing threat, especially for companies that are still in their infancy when it comes to cloud security and visibility. Whether you’ve been using AWS for years or you just recently considering adopting it, understanding the risks of privilege escalation and other common methods used in AWS is the first step to protecting your organization.
How Is Privilege Escalation Prevented in AWS?
Privilege escalation is a growing threat for anyone running a cloud environment, but generalized best practices tend to overshadow specific information on what is actually exploitable.
When it comes to AWS security, Identity and Access Management (IAM) permission misconfigurations have long held a spotlight, but that doesn’t mean they’re any easier to avoid. In reality, preventing privilege escalation begins with making it as difficult as possible–like by applying the principle of least privilege (POLP) when assigning permissions to identities (users).
Even with POLP in place, though, escalation is still possible. Completely preventing escalation is only possible when you follow all best practices and utilize tools to gain visibility into your environment, such as a scanning tool to help identify further vulnerabilities specific to your infrastructure.
However, before you begin trying to implement new tools and exploring the concept of least privilege, the first step in prevention is understanding. To follow is an exploration of 21 recently identified escalation tactics specific to AWS environments, with recommendations to follow on how you can limit these vulnerabilities and protect your account from exploitation.
Common Methods of AWS Privilege Escalation
Bad actors seem to always be finding new back doors to get into systems, but there’s a serious lack of information when it comes to the specifics they’re already using. To help you set your AWS environment up more securely and close vulnerable gaps, consult the following extensive list of common escalation methods used in AWS.
With each method, you’ll find a description of how it works and the potential impact it can have on your system. With that said, this is not an exhaustive list as the discovery of new methods is always occurring. Still, these methods represent the most pertinent, featuring permissions and permission combinations that many organizations may find themselves using, unaware of the consequences.
Self-Assigning Privileges Using a New IAM Policy
All a bad actor needs to execute this exploit is access to an identity (user account) with the permission iam:CreatePolicyVersion. This enables them to create a new version of an IAM policy, which allows them to simply grant themselves the access privileges they need to execute their plan. By enabling them to define their own custom permissions, this vulnerability allows a bad actor to wreak irreparable damage in your environment.
When you create a new version of an IAM Policy, you must set it as the default in order for it to take effect on the system, and that’s where the fatal misconception comes into play. In reality, being able to set a policy as default does not require a user (non-person identity) to have the iam:SetDefaultPolicyVersion permission, although most assume that it does. Rather, when creating a new permission, a user simply needs to flag it with “set-as-default” and AWS will automatically make the new default version when it’s created.
Escalating Privileges Using Inactive IAM Policies
Another common and straightforward escalation tactic is for a bad actor to gain access to an account with the iam:SetDefaultPolicyVersion permission. Even if the account is not allowed to create a new policy, the bad actor may be able to escalate their privileges by going through existing versions of your IAM policy that are not being used.
Using this method, the bad actor will simply update the IAM version to the policy that gives the most privileges. Of course, this is only possible if you have unused versions of your policy, and it’s only effective for the bad actor if one of those versions grants them additional privileges. The impact will depend entirely on how much privilege they can gain through an alternative policy version, which could range from no escalation to full administrative access.
Using an Existing Profile to Create a New EC2 Instance
The combination of iam:PassRole and ec2:RunInstances permissions mean a bad actor can create an EC2 instance (non-people identity) in your environment, to which they will have operating system access. From there, they simply need to pass an existing EC2 profile or service role to the new instance. Then they can log in and request the associated keys from the metadata, giving them all the permissions that profile or service role has been granted.
Bad actors can take a few different routes when it comes to gaining access to the instance. One of the most common is for them to make or import an SSH key and associate it with the instance when they create it, allowing them to SSH into it. They can also supply a script in EC2 User Data that grants them access, like a reverse shell payload or Empire stager.
As soon as the new instance is up and running, the bad actor is able to query metadata and get temporary credentials to access the instant profile, which grants them access to any AWS service the role can get to. If the bad actor has permission to access my_ssh_key and the corresponding security group, they won’t even need an SSH key or a security group that allows SSH access.
There are some obvious indicators of this exploitation, including the credentials for an EC2 instance profile being used outside of the specific instance they were intended for. If you use AWS GuardDuty (a non-person identity), it will even trigger an alert if that happens. To skirt that roadblock, many bad actors access the AWS API from within the instance rather than exfiltrating credentials to run locally.
Creating Access Keys for A Privileged User
Best practices say that you shouldn’t associate two sets of access keys to any user (a non-person identity) which is something bad actors take advantage of in this method. A bad actor uses the permission of iam:CreateAccessKey on other users (identities), allowing the bad actor to create a set of keys assigned to any user (identity) in the environment that doesn’t already possess two sets.
The bad actor will target an identity (user) that has the most permissions. Once the keys are created, they’ll use them to access your environment. The result is that the bad actor will have the same permissions levels as the user they were able to create keys for, which could result in anything from no privilege escalation (in the best case) to full administrative access (in the worst case).
Creating a Login Profile for an Inactive User
With this method, a bad actor utilizes the permission iam:CreateLoginProfile on other users, which allows them to create a password for any user in your environment who doesn’t already have a log-in profile set up. Once they set the password, they’ll be able to log in to that user’s account taking over that identity and taking advantage of any permissions the user has assigned to them.
A command that could alert you to this may include a password that’s the maximum possible length (128 characters) with all character types (numbers, symbols, uppercase, and lowercase) to ensure that the created password meets the minimum requirements. The impact of this hack again varies depending on the permissions of the user (identity) the bad actor was able to make a profile for.
Updating the Login Profile for an Active User
A bad actor with the iam:UpdateLoginProfile permission over other users (identities) will be able to change the password of any user (identity) within your AWS Console, which allows them to exploit the account of any user (identity) who does have a log in profile setup.
Similarly to creating a login profile, as seen above, the bad actor may use a command that changes the password to maximum length with all types of characters to ensure it doesn’t throw an error. You’ll also notice that the command specifics “no-password-reset-required” in both this case and the previous case, ensuring the bad actor does not need to deal with an email link or other confirmation method.
Attaching a New Policy to a User
By gaining the iam:AttachUserPolicy permission, a bad actor is able to escalate their privileges by attaching an IAM policy to any identity they have access to.
What’s most concerning about this method and the similar examples to follow, is that it provides a direct pathway to administrator privileges, as the bad actor simply needs to attach the AdministratorAccess AWS managed policy to a user they have access to, and they’ll suddenly have full access to your AWS environment.
Attaching a New Policy to a Group
Similar to the above, the iam:AttachGroupPolicy permission enables a bad actor to escalate their privileges by giving a group that they’re a member of a new policy with escalated access to the environment. Like the above method, the bad actor could attach the AdministratorAccess AWS managed policy to their user group, which would give them full access to your environment.
Attaching a New Policy to a Role
The iam:AttachRolePolicy permission is another commonly exploited privilege that allows a bad actor to escalate their permissions by attaching a policy, including the AdministratorAccess AWS managed policy, to a role they are assigned to. The user is able to assume the role temporarily using sts:AssumeRole.
Creating or Updating Inline User Policy
The iam:PutUserPolicy permission enables a bad actor to escalate their privileges by either creating a new policy or updating an inline policy for any user they have access to. The impact of this attack method can be huge, considering that the bad actor is able to specify any policy document. This enables them to specify a policy that enables them to perform any action anywhere in your AWS environment, ultimately allowing them to gain full administrative access to your infrastructure.
Creating or Updating Inline Group Policy
Similar to the above, the iam:PutGroupPolicy permission allows a bad actor to create or update an inline policy for a group of users, allowing them to escalate the privileges of an account they have access to. Again, the bad actor could specify any policy document, enabling them to grant their account the permission to access any resource and perform any action as they work to escalate to full administrative access.
Creating or Updating Inline Role Policy
Just like the previous two examples, the iam:PutRolePolicy permission allows a bad actor to escalate their privilege by creating or updating the inline policy of a user role. The bad actor then uses an account they have access to and the command sts:AssumeRole to temporarily assume that role. The potentially catastrophic result is that the bad actor can grant themselves a myriad of permissions by specifying any policy document as they seek full administrative access.
Adding Themselves to a Privileged Group
The iam:AddUserToGroup permission allows a bad actor to add the user account they have access to into any IAM group that already exists in your AWS account. This allows the bad actor to escalate their privileges by inserting themselves into a group with more or different permissions than their current account has.
The impact depends upon whether the group the bad actor adds themself to has more privilege than they started with, and to what degree they have the privilege.
Updating Role Policy to Assume a New Role
The combination of iam:UpdateAssumeRolePolicy and sts:AssumeRole permissions allow a bad actor to change the assume role policy of any document for any role in your environment, and the latter allows them to assume that role in their current account.
This means the bad actor will start by updating the policy document of a given role to ensure that their current user account is able to assume that specific role, and then they’ll use the sts:AssumeRole permission to take on that role. This method gives the bad actor the ability to gain the privileges of any role in your AWS environment, which could potentially result in full administrative access.
Passing a Role to a Function and Invoking It
A combination of the iam:PassRole, lambda:CreateFunction, and lambda:InvokeFunction permissions allow a bad actor to escalate their privileges by passing an existing role to a function (non-person identity) that the bad actor knows has code important to the AWS libraries they need in the programming language of their choosing.
Next, the bad actor uses these privileges to perform any action they choose. They may even be able to run the code in your AWS environment by using the AWS API to invoke the function. One outcome is that they use Identity and Access Management (IAM) to attach administrative access to their current user account.
The impact of this method is that the bad actor can gain the privileges that any Lambda service role (non-person identity) currently has in your AWS account.
Passing a Role to a Function and Triggering It
A combination of permissions, including iam:PassRole, lambda:CreateFunction, and lambda:CreateEventSourceMapping, enables a bad actor to escalate their privileges by passing a role from your existing IAM role set to a new Lambda function that features the code necessary to import relevant AWS libraries in the programming language of their choosing, so they can use it to perform various actions.
In this case, the user does not need the lambda:InvokeFunction permission, although they may need dynamodb:PutItem and dynamodb:CreateTable permissions to pull it off. Instead of invoking the function, which they aren’t allowed to do, the bad actor will instead trigger it using DynamoDB. This requires them to create a table in DynamoDB or use an existing table, where they will then use event source mapping to make the Lambda function (non-person identity) run when a new item is added to the table. The bad actor may add an item to the table themselves or wait for some other method to add it to invoke the Lambda function.
Similar to the last example, one way the bad actor can utilize their permissions is to attach administrative access to their current user account. From there, the actions they take depend on whether your current AWS environment is using DynamoDB or not. If it is used, the bad actor only needs to create an event source mapping for the Lambda function.
However, if your environment doesn’t use DynamoDB, the bad actor must set up a table with streaming enabled. Following that, the bad actor will set up the event source mapping and connect the function and stream, allowing them to invoke the function by triggering the stream. They’ll simply need to add something to the DynamoDB table to invoke the Lambda function, and now they hold administrator privileges.
Updating The Code of an Existing Function
The lambda:UpdateFunctionCode permission allows a bad actor to update the code of any existing Lambda function, which they would use to import relevant AWS libraries in the programming language of their choosing.
As in the previous examples, the bad actor will use this library to perform actions. In this case, they’ll have to wait for them to be invoked unless they find a way to invoke them directly, but it’s likely they will be invoked in some way by your system. The impact of this method is that the bad actor can gain the privileges that any Lambda service role currently has in your AWS account, potentially gaining administrative access.
Passing a Role to a Glue Development Endpoint
A combination of the iam:PassRole and glue:CreateDevEndpoint permissions enables a bad actor to create a new endpoint in the AWS Glue development portal and then pass a service role to that identity. The bad actor can then SSH into the instance and use the command line interface within AWS having the same privileges as the role assigned to it.
If you refer back to our example for escalating privileges using the inactive IAM Policies method, you’ll remember that it’s not recommended to exfiltrate the credentials and run them locally. Instead, it’s preferable to access the AWS API directly using the new instance. The impact of this method is that the bad actor can gain the privileges that any Glue service role currently has in your AWS account, potentially gaining administrative access.
Updating an Existing Glue Development Endpoint
The glue:UpdateDevEndpoint permission is all that’s necessary for this method for a bad actor to update the SSH public key associated with an existing Glue development endpoint. From there, the attack can SSH directly into the instance and access your environment with the same permissions the attached role has been given.
Again, it’s not ideal to exfiltrate the credentials from the Glue Instance. Instead, the instance should be used to access the AWS API directly. The impact of this method is that the bad actor can gain the privileges of the role attached to the Glue development endpoint they’re working with.
Passing a Role to Cloud Formation
The iam:PassRole and cloudformation:CreateStack permissions enable a bad actor to escalate their privileges by simply creating a new template in CloudFormation, which will be able to perform any actions allowed by the role that was passed to it when creating the stack in CloudFormation.
The template the bad actor calls on can come from anywhere and will include directions for malicious actions, like creating a new administrator account, which the bad actor will then use to escalate their access. In the least, this method gives the bad actor the same privileges granted to the role associated with the CloudFormation stack. In the worst case, this could result in the bad actor gaining full administrative privileges.
Passing a Role to Data Pipeline
A combination of the iam:PassRole, datapipeline:CreatePipeline, and datapipeline:PutPipelineDefinition permissions allows a bad actor to create a pipeline and update it to further make other resources or make it run any command in the CLI. Moreover, these actions may take place just once or at a set interval.
As an example, a bad actor could use this method to create an empty pipeline. They would then add a definition file with a directive to run a command or create new resources using the API, which will help them gain more privileges. The potential impact depends on the role that was passed when the pipeline was created.
Auditing for Permissions Flaws in AWS
These common AWS privilege escalations are all too often exploited, and it boils down to basic privilege settings being overlooked and under-managed in AWS environments. With that said, you can check all of your privilege settings manually by either reviewing IAM permissions given to users to by using penetration testing to try and exploit each of these methods, but either of these endeavors will prove time-consuming.
The quickest way to ensure that you are smart with permissions is to automate the process using the right cloud security tools. The right CIEM tool will help you detect who has access to what, when the data was accessed, and what was accessed, revealing the privilege escalation risk in your AWS environment. You can scan a single identity or every identity in our enterprise cloud security platform.
Escalation Defense and Mitigation
In theory, general defenses against escalation attacks are simple. What makes it difficult is defending against the myriad of attack methods in a production AWS environment, where things quickly grow complicated. However, the best place to start is with the IAM policies, ensuring that you are utilizing the “Resource” option to the fullest extent and using all of the built-in variables the policies are able to support.
Protecting Root Keys
For seasoned AWS users, protecting the access key set, which includes an access key ID and secret access key, is an obvious must-do. Unfortunately, less experienced users may not be aware of the risks associated with using these keys. These keys exist to allow you to make requests to AWS, but the root user access keys provide full access to all resources and services, including billing information.
It is not possible to reduce the permissions that AWS root user keys provide, so it is paramount that these keys are protected just as you would any other sensitive information. For starters, do not create access keys for the root users if you haven’t yet unless you absolutely need to. Instead, you can use the account email and password to enter the Management Console and create an IAM user for yourself, granting it administrative privileges.
Moreover, if you have an access key for any root user, it’s best to delete it. If you choose to keep the key, you should change it on a regular basis–a 90-day rotation period is recommended. Moreover, these keys should never be shared with any other user, not even a developer. Additional users should be created and assigned the appropriate privileges when necessary.
The root user account should also follow the best practices of password creation and management, as should all users in your AWS environment. Multi-factor authentication should also be activated to help prevent unauthorized access.
(See also our Common IAM Mistakes to learn more)
Creating Users and Assigning Permissions
Users should never share accounts in AWS, and the root keys should never be used for access. Aside from creating your own administrative account using IAM, you should also use the IAM console to create a new account for every user who needs access to the environment. Beyond that fundamental concept, though, there are additional best practices to follow.
For starters, you should never create a new user and assign permissions to it. Refer back to method #5 (creating a login profile for an inactive user) for a reminder as to why. Instead, users should be created with no privileges and given the ability to set their own passwords. Once they create their passwords, you can go about assigning permissions.
However, instead of assigning permissions to individual users–which is advised against by AWS and security professionals–you’ll want to place users into pre-defined groups. You do not want to assign permissions to users directly because they will maintain these permissions even if you change their role or group over time.
By assigning users to groups and granting permissions that way, you can effortlessly change a user’s permissions by simply changing the group they are assigned to.
Using Variables to Prevent Escalation
Amazon provides a complete list of the variables you can use in policies and a description of each, but in general, these variables will help you allow or deny certain permissions based on something that can change over time; differs between users; or a parameter that you cannot exactly define at the time of creation.
Of all the variables you can use, the two most important to pay attention to include the aws:SourceIp, which pertains to the detected IP address behind API calls; and aws:username, which pertains to the name of the user making API calls. An example of how you can use these variables is to set restrictions so that aws:SourceIp is always a known IP address, which greatly reduces the chance of API calls from a malicious address.
Meanwhile, you can use the aws:username variable to assign the majority of permissions to a user so they can only be used against themselves. In doing so for the aws:CreateAccessKey permission, you would prevent method #4 from being viable. Likewise, using this for aws:CreateLoginProfile would prevent ‘creating a Login Profile for an Inactive User’ method and using this for aws:UpdateLoginProfile would prevent method ‘updating the Login Profile for an Active User’. You should also use this variable to restrict the setup of multi-factor authentication to only the current user’s account.
By assigning users the above permissions, but restricting the permission to only their own account, a user will be able to create their own password, change their own password, create access keys for themselves, and set up multi-factor authentication for their own account, but never for anyone else’s account. This in itself is a big step in the right direction for most AWS environments.
Learn how Sonrai enforces Least Privilege.
Implementing The Principle of Least Privilege
As you can see, preventing escalation tactics comes down to being very mindful about the permissions each user possesses and being aware of all the ways that privileges can be misused. With that in mind, one of the best practices of cloud security is implementing the principle of least privilege.
The principle of least privilege is a simple concept, at least on paper. POLP states that a user should only be given the minimum permissions required to perform their duties and that they should only have their permissions for the least amount of time required to execute the task at hand. As you consider various use cases, the benefits of this principle become clear.
For example, take a developer who needs the cloudformation:CreateStack permission one time for a project. Why risk assigning that permission permanently? By making permissions expire and revoking permissions over time when they’re no longer needed, your organization is able to apply the principle of least privilege effectively.
AWS provides these recommendations in utilizing the principle of least privilege:
- Access Level Groupings: Identity and Access Management policies feature actions that are classified as either List, Read, Write, Permissions management, or Tagging. Access level groupings will help you understand the access level a particular policy grants. For instance, a “Read-Only” access level provides List and Read access.
- Policy Validation: The IAM Access Analyzer is a handy tool for performing policy validation. You should use it when you create or edit your policies and to review existing policies. The tool checks over 100 points to validate your policies and will generate warnings if a statement is overly permissive. The tool then provides actionable suggestions to work towards POLP.
- Policy Generation: Refining what permissions you grant to users can be made easier with the use of the AWS policy generation tool. This tool will generate a policy based on the actual activities of an IAM entity (either a user account or a user role). The analyzer will review logs and generate a template based on permissions the entity actually required in a given time frame. You can then fine-tune those permissions and attach them to an entity.
- Last Accessed: Aside from automated policy generation, AWS also provides a feature that reveals “last accesses” so you can see the activity of a user, group, role, or policy. This information will include actions for services and reports on how entities or policies are being used throughout the organization. This tool will help you identify permissions that are going unused and, therefore, may be unnecessary.
- Account Review: Amazon recommends using AWS CloudTrail to review account events so that you can further minimize permissions for users. The event logs will reveal detailed information surrounding events which you can use to further fine-tune user permissions.
The reason why more organizations aren’t using POLP all the time is simple: It’s complicated. Just like everything else to do with cloud security, merely getting to a point where you can define the least permissions each user needs is time-consuming. From there, you’ll need a system that manages and automates privileges, too, so that requesting permissions doesn’t become a point of friction for end-users.
With all of that stated, it’s still worth pursuing the principle of least privilege, and it’s the only viable way forward if you want to lock down your AWS environment and prevent escalation tactics. By only giving users the minimum permissions necessary, for the least amount of time needed, you greatly reduce the likelihood of a bad actor abusing the permissions assigned to an account.