September 19, 2026
Tryhackme Towel on the Sunbed Writeup: Complete CTF Walkthrough
Introduction

By nithunwilson
4 min read
Introduction
In this TryHackMe CTF, I worked on Ponzi Portfolio, a web application built around a cryptocurrency staking system. The goal was to reach the required PONZI balance and unlock the Whale Vault.
During enumeration, I found that the /claim endpoint was vulnerable to a race condition, allowing me to send multiple reward requests concurrently and bypass the intended 24-hour claim restriction. By exploiting this flaw, I increased my balance, unlocked the vault, and retrieved the flag.
This write-up covers the steps I followed, from account creation and Burp Suite interception to exploiting the race condition and obtaining the flag.
1. Access the Web Application
I started by opening the target:
The application was a crypto-themed website called Ponzi Portfolio.
2. Create an Account
On the login page, I clicked Register to create a new account.
I entered a username and password, then clicked Create Account.
After the account was created, I was redirected to the dashboard.
3. Check the Dashboard
Initially, my portfolio showed:
0 PONZI0 PONZIThe interesting part was the Staking Rewards section.
It said I could:
Earn 50 PONZI every 24 hours by claiming your staking reward.
There was also a Claim Reward button.
4. Capture the Claim Request
I opened Burp Suite Community Edition and configured the browser to use the Burp proxy.
- Open Burp Suite Community Edition.
- Go to the Proxy tab.
- Turn Intercept on.
- Return to the Ponzi Portfolio dashboard.
- Click the Claim Reward button.
- Go back to Burp Suite.
The request was now captured in the Proxy โ Intercept section:
This allowed me to inspect the /claim request before it was forwarded to the server.
5. Test for a Race Condition
After capturing the /claim request, I moved it to Repeater.
- Right-click the intercepted request and select Send to Repeater. Alternatively, use Ctrl + R.
- Go to the Repeater tab. The
/claimrequest will appear in a new tab.
- Right-click the Repeater tab and select Add tab to group.
- Select New tab group and name the group Group1.
- Right-click the request tab again and select Duplicate tab.
- I duplicated the request around 10 times. I used more requests than the required 3 because sending several requests simultaneously increases the chance that multiple requests will be processed before the server updates the reward state.
- Click the dropdown arrow next to the Send button.
- Select Send group (parallel).
- Finally, click Send group (parallel) to send all the
/claimrequests at approximately the same time.
After sending the requests in parallel, the responses showed that the reward had been claimed successfully. We could see:
{
"message": "Staking reward claimed successfully.",
"reward": 50,
"newBalance": 250,
"tier": "Whale",
"priceSnapshot": 4.2
}{
"message": "Staking reward claimed successfully.",
"reward": 50,
"newBalance": 250,
"tier": "Whale",
"priceSnapshot": 4.2
}The newBalance increased to 250 PONZI, confirming that multiple reward claims were processed. This showed that the /claim endpoint could be exploited through a race condition.
6. Open the Whale Vault
After the balance was increased, I went back to the Dashboard and refreshed the page.
The Open Vault button was now enabled.
I clicked Open Vault, which revealed the flag.
Why the Race Condition Worked
The application was designed to allow a user to claim 50 PONZI once every 24 hours. The important part is how the server handles multiple requests arriving at nearly the same time.
A simple way to understand this is with a cinema that has one movie ticket left. Alice and Bob both try to book it at the same time. The server checks whether the ticket is available before marking it as sold:
Alice โ Check โ Available
Bob โ Check โ Available
Alice โ Book ticket
Bob โ Book ticketAlice โ Check โ Available
Bob โ Check โ Available
Alice โ Book ticket
Bob โ Book ticketBecause both requests passed the availability check before the ticket was updated, both could potentially receive the same ticket.
The same principle applies to the reward system. If multiple requests reach the server during the small window between checking the claim status and updating it, more than one request can pass the check.
This is known as a race condition, where the final result depends on the timing of concurrent operations.
In this application, the /claim endpoint should have ensured that the reward check and the balance/state update happened as a single atomic operation. Without that protection, sending multiple requests in parallel allowed the same reward logic to be executed more than once.
How It Can Be Prevented
Developers can reduce this type of vulnerability by:
- Using atomic database transactions for check-and-update operations.
- Applying appropriate database locks when modifying shared state.
- Enforcing unique constraints where applicable.
- Performing reward or balance updates on the server side, rather than relying on client-side restrictions.
- Designing endpoints so that repeated or concurrent requests cannot produce multiple successful operations.
Conclusion
This CTF was a good example of how a small timing issue in an application's logic can lead to an unintended result. The /claim endpoint was intended to allow only one 50 PONZI reward every 24 hours, but concurrent requests allowed the reward to be processed multiple times.
The key takeaway is that check-and-update operations involving shared resources must be handled atomically. Proper synchronization, transactions, locking, and server-side validation can help prevent race conditions and ensure that an action intended to happen once cannot be performed multiple times simultaneously.