# Cloudgoat Easy: Beanstalk Secrets

In this lab, we'll be doing a walkthrough on an easy [**cloudgoat lab**](https://github.com/RhinoSecurityLabs/cloudgoat#beanstalk_secrets-easy).

**Summary of the path**`Enumerate Beanstalk environment on low priv user` -> `Get environment existing configuration` -> `Find hard-coded access keys to another user` -> `Pivot to secondary user and read policies` -> `Find CreateAccessKey scoped to *` -> `List interesting users` -> `Privilege escalation via creating access keys for admin user and get flag`

Two things happen here, and they're worth separating. The hard-coded keys get us **lateral movement**, since we move to a different identity without knowing yet whether it's more privileged than ours. The **privilege escalation** is the last step, where an overly broad `iam:CreateAccessKey` lets us mint credentials for an account we were never supposed to touch.

This lab covers the importance of enumeration, especially looking into configurations that might have exposed access keys you can use to pivot to another IAM user. It also covers how to abuse an overprivileged user, and why you should never use `*` wildcards in your AWS environment.

## Setting up

We'll spin up the lab via [cloudgoat](https://github.com/RhinoSecurityLabs/cloudgoat), and get our initial low privilege access keys.

```shell
$ cloudgoat create beanstalk_secrets
<SNIP>
[cloudgoat] terraform output completed with no error code.
initial_low_priv_credentials = Access Key: AKIAUWJPWY7O5GAB6YEH
Secret Key: <REDACTED>
$ aws configure --profile beanstalk
 
AWS Access Key ID [None]: AKIAUWJPWY7O5GAB6YEH
AWS Secret Access Key [None]: <REDACTED>
Default region name [None]: us-east-1
Default output format [None]: json
```

![](https://cdn.hashnode.com/uploads/covers/673c6b60dcfeadc44f6aa79d/4b5db72d-07c7-48c7-804c-f250dcfc501f.png align="center")

![](https://cdn.hashnode.com/uploads/covers/673c6b60dcfeadc44f6aa79d/68950130-56e4-4c0c-8bac-41276faff8b8.png align="center")

That's everything we start with: one set of keys, no idea yet what they can do.

## Enumeration

We'll check the available environments of this user via [describe-environments](https://docs.aws.amazon.com/cli/latest/reference/elasticbeanstalk/describe-environments.html).

```shell
$ aws elasticbeanstalk describe-environments --profile beanstalk
```

![](https://cdn.hashnode.com/uploads/covers/673c6b60dcfeadc44f6aa79d/880b634c-df9e-4193-837a-1b7aecebebd6.png align="center")

We can then use [describe-configuration-settings](https://docs.aws.amazon.com/cli/latest/reference/elasticbeanstalk/describe-configuration-settings.html) to get further information on the environment.

![](https://cdn.hashnode.com/uploads/covers/673c6b60dcfeadc44f6aa79d/67d75b72-f33d-4316-8016-1fde005ace57.png align="center")

Scrolling down, we can see a namespace that has hard-coded access keys.

![](https://cdn.hashnode.com/uploads/covers/673c6b60dcfeadc44f6aa79d/748d0d7e-8d7d-4652-8b8b-fcca1c1a3f43.png align="center")

Worth pausing on why this works at all. Environment variables set on a Beanstalk environment live under the `aws:elasticbeanstalk:application:environment` namespace, and Beanstalk treats them as configuration, not as secrets. There's no redaction on read. Any principal holding `elasticbeanstalk:DescribeConfigurationSettings` gets the values back in plaintext, so if a developer drops long-lived keys into an env var, read access to the environment config is read access to the credentials.

## Pivoting to the secondary user and enumerating policies

Configure a profile for **secondary\_user**.

![](https://cdn.hashnode.com/uploads/covers/673c6b60dcfeadc44f6aa79d/d88ca993-f247-4db4-b4a7-ba6a84d409f8.png align="center")

### Enumerating the policies for the user

```shell
$ aws iam list-attached-user-policies --user-name cgid6sd400b28h_secondary_user --profile beanstalk-01
$ aws iam get-policy --policy-arn arn:aws:iam::322759477213:policy/cgid6sd400b28h_secondary_policy --profile beanstalk-01
$ aws iam get-policy-version --policy-arn arn:aws:iam::322759477213:policy/cgid6sd400b28h_secondary_policy --version-id v1 --profile beanstalk-01
```

Listing the attached user policies.

![](https://cdn.hashnode.com/uploads/covers/673c6b60dcfeadc44f6aa79d/c11bec3c-a9e2-4fc1-a8b2-282f7d1ec967.png align="center")

Getting the specific policy.

![](https://cdn.hashnode.com/uploads/covers/673c6b60dcfeadc44f6aa79d/6757caa3-8bb9-4ee9-b3c1-eccd0287e0fa.png align="center")

![](https://cdn.hashnode.com/uploads/covers/673c6b60dcfeadc44f6aa79d/93f35a41-4bda-4143-8b9a-72bda8bdf3ab.png align="center")

Note that `get-policy` only gives us metadata. The actual permissions live in the policy document, so we need `get-policy-version` with the default version ID to see what the user can really do.

Here's the interesting statement attached to **secondary\_user**:

```json
{
    "Action": [
        "iam:CreateAccessKey"
    ],
        "Effect": "Allow",
        "Resource": "*"
}
```

The `Resource` is what makes this a finding. If the intent was to let users rotate their own keys, this should have been scoped to the caller:

```json
"Resource": "arn:aws:iam::<ACCOUNT_ID>:user/${aws:username}"
```

Instead it's `*`, which means `secondary_user` can create access keys for **any** IAM user in the account, including users far more privileged than itself.

## Listing interesting users

```shell
$ aws iam list-users --profile beanstalk-01
```

![](https://cdn.hashnode.com/uploads/covers/673c6b60dcfeadc44f6aa79d/8c821b24-9c7c-42d5-b6dd-ccfdd2bed58d.png align="center")

We find an `admin_user`. CloudGoat names things helpfully, so picking a target here is just reading the list. On a real account nobody is called `admin_user`, so you'd work through `list-attached-user-policies` and `list-groups-for-user` on each principal and look for who actually holds the wide permissions, rather than trusting the naming.

## Privilege escalation

With the information we've gathered, we can use `secondary_user` to create access keys for `admin_user`, and use those to act as the admin.

```shell
$ aws iam create-access-key --user-name cgid6sd400b28h_admin_user --profile beanstalk-01
```

![](https://cdn.hashnode.com/uploads/covers/673c6b60dcfeadc44f6aa79d/393f4e6a-f01e-44f3-8b8e-ff0d3899c37a.png align="center")

Configure a profile for the admin account, and confirm access.

![](https://cdn.hashnode.com/uploads/covers/673c6b60dcfeadc44f6aa79d/90741139-9f78-461f-ae53-d2586191c609.png align="center")

Let's confirm the policies.

![](https://cdn.hashnode.com/uploads/covers/673c6b60dcfeadc44f6aa79d/56fdad53-1960-4e60-a6ae-72fc90e3aecb.png align="center")

![](https://cdn.hashnode.com/uploads/covers/673c6b60dcfeadc44f6aa79d/06d6cf8f-088d-4b41-a735-2de422da72cd.png align="center")

## Accessing the flag

```shell
$ aws secretsmanager list-secrets --profile beanstalk-admin
$ aws secretsmanager get-secret-value --secret-id cgid6sd400b28h_final_flag --profile beanstalk-admin
```

![](https://cdn.hashnode.com/uploads/covers/673c6b60dcfeadc44f6aa79d/43336dcf-d8fe-43d3-8192-b82af340167d.png align="center")

`FLAG{D0nt_st0r3_s3cr3ts_in_b3@nsta1k!}`

## Remediations

Three things would break this chain:

*   Keep credentials out of Beanstalk environment variables. Store them in Secrets Manager or SSM Parameter Store and have the application resolve them at runtime, so `DescribeConfigurationSettings` returns a reference instead of a usable key.
    
*   Scope `iam:CreateAccessKey` to `arn:aws:iam::<ACCOUNT_ID>:user/${aws:username}` so a user can only rotate their own keys.
    
*   Alert on CloudTrail `CreateAccessKey` events where the caller identity doesn't match the target `userName`. That's the signal that survives the key rotation a responder would reach for first. Finally, tear the lab down so you're not leaving live IAM users and keys in your account:
