Cloudgoat Easy: Beanstalk Secrets

In this lab, we'll be doing a walkthrough on an easy cloudgoat lab.
Summary of the pathEnumerate 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, and get our initial low privilege access keys.
$ 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
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.
$ aws elasticbeanstalk describe-environments --profile beanstalk
We can then use describe-configuration-settings to get further information on the environment.
Scrolling down, we can see a namespace that has hard-coded access keys.
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.
Enumerating the policies for the user
$ 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.
Getting the specific policy.
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:
{
"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:
"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
$ aws iam list-users --profile beanstalk-01
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.
$ aws iam create-access-key --user-name cgid6sd400b28h_admin_user --profile beanstalk-01
Configure a profile for the admin account, and confirm access.
Let's confirm the policies.
Accessing the flag
$ aws secretsmanager list-secrets --profile beanstalk-admin
$ aws secretsmanager get-secret-value --secret-id cgid6sd400b28h_final_flag --profile beanstalk-admin
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
DescribeConfigurationSettingsreturns a reference instead of a usable key.Scope
iam:CreateAccessKeytoarn:aws:iam::<ACCOUNT_ID>:user/${aws:username}so a user can only rotate their own keys.Alert on CloudTrail
CreateAccessKeyevents where the caller identity doesn't match the targetuserName. 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:




