Captcha bypass tutorials

Was this helpful?

How to bypass Cloudflare Challenge on VFS Global

Gregory Fisher
Gregory Fisher

Technical engineer

Introduction

VFS Global is the world's largest visa center operator, using Cloudflare Turnstile to protect login and registration forms from automation. Unlike standard implementations, VFS Global uses dynamic parameters (data, pagedata, action) that are generated with each interaction with the form.

In this guide, we will break down how to intercept these dynamic parameters using a special script, solve the captcha via the 2Captcha API, and correctly inject the token into the page using a callback function.

Task parameters

To bypass Cloudflare Turnstile, the TurnstileTask task type is used. For VFS Global, it is critical to pass not only the sitekey but also the dynamic parameters, as well as the exact User-Agent.

json Copy
{
    "clientKey": "YOUR_API_KEY",
    "task": {
        "type": "TurnstileTaskProxyless",
        "websiteURL": "https://visa.vfsglobal.com/tur/en/fra/login",
        "websiteKey": "0x4AAAAAAAAjq6WYeRDKmebM",
        "data": "dynamic_data_string_here",
        "pagedata": "dynamic_pagedata_string_here",
        "action": "login",
        "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36"
    }
}

How to find parameters

websiteURL

The full address of the page with the captcha. For example: https://visa.vfsglobal.com/tur/en/fra/login

websiteKey (sitekey)

The public Cloudflare Turnstile key. For the specified VFS Global site, it is: 0x4AAAAAAAAjq6WYeRDKmebM

Dynamic parameters (data, pagedata, action, userAgent)

These parameters change with each page load. To get them, you need to inject a special interception script into the browser console (DevTools) BEFORE the captcha loads.

Instructions:

  1. Open the login page in incognito mode and press F12 (Developer Tools).
  2. Go to the Console tab.
  3. Paste the following script and press Enter. Do not refresh the page after this!
javascript Copy
const i = setInterval(()=>{
    if (window.turnstile) {
        clearInterval(i)
        window.turnstile.render = (a,b) => {
            let p = {
                type: "TurnstileTaskProxyless",
                websiteKey: b.sitekey,
                websiteURL: window.location.href,
                data: b.cData,
                pagedata: b.chlPageData,
                action: b.action,
                userAgent: navigator.userAgent
            }
            console.log(JSON.stringify(p))
            window.tsCallback = b.callback
            return 'foo'
        }
    }
}, 10)
  1. Perform the action that triggers the captcha (for example, enter email and password and click the login button).
  2. A JSON object with the current parameters will instantly appear in the console. Copy them.
  3. Immediately send these parameters in the solving request via the API. Do not refresh the page, otherwise the parameters will become invalid.

Code examples

Python + requests (Getting the token)

Used to send a task to the API after you have extracted the dynamic parameters via the console.

python Copy
import sys
import os
sys.path.append(os.path.dirname(os.path.dirname(os.path.realpath(__file__))))

from twocaptcha import TwoCaptcha

api_key = os.getenv('APIKEY_2CAPTCHA', 'YOUR_API_KEY')
solver = TwoCaptcha(api_key)

try:
    result = solver.turnstile(
        sitekey='0x4AAAAAAAAjq6WYeRDKmebM',
        url='https://visa.vfsglobal.com/tur/en/fra/login',
        action='login',
        data='extracted_data_value',
        pagedata='extracted_pagedata_value',
        user_agent='Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36'
    )
    print(f"Token successfully received: {result['code']}")
    
except Exception as e:
    print(f"An error occurred: {e}")

Python + Selenium (Automation with injection)

An adapted script based on the provided file. Uses seleniumbase Driver (undetected-chromedriver) to bypass basic detection, intercepts parameters, and injects the token via a callback function.

python Copy
import time
import chromedriver_autoinstaller_fix
import requests
from seleniumbase import Driver
from selenium.webdriver.common.by import By

chromedriver_autoinstaller_fix.install()

agent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36"
# Pass your own userAgent for the headless version
driver = Driver(uc=True, headless=False, proxy=False, agent=agent)

url = 'https://visa.vfsglobal.com/tur/en/fra/login'
driver.delete_all_cookies()
# Start the driver with a delay, which helps to fool the turnstile
driver.uc_open_with_reconnect(url, reconnect_time=20)
time.sleep(5)

# Close the cookies banner if it exists
try:
    driver.find_element(By.CLASS_NAME, "onetrust-close-btn-handler.onetrust-close-btn-ui.banner-close-button.ot-close-icon").click()
    time.sleep(2)
except:
    pass

# Enter data to trigger the captcha
driver.find_element(By.ID, "mat-input-0").send_keys("alter_shmalter@gmail.com")
time.sleep(1)
driver.find_element(By.ID, "mat-input-1").send_keys("numbers12345")
time.sleep(3)

# Inject the interception script BEFORE the captcha loads
intercept_script = """
const i = setInterval(()=>{
    if (window.turnstile) {
        clearInterval(i)
        window.turnstile.render = (a,b) => {
            let p = {
                type: "TurnstileTaskProxyless",
                websiteKey: b.sitekey,
                websiteURL: window.location.href,
                data: b.cData,
                pagedata: b.chlPageData,
                action: b.action,
                userAgent: navigator.userAgent
            }
            console.log("CAPTCHA_PARAMS:", JSON.stringify(p))
            window.tsCallback = b.callback
            return 'foo'
        }
    }
}, 10)
"""
driver.execute_script(intercept_script)

# Click the button that triggers the captcha (adapt the selector to the current version of the site)
driver.find_element(By.XPATH, "//button[@type='submit']").click()
time.sleep(5)

# Extract parameters from the console (in a real script, it is better to intercept them via performance logs)
# For example, let's assume we already know them or have extracted them
sitekey = "0x4AAAAAAAAjq6WYeRDKmebM"
user_agent = driver.get_user_agent()

my_key = "YOUR_2CAPTCHA_KEY"

# Send the task for solving
data = {
    "key": my_key,
    "method": "turnstile",
    "sitekey": sitekey,
    "json": 1,
    "pageurl": url,
    "useragent": user_agent,
    # Add data, pagedata, action here if they are required for your version of the site
}

response = requests.post("https://api.2captcha.com/in.php", data=data)
print("Sent to the service:", response.text)
s = response.json()["request"]

# Wait for the result
time.sleep(15)
while True:
    solu = requests.get(f"https://api.2captcha.com/res.php?key={my_key}&action=get&json=1&id={s}").json()
    if solu["request"] == "CAPCHA_NOT_READY":
        print(solu["request"])
        time.sleep(10)
    elif "ERROR" in solu["request"]:
        print(solu["request"])
        driver.quit()
        exit(0)
    else:
        break

token = solu["request"]
print("Token received:", token)

# Inject the token using the callback function that we saved in window.tsCallback
driver.execute_script(f"window.tsCallback('{token}');")

# Additionally, remove the disabled attribute from the button if it is still locked
try:
    el = driver.find_element(By.XPATH, "//button[@type='submit']")
    driver.execute_script("arguments[0].removeAttribute('disabled')", el)
except:
    pass

time.sleep(2)

# Check the injection
resp = driver.execute_script("return turnstile.getResponse();")
print("turnstile.getResponse() response:", resp)
print("Response injected")

time.sleep(10)
driver.quit()

Solution accuracy reports

If the site rejected the received token, be sure to report it via the reportIncorrect method. This helps the service improve its algorithms and refunds your funds for the failed attempt. If the token successfully passed the verification, send reportCorrect.

You can submit a report via the API:

python Copy
import requests

API_KEY = "YOUR_API_KEY"
TASK_ID = "123456789"

# Report for an incorrect solution
requests.post(
    "https://api.2captcha.com/reportIncorrect",
    json={"clientKey": API_KEY, "taskId": TASK_ID}
)

# Report for a correct solution
requests.post(
    "https://api.2captcha.com/reportCorrect",
    json={"clientKey": API_KEY, "taskId": TASK_ID}
)

Detailed guide on submitting reports with examples for different languages: https://2captcha.com/h/how-to-submit-reports

Common errors and solutions

Error / Problem Cause Solution
ERROR_CAPTCHA_UNSOLVABLE Dynamic parameters (data, pagedata, action) or User-Agent are missing or incorrect. Use the interception script to get the current parameters. Make sure that the userAgent in the task exactly matches the browser's userAgent.
Token received, but the site does not let you in Incorrect token injection or expired parameter lifetime. Use the window.tsCallback('token') call immediately after receiving the solution. Do not refresh the page between intercepting parameters and sending the request.
Error finding elements The site has updated the layout or protection mechanisms. Check the current XPath or CSS selectors via DevTools. Increase the wait time or use explicit waits (WebDriverWait).
ERROR_WRONG_USER_KEY Invalid API key. Check that you are using the correct key from your 2Captcha dashboard.