РоБОТЫ 2
РоБОТЫ 2
Platform: bug-makers | Category: Web | Type: Challenge | Difficulty: Medium | OS: Linux | Author: XESXOR | Date: 2026-05-26 | Status: Solved Techniques: browser_impersonation, cloudflare_bot_bypass, ja3_impersonation, python_sandbox_enumeration, tls_fingerprint_bypass
Summary
Task: Flask sandbox runs a user-supplied solve(url) function against a Cloudflare-protected target; requests.get returns 403 because Python's TLS/JA3 fingerprint is flagged as a bot. Solution: use curl_cffi with impersonate='chrome120' to mimic Chrome's TLS handshake and defeat Cloudflare's JA3-based bot detection.
РоБОТЫ 2 — bug-makers CTF
Description
Боты, боты и еще раз боты, они везде и повсюду! Уже все давным давно научились их вычислять, даже не по хедерам, а как-то по другому. А может ты тоже бот? Докажи обратное.
English summary: "Bots, bots, and more bots — they're everywhere! Everyone has long learned to detect them, not even by headers, but somehow differently. Maybe you're a bot too? Prove otherwise." The challenge is hosted at https://robots-2.t1.bug-makers.ru.
The key semantic hint is "not even by headers, but somehow differently" — this points to TLS / JA3 fingerprinting rather than HTTP header inspection.
Analysis
The homepage is served by Werkzeug/3.1.6 Python/3.9.25 (Flask) and presents a Python code editor. You must define a function:
python read-only • copy alloweddef solve(url: str): ...
that returns a real HTTP response object with status 200, within a 30-second time limit. Code is submitted via POST /api/submit with a JSON body {"code": "..."}. The sandbox substitutes a placeholder <URL> with a Cloudflare-protected target and calls solve(target_url).
Observations during recon:
/robots.txtreturns 404 — a red herring playing on the task name "robots".- Submitting the default code
requests.get(url)returns HTTP 403 — "Cloudflare says you're still a bot" (Russian message). So the target is fronted by Cloudflare's bot-detection WAF. - A traceback leaked the sandbox path
/app/sandbox.pyline 276:result = solve_fn(target_url). - Rate limit is roughly 1 request/minute after a short burst.
Sandbox validation blacklist
Probing the validator revealed a blacklist of dangerous constructs:
open()— forbiddengetattr()— forbidden- substring
__import__— forbidden - attribute access
__dict__— forbidden - importing
importlib— forbidden
Crucially, plain import statements are allowed (the default code itself imports requests). This means we can freely import alternative HTTP libraries.
Why requests fails
Cloudflare's bot detection here keys on the TLS/JA3 handshake fingerprint, not on HTTP headers. Python's requests (urllib3 over OpenSSL) produces a recognizable non-browser TLS ClientHello fingerprint and is blocked regardless of any User-Agent or header spoofing. This matches the challenge hint perfectly: detection is "not by headers, but somehow differently".
Available libraries
Using plain try/except imports inside solve, the sandbox was probed for installed libraries. The following TLS-fingerprint-bypass-capable libraries were all available:
curl_cfficloudscraperhttpxtls_client
The presence of curl_cffi is the strongest signal: it can impersonate a browser's exact TLS/JA3 fingerprint.
Solution
Use curl_cffi with browser impersonation. The impersonate="chrome120" option makes the underlying libcurl emit Chrome's exact TLS ClientHello (cipher suites, extensions, curves), so Cloudflare's JA3 fingerprinting sees a legitimate Chrome browser and returns HTTP 200.
python read-only • copy alloweddef solve(url): from curl_cffi import requests as creq r = creq.get(url, impersonate="chrome120") return r
Submit it via the API:
bash read-only • copy allowedcurl -s https://robots-2.t1.bug-makers.ru/api/submit \ -H 'Content-Type: application/json' \ -d '{"code": "def solve(url):\n from curl_cffi import requests as creq\n r = creq.get(url, impersonate=\"chrome120\")\n return r\n"}'
The response is HTTP 200 and contains the flag. Equivalent solutions exist with cloudscraper or tls_client, which also rewrite the TLS handshake to mimic a browser.