Overview

Towel on the Sunbed is a TryHackMe web challenge built around Ponzi Portfolio, a crypto rewards dashboard that lets users claim 50 PONZI once every 24 hours, with 150 PONZI unlocking a restricted “Whale Vault”. The /claim endpoint enforces its once-per-day cooldown with a check that isn’t atomic with the update, so firing the same claim request multiple times in parallel through Burp Suite’s Repeater lets several claims land before the cooldown is recorded, a classic TOCTOU (Time-of-Check to Time-of-Use) race condition.

Attack Chain

  • Scan the target and identify SSH on port 22 and an HTTP service on port 3000
  • Get redirected to the /auth/login page for Ponzi Portfolio
  • Confirm the backend is Express.js on Node.js from the X-Powered-By header
  • Register a new account and reach the crypto rewards dashboard
  • Learn that claiming rewards grants 50 PONZI every 24 hours, with 150 PONZI unlocking the Whale Vault
  • Claim the reward once and observe the cooldown timer
  • Inspect the /claim request and rule out simple parameter tampering (secondsRemaining)
  • Register a second, unclaimed account to test for a race condition
  • Capture the /claim request in Burp Suite and send it to Repeater
  • Group and fire multiple /claim requests in parallel
  • Confirm several claims succeed before the cooldown takes effect, crossing the 150 PONZI threshold
  • Open the Whale Vault and read the flag

Enumeration

Port Scanning

We start by defining the target and running a full TCP port scan.

export IP=10.82.152.132; export NAME=TOWELSUNBED; echo $IP; echo $NAME; ping $IP -c 1

nmap --min-rate 4500 --max-rtt-timeout 1500ms $IP -p- -v -oA scans/nmap_allports_$NAME

The open TCP ports are:

22,3000

We export the discovered ports and run a service and version scan.

ports=$(cat scans/nmap_allports_$NAME.nmap | grep '^[0-9]' | cut -d '/' -f 1 | tr '\n' ',' | sed s/,$//); echo $ports

nmap $IP -p$ports -A -oA scans/nmap_initial_$NAME -v

nmap results

The scan identifies SSH on port 22 and a web service on port 3000, which becomes the focus of enumeration.


Web Enumeration

Ponzi Portfolio Login

Browsing to port 3000 redirects us to /auth/login, a login page for Ponzi Portfolio.

Ponzi Portfolio login page

We try logging in with ponzi:ponzi and receive an Invalid credentials message. Looking at the request in BurpSuite, the response’s X-Powered-By header indicates we’re working with the Express.js framework on Node.js.

invalid login and Express header

Registration and Dashboard

We register a new account with test:Pass1234! to see what we can find.

registering an account

We land on a crypto rewards dashboard.

crypto rewards dashboard

At the bottom of the page, we can Claim Reward for 50 PONZI every 24 hours. Reaching 150 PONZI unlocks the Whale Vault.

claim reward and Whale Vault info


Race Condition

Claiming the Reward

We claim our reward. Afterwards, a timer counts down to the next claim, and our balance now shows 50 PONZI towards the Whale Vault.

reward claimed with cooldown timer

Looking at the /claim POST request doesn’t give us much to work with, attempting to claim again just returns an error that the reward was already claimed, along with the remaining cooldown.

claim request returns already-claimed error

We try sending our own JSON body with secondsRemaining set to 0, but get the same result.

tampering with secondsRemaining has no effect

Testing for a Race Condition

Simple parameter tampering doesn’t work, so instead we test whether the /claim endpoint’s cooldown check is vulnerable to a race condition by sending the same original request multiple times at once.

We register a second account with test2:Pass1234!. On this fresh account, the reward hasn’t been claimed yet.

second account with unclaimed reward

With BurpSuite set to intercept requests, we click Claim Reward and capture the POST request.

intercepting the claim request in Burp

We send the request to Repeater. To fire multiple copies at once, we create a tab group and duplicate the request across several tabs.

creating a Repeater tab group

We then click the drop-down arrow next to Send and select Send group in parallel to test for the race condition, followed by Send group.

sending the request group in parallel

Looking at the responses, the race condition succeeds, the /claim cooldown check isn’t atomic with the update, so multiple requests land before the reward is marked as claimed, and we’re now eligible for the Whale Vault.

race condition succeeds across parallel responses

Refreshing the dashboard confirms our balance jumped to 450 PONZI.

dashboard now showing 450 PONZI


Flag

With enough PONZI, we can open the Whale Vault.

opening the Whale Vault

Opening the vault reveals the flag.

flag


Key Takeaways

Towel on the Sunbed is a compact demonstration of a TOCTOU race condition in a business-logic control rather than a memory-safety bug. The /claim endpoint checked whether a reward had already been claimed and then updated that state as two separate, non-atomic steps, leaving a window where several requests could all pass the check before any of them recorded the claim. Burp Suite’s Repeater tab groups made this trivial to exploit by firing the same authenticated request in parallel, turning a once-a-day reward into an instant path to the restricted Whale Vault.