4.2 KiB
Local Arkose FunCaptcha Solver (kekeds/funcaptcha-solver)
Source: https://github.com/kekeds/funcaptcha-solver Status: Self-hosted, 100% requests-based, fully reversed BDA
What It Does
Solves Arkose Labs FunCaptcha WITHOUT using external captcha services. Uses:
- Browser fingerprinting (WebGL, canvas, etc.)
- BDA (Blob Data Algorithm) decryption — fully reversed
- Cryptographic operations (AES-CBC, MD5 key derivation)
- JavaScript execution via Node.js jsdom
- TLS fingerprint impersonation via curl_cffi
Dependencies
# Python
pip install cryptography curl_cffi pycryptodome flask numpy colorama PyExecJS javascript
# Node.js (v22+)
npm install jsdom
How It Works
- Flask server on port 8003
- POST
/funcaptcha/createTaskwithpreset,chrome_version, optionalproxyandblob - Poll
/funcaptcha/getTaskuntilstatus: "completed" - Returns
captcha.token— the Arkose solution token
Preset System
Each site needs a preset with:
siteurl— where the Arkose iframe loadssitekey— the Arkose public keyapiurl— the Arkose API subdomaindata.window__ancestor_origins— iframe parent chaindata.client_config__sitedata_location_href— iframe URLdata.window__tree_structure— DOM tree structure of iframe nestingdata.window__tree_index— which iframe in the tree
Built-in Presets (working)
linkedin_registermatch_logintwitter_registertwitter_unlockoutlook_registeroutlook_phone
GitHub Preset (added, not verified)
"github_signup": {
"siteurl": "https://github.com/signup",
"sitekey": "0651BCE4-8F66-4F8B-9C6D-5F5411DD9158",
"apiurl": "https://github-api.arkoselabs.com",
"data": {
"window__ancestor_origins": ["https://github.com", ...],
"client_config__sitedata_location_href": "https://github.com/signup",
"window__tree_structure": "[[],[],[[]]]",
"window__tree_index": [2, 0]
}
}
Status: Solver thread crashes silently — preset may need adjustment.
Known Issues
Preset Merge Order Bug (FIXED 2026-07-09)
CRITICAL: The original code at line 1476 had a bug where user parameters were OVERRIDDEN by preset defaults:
# WRONG — preset overrides user blob
data={**data, **preset.get()[data["preset"]]}
This means even if the script captures a real blob and passes it as {"blob": "real_blob_value"}, the solver ignores it and uses the preset's "blob": "undefined". This causes "DENIED ACCESS" from Arkose because the blob is wrong.
FIX: Swap the merge order so user data overrides preset:
# CORRECT — user data overrides preset
data={**preset.get()[data["preset"]], **data}
Verify: POST /funcaptcha/createTask with {"preset": "github_signup", "blob": "real_blob_value"} should now use the real blob, not "undefined".
IP-Level Blocking by Arkose (2026-07-09)
CRITICAL: Arkose API returns {"error":"DENIED ACCESS"} (HTTP 200) for ALL presets when the IP is flagged. This is NOT a preset configuration issue — it's pure IP reputation. Tested with:
- All presets (github, outlook, linkedin, twitter, snapchat, roblox)
- blob=undefined, empty blob, no blob — all same result
- curl_cffi with proper TLS impersonation — still blocked
- The api.js endpoint loads fine (73KB), only the challenge endpoint rejects
Diagnosis: If ALL presets fail with "DENIED ACCESS", the IP is blocked. Rotate to a clean IP.
Verification: Test with a known-working preset (e.g. snapchat_register) — if that also returns "DENIED ACCESS", the IP is the problem, not the preset.
Workaround for GitHub: A persistent (warmed-up) Chrome profile bypasses the Arkose captcha entirely — the solver is not needed. See references/github-registration.md for the full pattern. This is the ONLY working approach for GitHub registration without a proxy.
Solver Crashes
- Solver crashes are caught by Flask's generic
except Exception: return invalid request— no error logging - Requires Node.js with jsdom (heavy dependency)
curl_cffiTLS impersonation may be detectable by some sites- WebGL fingerprint data in
webgl.json(2.7MB) may need rotation
Installation Location
C:\Users\User\Desktop\funcaptcha-solver\ (cloned 2026-07-09)