August 5, 2026
Hacker’s Holiday 2026: Towel on the Sunbed Writeup
Exploiting a Race Condition (TOCTOU) in TryHackMe’s “Towel on the Sunbed”
By Angeline Marietta Charley
5 min read
Room: Towel on the Sunbed (TryHackMe Hacker's Holiday 2026)
Category: Business Logic / API Abuse
Vulnerability class: Race Condition (TOCTOU) - CWE-362
Disclaimer: This walkthrough is based on the intentionally vulnerable TryHackMe room Towel on the Sunbed from the Hacker's Holiday 2026 event. All testing was performed in the provided lab environment for educational purposes.
Introduction
Ponzi Portfolio is a fictional wellness-resort cryptocurrency rewards application. Every new guest starts with 0 PONZI and can claim a 50 PONZI staking reward once every 24 hours. Reaching 150 PONZI unlocks the exclusive Whale Vault, which would normally require three days of consecutive claims. The objective of this room is to determine whether the application's business logic can be abused to reach the Whale tier immediately instead of waiting three days.
My goal was to understand how the reward system was enforced and whether there was a way to bypass the 24-hour restriction.
Initial Attempt (and the Wall I Hit)
I started by registering a guest account (test / testuser) and was taken to the application dashboard. The account began with 0 PONZI, belonged to the Shrimp tier, and the Whale Vault remained locked until reaching 150 PONZI. The only available action was the Claim Reward button.
Before interacting with the application further, I checked the page source and the dashboard.js file to see whether any client-side logic was responsible for enforcing the reward timer. There wasn't. The JavaScript appeared to be responsible only for rendering the user interface, so any meaningful validation was likely happening on the server.
I then configured Burp Suite, intercepted the request generated by the Claim Reward button, and sent it to Repeater.
The first request succeeded as expected. My balance increased from 0 to 50 PONZI, but I remained in the Shrimp tier.
Out of curiosity, I immediately replayed the exact same request.
This time, the application rejected it with a 429 Too Many Requests response, informing me that I had already claimed the reward and needed to wait another 24 hours.
Although I hadn't bypassed anything yet, this response was valuable. It confirmed that the server wasn't simply trusting the client, it was checking whether a reward had already been claimed before allowing another one.
At that point, there was no reason to continue testing with the same account because I had already used my daily claim. Instead, I logged out and created a fresh account (test / testing) so I could experiment with the timing of the requests rather than waiting an entire day.
Understanding the Vulnerability
Based on the application's behaviour, I inferred that the backend was performing something similar to the following logic:
- Read the user's
last_claim_timestamp. - If the previous claim was made less than 24 hours ago, reject the request.
- Otherwise, award 50 PONZI.
- Update the
last_claim_timestamp.
Sending requests one after another would never bypass this logic because the first request finishes updating the timestamp before the second request is processed.
The interesting part is the brief period between the server checking whether the reward can be claimed and updating the timestamp afterwards.
If multiple requests reach the server during that tiny window, they can all observe the old timestamp, conclude that the reward is still available, and each process successfully before any of them writes the updated value back to the database.
This is a classic Time-of-Check to Time-of-Use (TOCTOU) race condition.
Exploiting the Race Condition
Using a new account, I intercepted the reward request again and sent it to Burp Repeater.
Instead of sending the request repeatedly, I duplicated it into a Repeater group of three and selected Send group (parallel).
Unlike normal sequential requests, Burp sends every request in the group at almost exactly the same time.
Because the requests were processed concurrently, they all reached the server before the claim timestamp had been updated.
All three requests succeeded.
{
"message": "Staking reward claimed successfully.",
"reward": 50,
"newBalance": 150,
"tier": "Whale",
"priceSnapshot": 4.2
}{
"message": "Staking reward claimed successfully.",
"reward": 50,
"newBalance": 150,
"tier": "Whale",
"priceSnapshot": 4.2
}Rather than progressing from 0 → 50 PONZI, the account immediately jumped from 0 → 150 PONZI, unlocking the Whale tier in a single burst.
After disabling Burp interception and refreshing the dashboard, the Whale Vault was now accessible, completing the objective of the room.
(Flag on my Github linked below)
How I Would Fix It
From my observations, the application performs the eligibility check and the timestamp update as two separate operations. Because these operations are not atomic, multiple concurrent requests can all pass the validation before any of them updates the stored timestamp.
Some possible mitigations include:
- Performing the validation and update inside a single database transaction (for example, using
SELECT ... FOR UPDATE) so only one request can process the reward at a time. - Using a conditional database update that succeeds only if the previous claim was more than 24 hours ago. Once one request updates the record, all concurrent updates automatically fail.
- Recording each claim as a separate database entry protected by a unique constraint (for example, one reward per account per day), ensuring only one insert can succeed.
Simply adding rate limiting would not solve the underlying issue. The vulnerability is caused by concurrent requests reaching the server during the same processing window and not by sending too many requests over a longer period.
Note:_ These recommendations are based solely on the application's observed behaviour. Since I did not have access to the backend source code, I cannot confirm the exact implementation._
What I Learned
This room reinforced that vulnerabilities are not always caused by poor input validation or missing authentication. Sometimes the application performs all the correct checks but performs them in the wrong order.
Although my first account didn't expose the vulnerability, it provided the clue I needed. The 24-hour lockout demonstrated that the application was tracking claim timestamps, which meant the challenge wasn't to bypass the check itself, but to exploit the tiny window between checking the timestamp and updating it.
It also highlighted why race conditions are difficult to identify through normal application testing. Reading client-side JavaScript or manually clicking buttons would never reveal this issue. The vulnerability only became visible when multiple requests were processed concurrently, making Burp Suite's parallel request feature the key to reproducing the bug.
Overall, this room gave me a practical understanding of how TOCTOU race conditions occur and why synchronizing shared resources is essential when designing secure web applications.
Additional Resources
This Medium article focuses on the methodology and concepts behind the vulnerability.
If you'd like to see the complete walkthrough chronologically with all the screenshots, steps, and the room flag, you can find it on my GitHub: