Complimentary — TryHackMe Writeup
Discovered a static website hosted on Amazon S3 with a client-side JavaScript file containing hardcoded AWS Cognito Identity Pool ID and DynamoDB table name.
Used the exposed Identity Pool ID to provision an anonymous Cognito identity and exchanged it for temporary AWS credentials (AccessKeyId, SecretKey, SessionToken) via the AWS CLI.
Leveraged the overprivileged unauthenticated IAM role to perform a full DynamoDB Scan (instead of the intended GetItem), exfiltrating all guest profiles including the flag.
Introduction
Complimentary is an easy-rated TryHackMe cloud machine that serves as an excellent introduction to AWS security misconfigurations. The target is a "wellness app" hosted on Amazon S3 that claims to require no login or account — it simply knows things about you. This immediately hints at client-side credential issuance.
The attack chain is entirely cloud-based: we extract hardcoded AWS resource identifiers from the frontend JavaScript, abuse a Cognito Identity Pool configured for unauthenticated access, and exploit an overprivileged IAM role to dump an entire DynamoDB table — revealing the flag hidden in another guest's profile.
This machine has no SSH, no Nmap, and no traditional ports to scan. The entire attack surface is AWS services — making it a refreshing change from the typical box.
Reconnaissance
::Target Discovery
The target is a static website hosted on Amazon S3:
The app claims to require "no login screen" and "no account needed." It simply knows things about the visitor upon opening. This immediately suggests some form of client-side credential issuance — most commonly an AWS Cognito Identity Pool configured to allow unauthenticated access.
::Frontend Source Analysis
Viewing the page source http://complimentary-wellness-app-332173347248.s3-website-us-east-1.amazonaws.com/app.js reveals a JavaScript file containing the app's core logic. Buried in the code are three critical constants:
The script initializes AWS.CognitoIdentityCredentials with the IdentityPoolId, then calls dynamodb.getItem() using a locally-generated guest_id. This confirms the attack vector:
- ▹The app uses a Cognito Identity Pool to hand out temporary AWS credentials to anonymous users.
- ▹Those credentials are then used to query DynamoDB directly from the browser.
- ▹If the IAM role attached to unauthenticated users is overprivileged, we can perform operations beyond a single
GetItem— such as a full tableScan.
Exploitation
::Step 1 — Obtain a Cognito Identity ID
Using the exposed IdentityPoolId, we request an anonymous identity from AWS Cognito:
::Step 2 — Exchange Identity for Temporary Credentials
With the IdentityId, we request temporary credentials valid for the unauthenticated IAM role:
These are temporary STS credentials — they include an AccessKeyId, SecretKey, and a SessionToken. All three must be used together. They expire after a short period, but that's more than enough time to exfiltrate data.
::Step 3 — Configure AWS CLI
Export the temporary credentials into the environment so subsequent aws commands use the guest role:
::Step 4 — DynamoDB Table Scan
The frontend only calls GetItem for the current visitor. However, the unauthenticated IAM role is overprivileged and allows Scan on the entire table. We dump every record:
To quickly locate the flag, we filter the output:
Flag
Automation
This entire exploit chain — from fetching the frontend source to capturing the flag — can be fully automated. Instead of running each command manually, here's an interactive demo of the automation script in action:
References

Red Team Consultant · Penetration Tester · Bug Bounty Hunter
Offensive security professional with 250+ vulnerabilities reported across 50+ organizations including Atlassian, Vimeo, and AT&T. Sharing research, tools, and field notes.