#!/usr/bin/env python3 """Agent for testing race condition (TOCTOU) vulnerabilities in web applications.""" import argparse import json import sys import threading import time from datetime import datetime, timezone try: import requests HAS_REQUESTS = True except ImportError: HAS_REQUESTS = False def send_concurrent_requests(url, method, data, headers, count, results_list): """Send multiple identical requests concurrently to trigger race conditions.""" if not HAS_REQUESTS: return barrier = threading.Barrier(count) def worker(idx): try: barrier.wait(timeout=5) if method == "POST": resp = requests.post(url, json=data, headers=headers, timeout=15, verify=False) elif method == "PUT": resp = requests.put(url, json=data, headers=headers, timeout=15, verify=False) else: resp = requests.get(url, headers=headers, timeout=15, verify=False) results_list.append({ "thread": idx, "status_code": resp.status_code, "response_length": len(resp.content), "response_preview": resp.text[:200], "elapsed": resp.elapsed.total_seconds(), }) except Exception as e: results_list.append({"thread": idx, "error": str(e)[:100]}) threads = [] for i in range(count): t = threading.Thread(target=worker, args=(i,)) threads.append(t) t.start() for t in threads: t.join(timeout=20) def analyze_results(results): """Analyze concurrent request results for race condition indicators.""" indicators = [] success_count = sum(1 for r in results if r.get("status_code") == 200) if success_count > 1: indicators.append(f"{success_count} successful responses (expected 1 for idempotent operations)") response_bodies = [r.get("response_preview", "") for r in results if r.get("status_code") == 200] unique_bodies = set(response_bodies) if len(unique_bodies) > 1: indicators.append(f"Different response bodies across concurrent requests: {len(unique_bodies)} unique") status_codes = [r.get("status_code") for r in results if r.get("status_code")] if len(set(status_codes)) > 1: indicators.append(f"Mixed status codes: {set(status_codes)}") return indicators def test_race_condition(url, method, data, token, concurrency): """Execute race condition test.""" headers = {"Authorization": f"Bearer {token}"} if token else {} headers["Content-Type"] = "application/json" results = [] send_concurrent_requests(url, method, data, headers, concurrency, results) indicators = analyze_results(results) return { "url": url, "method": method, "concurrency": concurrency, "responses": results, "race_indicators": indicators, "potential_race": len(indicators) > 0, } def main(): parser = argparse.ArgumentParser( description="Test race condition vulnerabilities (authorized testing only)" ) parser.add_argument("--url", required=True, help="Target URL") parser.add_argument("--method", default="POST", choices=["GET", "POST", "PUT"]) parser.add_argument("--data", default="{}", help="JSON payload") parser.add_argument("--token", help="Bearer token") parser.add_argument("--concurrency", type=int, default=10, help="Concurrent requests") parser.add_argument("--output", "-o", help="Output JSON report") args = parser.parse_args() print("[*] Race Condition Testing Agent") print("[!] For authorized security testing only") data = json.loads(args.data) result = test_race_condition(args.url, args.method, data, args.token, args.concurrency) report = { "timestamp": datetime.now(timezone.utc).isoformat(), "test_result": result, "risk_level": "HIGH" if result["potential_race"] else "LOW", } print(f"[*] Race condition detected: {result['potential_race']}") if args.output: with open(args.output, "w") as f: json.dump(report, f, indent=2) print(f"[*] Report saved to {args.output}") else: print(json.dumps(report, indent=2)) if __name__ == "__main__": main()