When Your Scanner Can't Get Through the Front Door: Cloudflare Bot Protection and the Modern Bug Bounty Hunter
Doctolib blocks curl at the TLS layer, not the IP layer. How Cloudflare bot fight mode works and what to do when standard scanner tooling can't get through.
Affiliate Disclosure: This site contains affiliate links. We earn a commission when you purchase through our links at no additional cost to you.
The story of running a structured bug bounty campaign against Doctolib (âŹ50k max bounty, YesWeHack) and hitting an invisible wall that no User-Agent trick can solve. Doctolib uses Cloudflareâs aggressive bot fight mode â specifically TLS fingerprint-based JS challenges that block curl, Python requests, and most programmatic HTTP clients, even from residential IPs. This is increasingly common on high-value healthcare/fintech targets. Hunters who donât understand the mechanism will waste hours debugging a problem they canât tool their way around.
This article turns the Doctolib experience into a practical guide: what TLS fingerprinting is, how to detect it, what your options actually are, and what to do when automation is genuinely blocked.
The Doctolib Campaign
SecurityClaw ran a structured campaign against Doctolib in May 2026: 15 skills, User-Agent: BugBounty/42 (YWH), rate-limited to â¤10 requests/second, targeting api.doctolib.fr as the primary surface (the API endpoint that bypasses the JS challenge on www.doctolib.fr).
The infrastructure confirmed before the campaign ran: Cloudflare CDN on www.doctolib.fr (CF-RAY in response headers, cf-mitigated: challenge on curl), Rails monolith (_doctolib_session encrypted session cookie), Datadog RUM and Sentry monitoring. Target scope confirmed in YesWeHack program: *.doctolib.fr, *.doctolib.de, *.doctolib.it all in-scope.
15 skills ran against api.doctolib.fr. 20 raw findings. Zero submittable.
The reason: every finding that looked interesting on first pass had an explanation that killed submittability on second look.
Key Technical Points
1. The real block is TLS fingerprinting, not IP or User-Agent
Cloudflareâs bot fight mode uses JA3/JA4 TLS fingerprints â a hash of how your TLS client says hello. curl, Python requests, and most HTTP libraries have fingerprints that are trivially identified as non-browser. Rotating the User-Agent string does nothing because the block happens at the TLS layer, before HTTP headers are even read. Even on a residential IP, a curl-based client will be challenged.
The JA3 fingerprint is computed from:
- TLS version
- Cipher suites (in order)
- Extension types (in order)
- Elliptic curve groups
- Elliptic curve point formats
A curl 8.x TLS hello looks nothing like a Chrome 124 TLS hello, and Cloudflareâs fingerprint database has been trained on billions of browser interactions. The mismatch is detected in milliseconds. No User-Agent string changes this â the fingerprint comes from the TLS handshake, not the HTTP request.
2. Detection signature: cf-mitigated: challenge
When Cloudflareâs JS challenge fires, the response includes cf-mitigated: challenge in the headers. If your scanner is getting this header, youâre not hitting a rate limit or a soft block â youâre hitting active bot protection. Stop, donât retry 20 times hoping itâll lift.
# Quick detection check (30 seconds):
curl -si https://www.target.com -H "User-Agent: Mozilla/5.0" | grep -i "cf-mitigated"
# cf-mitigated: challenge â Cloudflare JS challenge active
If cf-mitigated: challenge appears, curl and Python requests wonât get through regardless of IP, User-Agent, or rate limiting.
3. BugBounty/42 (YWH) User-Agent is program-layer, not Cloudflare-layer
YesWeHack programs require this UA header so the targetâs own WAF rules whitelist scanner traffic. It operates at Layer 7 (HTTP). Cloudflareâs bot fight mode operates at Layer 4 (TLS). The two layers are independent. YWH UA passes the programâs allowlist; it does not bypass Cloudflareâs TLS fingerprint challenge.
This matters because hunters frequently assume that using the correct platform User-Agent should get them through bot protection. It doesnât. The program operator added the UA to their own WAF allowlist â they canât add it to Cloudflareâs TLS fingerprint whitelist, which operates at a layer the origin server doesnât control.
4. What the Doctolib triage found
20 raw findings, 0 submittable. The triage breakdown explains why:
Finding 1 â CSP Missing (scanner: HIGH â FALSE POSITIVE)
api.doctolib.fr serves content-security-policy-report-only with nonces and a strict allowlist. The scanner flagged âno enforced CSP.â Correct technical observation. But report-only with a well-crafted policy is a mid-rollout deployment state, not a missing control. Not submittable.
Finding 2 â Permissions-Policy / COOP / CORP (scanner: MEDIUM/LOW â NOT SUBMITTABLE) Standard browser-hardening headers. On an API endpoint, no demonstrated exploit path. Doctolibâs existing CSP and HSTS already address higher-priority controls. These are treated as informational by YesWeHack programs.
Finding 3 â SRI Missing on Datadog/Sentry (scanner: HIGH Ă 7 â INFORMATIONAL)
www.datadoghq-browser-agent.com/datadog-rum-v4.js and Sentry scripts loaded without Subresource Integrity. Technically valid. Programs universally treat third-party monitoring scripts as out-of-scope for SRI enforcement â vendors donât publish stable hashes for CDN scripts that update continuously.
Finding 4 â Referrer-Policy missing on pro.doctolib.fr (scanner: MEDIUM â INFORMATIONAL)
pro.doctolib.fr sends Referrer: https://pro.doctolib.fr/internal/path to third-party requests. Real privacy concern for a healthcare platform handling patient data. YesWeHack verdict: informational, no demonstrated patient data exposure.
The pattern: SecurityClaw found real things, but real things that Doctolibâs program explicitly excludes or treats as informational. Thatâs not scanner failure â itâs triage working.
5. Your options when blocked by Cloudflare bot fight mode
Switch to browser-based testing: Selenium/Playwright with a real Chrome binary has a browser TLS fingerprint. Cloudflare typically doesnât challenge it (or challenges much less aggressively). This works for interactive testing but not for bulk automation.
from playwright.async_api import async_playwright
async with async_playwright() as p:
browser = await p.chromium.launch(headless=True)
page = await browser.new_page()
response = await page.goto("https://www.doctolib.fr/")
# Chrome TLS fingerprint â Cloudflare doesn't challenge it
print(response.status) # 200
Use a browser-based proxy: Tools like Caido or Burp Suite with an embedded Chromium keep your proxy fingerprint clean. Combine with manual exploration.
Focus on what the scanner CAN reach: Even when www.doctolib.fr is blocked, CT log enumeration (certspotter, crt.sh) still works â it queries the CT log service, not the target. JS bundle analysis from assets.doctolib.fr (accessible, CDN-served, no JS challenge) can reveal API endpoints for authenticated manual testing later.
Accept the scope and prioritise manual work: Doctolib has a âŹ50k max bounty. The highest-value findings on a healthcare platform â IDOR in patient booking data, authentication bypass on pro.doctolib.fr, access control failures in the API â require authentication anyway. A valid test account and Burp Suite with a browser are a better investment than fighting Cloudflare.
The CVE scoring mismatch problem
The SecurityClaw CVE pipeline scored Doctolib at 10.0 because it matched by bounty ceiling Ă CVSS, not by tech stack relevance. The matched CVEs (SimpleHelp OIDC bypass, SAP NetWeaver, Flowise, Magento file upload) have zero relevance to Doctolibâs Ruby on Rails stack.
This is a known gap in automated CVE-to-target matching â the pipeline needs tech stack confirmation before a CVE score means anything. A CVSS 10.0 CVE in Magento doesnât mean anything if the target runs Rails. The fix is in progress: the CVE intelligence pipeline (covered separately) now validates tech stack match before computing opportunity scores.
The Doctolib campaign was also where this mismatch became visible at real scale: a âŹ50k max bounty target that looks like the highest-opportunity target in the queue, but where the relevant CVE surface (Rails CVEs, Rails gem vulnerabilities, OAuth implementation bugs in omniauth-doctolib) is a much smaller set than the automated score suggested.
Impact Narrative
Healthcare SaaS is a high-value bug bounty category: PHI (Protected Health Information) leakage = instant critical finding on most programs. Doctolib processes patient booking data for millions of users across France, Germany, and Italy. But that same healthcare-grade paranoia means aggressive bot protection, strict scoping, and explicitly non-qualifying automated scanner findings.
The lesson for hunters: before launching a campaign against a healthcare/fintech target, spend 10 minutes checking whether their Cloudflare configuration blocks programmatic clients. The detection signature (cf-mitigated: challenge) takes 2 seconds to check with a single curl command. If it fires, youâre not getting in with standard tooling.
Doctolib in particular is worth manual testing. The program is open, the bounties are real, and the patient data surface is significant. But itâs a manual test target, not an automated scan target.
Why This Matters For Bug Bounty Hunters
Cloudflareâs bot protection is deployed on a large fraction of high-value bug bounty targets. Hunters hitting this wall typically:
- Assume itâs a User-Agent block (it isnât) and rotate UA strings (doesnât work)
- Assume itâs a datacenter IP block (it isnât â residential egress doesnât help with TLS fingerprinting)
- Waste hours trying to debug or bypass something that fundamentally requires a real browser
This article saves those hours. It explains the real mechanism, the correct detection signature, and the actual options â including when to abandon automation and switch to manual browser-based testing.
Related articles
- Cloudflare bot protection: the full pattern analysis â how bot fight mode affects multiple targets, not just Doctolib
- How to use CVE intel before you scan â why tech stack confirmation matters before acting on automated opportunity scores
- CORS misconfiguration hunting on EU bug bounty targets â companion methodology for EU-focused campaigns
If Cloudflare is blocking your automated campaigns and youâre also hitting Akamai-protected targets, the residential IP approach SecurityClaw built is covered in the bol.com WAF bypass walkthrough. The residential container approach solves Akamai CDN blocks; Cloudflare TLS fingerprinting still requires browser-based tooling regardless of egress IP.
Recommended Reading
- Black Hat Python ($40)
- Violent Python ($35)
- Hacking: The Art of Exploitation ($40)