AWS IAM Privilege Escalation Techniques

Original Research: Spencer Gietzen - AWS IAM Privilege Escalation
Further Reading: AWS-IAM-Privilege-Escalation
Further Reading: Investigating PrivEsc Methods in AWS

Quick List

codestar:CreateProject, codestar:AssociateTeamMember

glue:UpdateDevEndpoint

iam:AddUserToGroup

iam:AttachGroupPolicy

iam:AttachRolePolicy

iam:AttachUserPolicy

iam:CreateAccessKey

iam:CreateLoginProfile

iam:CreatePolicyVersion

iam:PassRole, cloudformation:CreateStack

iam:PassRole, codestar:CreateProject

iam:PassRole, datapipeline:CreatePipeline, datapipeline:PutPipelineDefinition, datapipeline:ActivatePipeline

iam:PassRole, ec2:RunInstances

iam:PassRole, glue:CreateDevEndpoint

iam:PassRole, lambda:AddPermission, lambda:CreateFunction

iam:PassRole, lambda:CreateEventSourceMapping, lambda:CreateFunction

iam:PassRole, lambda:CreateFunction, lambda:InvokeFunction

iam:PutGroupPolicy

iam:PutRolePolicy

iam:PutUserPolicy

iam:SetDefaultPolicyVersion

iam:UpdateAssumeRolePolicy

iam:UpdateLoginProfile

lambda:UpdateFunctionCode

lambda:UpdateFunctionConfiguration

If you’d like to get hands on experience exploiting these misconfigurations, check out iam-vulnerable by Seth Art.

codestar:CreateProject, codestar:AssociateTeamMember

With access to the codestar:CreateProject and codestar:AssociateTeamMember permissions, an adversary can create a new CodeStar project and associate themselves as an Owner of the project.

This will attach a new policy to the user that provides access to a number of permissions for AWS services. This is most useful for further enumeration as it gives access to lambda:List*, iam:ListRoles, iam:ListUsers, and more.

Using codestar:AssociateTeamMember

Showing the CodeStar policy

glue:UpdateDevEndpoint

With access to the glue:UpdateDevEndpoint permission, an adversary can update the existing SSH key associated with the glue endpoint. This will allow the adversary to SSH into the host and gain access to IAM credentials associated with the role attached to the glue endpoint. Though not required, it may be helpful to have the glue:GetDevEndpoint permission as well, if the existing endpoint cannot be identified via other means.

iam:AddUserToGroup

With access to the iam:AddUserToGroup permission, an adversary can add an IAM user they control to an existing group with more privileges. Although this is not required, it may be helpful to have other permissions in the IAM family to identify other groups and their privileges.

iam:AttachGroupPolicy

With access to the iam:AttachGroupPolicy permission, an adversary can attach an IAM policy to a group they are a member of. This potentially includes policies such as AdministratorAccess, which would provide them (surprise) administrator access to the AWS account.

iam:AttachRolePolicy

With access to the iam:AttachRolePolicy permission, an adversary can attach an IAM policy to a role they have access to. This potentially includes policies such as AdministratorAccess, which would provide them administrator access to the AWS account.

iam:AttachUserPolicy

With access to the iam:AttachUserPolicy permission, an adversary can attach an IAM policy to an IAM user they have access to. This potentially includes policies such as AdministratorAccess, which would provide them administrator access to the AWS account.

iam:CreateAccessKey

With access to the iam:CreateAccessKey permission, an adversary can create an IAM Access Key and Secret Access Key for other users. This would allow them to create credentials for more privileged users and have access to their privileges.

Showing iam:CreateAccessKey

iam:CreateLoginProfile

With access to the iam:CreateLoginProfile permission, an adversary can create a password for a more privileged IAM user to login to the console as. Note: if a password is already set, you must use iam:UpdateLoginProfile instead.

iam:CreatePolicyVersion

With access to the iam:CreatePolicyVersion permission, an adversary can create a new version of a existing policy with more privilege. If the adversary has access to the principal that policy is attached to, they can elevate their privileges.

iam:PassRole, cloudformation:CreateStack

With access to the iam:PassRole and cloudformation:CreateStack permissions, an adversary can create a new CloudFormation stack and pass a more privileged role to it. This would allow an adversary to escalate privileges to that more privileged role.

iam:PassRole, codestar:CreateProject

With access to the iam:PassRole and codestar:CreateProject permissions, an adversary can create a new CodeStar project and pass a more privileged role to it. This would allow an adversary to escalate privileges to that more privileged role including that of an administrator.

iam:PassRole, datapipeline:ActivatePipeline, datapipeline:CreatePipeline, datapipeline:PutPipelineDefinition

With access to the iam:PassRole, datapipeline:ActivatePipeline, datapipeline:CreatePipeline, and datapipeline:PutPipelineDefinition permissions, an adversary can create a new pipeline and pass in a more privileged role. It is worth noting that to do this the AWS account must already contain a role that can be assumed by DataPipeline and that role must have greater privileges (or at least different ones) than the principal the adversary controls.

iam:PassRole, ec2:RunInstances

With access to the iam:PassRole and ec2:RunInstances permissions, an adversary can create a new EC2 instance and pass a more privileged role to it.

This can be taken advantage of with the following one-liner:

ec2 run-instances one-liner

Some things to note: The instance profile must already exist, and (realistically) it must have greater permissions than the role you have access to. If you also have the ability to create a role, this can be leveraged (although you may as well set the trust policy of that role to one you control at that point). The role that is being passed must have a trust policy allowing the EC2 service to assume it. You cannot pass arbitrary roles to an EC2 instance.

A common misconception about this attack is that an adversary must have access to an existing SSH key, or be able to spawn an SSM session. This is not actually true, you can leverage user data to perform an action on the host. One common example is to have the EC2 instance curl the metadata service, retrieve the IAM credentials, and then send them to an attacker controlled machine using curl.

Another (stealthier) example would be to perform all your API operations at once in the user-data script. This way you are not dinged with the IAM credential exfiltration finding (which can be bypassed).

iam:PassRole, glue:CreateDevEndpoint

With access to the iam:PassRole and glue:CreateDevEndpoint permissions, an adversary can create a new Glue development endpoint and pass in a more privileged role. It is worth noting that to do this the AWS account must already contain a role that can be assumed by Glue and that role must have greater privileges (or at least different ones) than the principal the adversary controls.

iam:PassRole, lambda:AddPermission, lambda:CreateFunction

With access to the iam:PassRole, lambda:AddPermission, and lambda:CreateFunction permissions, an adversary can create a Lambda function with an existing role. This function could then by updated with lambda:AddPermission to allow another principal in another AWS account the permission to invoke it. It is worth noting that the AWS account must already contain a role that can be assumed by Lambda.

iam:PassRole, lambda:CreateEventSourceMapping, lambda:CreateFunction

With access to the iam:PassRole, lambda:CreateEventSourceMapping, and lambda:CreateFunction permissions, an adversary can create a Lambda function with an existing privileged role and associating it with a DynamoDB table. Then, when a new record is inserted into the table, the Lambda function will trigger with the privilege of the passed in role.

It is worth noting that the AWS account must already contain a role that can be assumed by Lambda. Additionally, while not required, it may be beneficial to have the dynamodb:CreateTable and dynamodb:PutItem permissions to trigger this yourself.

iam:PassRole, lambda:CreateFunction, lambda:InvokeFunction

With access to the iam:PassRole, lambda:CreateFunction, and lambda:InvokeFunction permissions, an adversary can create a new Lambda function and pass an existing role to it. They can then invoke the function allowing them access to the privileges of the role associated with the function. It is worth noting that unless the adversary can create a role, they must use an already existing role that can be assumed by Lambda.

iam:PutGroupPolicy

With access to the iam:PutGroupPolicy permission, an adversary can create an inline policy for a group they are in and give themselves administrator access to the AWS account.

iam:PutRolePolicy

With access to the iam:PutRolePolicy permission, an adversary can create an inline policy for a role they have access to and give themselves administrator access to the AWS account.

iam:PutUserPolicy

With access to the iam:PutUserPolicy permission, an adversary can create an inline policy for a user they have access to and give themselves administrator access to the AWS account.

iam:SetDefaultPolicyVersion

With access to the iam:SetDefaultPolicyVersion permission, an adversary can revert a policy associated with their principal to a previous version. This is useful for scenarios in which a previous version of a policy had more access than the current version.

iam:UpdateAssumeRolePolicy

With access to the iam:UpdateAssumeRolePolicy permission, an adversary can modify the assume-role policy of a role, allowing them to assume it. This is useful to gain access to administrator roles, or other more privileged roles.

iam:UpdateLoginProfile

With access to the iam:UpdateLoginProfile permission, an adversary can change the password of an IAM user. This would allow them to log into the console as that user.

lambda:UpdateFunctionCode

With access to the lambda:UpdateFunctionCode permission, an adversary can modify an existing Lambda function’s code. This would allow them to gain access to the privileges of the associated IAM role the next time the function is executed.

lambda:UpdateFunctionConfiguration

With access to the lambda:UpdateFunctionConfiguration permission, an adversary can modify an existing Lambda function’s configuration to add a new Lambda Layer. This Layer would then override an existing library and allow an adversary to execute malicious code under the privilege of the role associated with the Lambda function.