Was this helpful?
How to solve CaptchaFox on auth.gmx.net
Technical engineer
auth.gmx.net is the GMX mail login page. The captcha does not always appear — the server decides after the first step.
The captcha here is CaptchaFox, and it is harder to work with than on the sign-up page. There are three differences.
First, the login page has two site keys instead of one. One is for the checkbox variant, the other for the slider variant. Which one is used is decided by the server for each session, so you have to read the key from the page rather than write it into the code in advance.
Second, GMX loads the captcha from its own address, s.uicdn.com, not from CaptchaFox's own server. Because of that you have to pass the apiServer parameter in the task. Without it the token comes back in the ordinary format, while the site needs a token with the MAM_ prefix.
Third, there is no form on this page that you submit yourself. The page passes the token to a callback function and then sends it with its own request. So writing the token into a hidden field does nothing here.
Task parameters
The task type is CaptchaFoxTask.
| Parameter | Type | Required | Description |
|---|---|---|---|
| type | String | Yes | Must be set to CaptchaFoxTask |
| websiteURL | String | Yes | Login page address: https://auth.gmx.net/login |
| websiteKey | String | Yes | Site key. The login page has two, and the right one is read from the page |
| apiServer | String | Yes for GMX | Address of the captcha build: https://s.uicdn.com/mampkg/@mamdev/core.frontend.libs.captchafox/. The field is formally optional, but without it the token comes back in the wrong format |
| userAgent | String | Yes | Browser User-Agent. Has to match the real one |
| proxyType | String | Yes | Proxy type: http, socks4 or socks5 |
| proxyAddress | String | Yes | Proxy IP address or hostname |
| proxyPort | Number | Yes | Proxy port |
| proxyLogin | String | No | Login for proxy authentication |
| proxyPassword | String | No | Password for proxy authentication |
How to find the parameters
Both site keys sit in the page itself, in a script tag with the id view-properties. You do not have to open the developer tools — it is enough to look at the page source and find the captchaSiteKeys line:
json
{
"captchaSiteKeys": {
"captchaFox": {
"oneClick": "sk_MkbSHwQwyv1NxVi2fogWFiHu2hx6q",
"slide": "sk_uVvZFK06t1rgOKEXgJafrEXI4f9e4"
},
"recaptcha": "6LeKN00rAAAAAI_EfKCvVDrPPyXihxkRmB6dIhKZ"
}
}
The oneClick key belongs to the checkbox variant, the slide key to the slider variant. Inside the page these variants are named ONE_CLICK and SLIDE. The third key, recaptcha, has nothing to do with CaptchaFox: GMX also has a reCAPTCHA fallback, but that is a separate task and a separate request type.
You cannot choose one of the two keys in advance — which one you need becomes clear only after the first login step. It is more reliable to take the key from the page itself at the moment it draws the captcha. The Playwright example shows how.
The captcha build address is visible on the Network tab. Filter the requests by captchafox and see where api.js is loaded from. On GMX it is https://s.uicdn.com/mampkg/@mamdev/core.frontend.libs.captchafox/api.js — the path up to the file name is the value of apiServer. The same address is used on the GMX sign-up page and on mail.com.
Take the User-Agent you open the site with. In a script it is easier to read it straight from the browser rather than typing it in by hand.
Code examples
Python + requests
Sending the task and polling for the result with no extra libraries. The example uses the checkbox variant key; if your session showed the slider, put in the second key.
python
import time
import requests
API_KEY = "YOUR_API_KEY"
WEBSITE_KEY = "sk_MkbSHwQwyv1NxVi2fogWFiHu2hx6q"
API_SERVER = "https://s.uicdn.com/mampkg/@mamdev/core.frontend.libs.captchafox/"
USER_AGENT = ("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 "
"(KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36")
task = {
"type": "CaptchaFoxTask",
"websiteURL": "https://auth.gmx.net/login",
"websiteKey": WEBSITE_KEY,
"apiServer": API_SERVER,
"userAgent": USER_AGENT,
"proxyType": "http",
"proxyAddress": "1.2.3.4",
"proxyPort": 8080,
"proxyLogin": "user23",
"proxyPassword": "p4$w0rd",
}
created = requests.post(
"https://api.2captcha.com/createTask",
json={"clientKey": API_KEY, "task": task},
timeout=30,
).json()
if created.get("errorId") != 0:
raise RuntimeError(f"Task was not created: {created}")
task_id = created["taskId"]
print("Task created:", task_id)
for _ in range(24):
time.sleep(5)
result = requests.post(
"https://api.2captcha.com/getTaskResult",
json={"clientKey": API_KEY, "taskId": task_id},
timeout=30,
).json()
if result.get("status") == "ready":
token = result["solution"]["token"]
print("Token received:", token[:20], "…")
break
if result.get("errorId") != 0:
raise RuntimeError(f"Error: {result}")
else:
raise RuntimeError("No solution arrived within the time limit")
Check the start of the token: it has to begin with MAM_. If the prefix is missing, the task went out without apiServer or with a wrong value.
Python + Playwright
The script opens the login page, enters the email address, waits for the captcha step, reads the site key from the page, sends the task and passes the token back to the page.
The main thing here is a small script that runs in the browser before the page loads. It replaces the captchafox.render function with its own: the new one remembers the widget settings and hands the work over to the real function. These settings hold both the site key and the function the token has to be given to afterwards.
python
import os
from playwright.sync_api import sync_playwright
from twocaptcha import TwoCaptcha
API_KEY = os.getenv("APIKEY_2CAPTCHA", "YOUR_API_KEY")
TARGET_URL = "https://auth.gmx.net/login"
API_SERVER = "https://s.uicdn.com/mampkg/@mamdev/core.frontend.libs.captchafox/"
EMAIL = "YOUR_ADDRESS@gmx.net"
PROXY_BROWSER = {"server": "http://1.2.3.4:8080", "username": "user23", "password": "p4$w0rd"}
PROXY_API = {"type": "HTTP", "uri": "user23:p4$w0rd@1.2.3.4:8080"}
INTERCEPT = """
window.__cfOptions = null;
let stored = undefined;
Object.defineProperty(window, 'captchafox', {
configurable: true,
get() { return stored; },
set(value) {
const render = value.render;
value.render = (container, options) => {
window.__cfOptions = options;
return render(container, options);
};
stored = value;
},
});
"""
solver = TwoCaptcha(API_KEY)
with sync_playwright() as p:
browser = p.chromium.launch(headless=False, proxy=PROXY_BROWSER)
page = browser.new_page()
page.add_init_script(INTERCEPT)
try:
page.goto(TARGET_URL)
page.wait_for_load_state("networkidle")
page.fill('input[name="username"]', EMAIL)
page.click("#button-next")
page.wait_for_function("window.__cfOptions !== null", timeout=30000)
website_key = page.evaluate("window.__cfOptions.sitekey")
user_agent = page.evaluate("navigator.userAgent")
print("Site key:", website_key)
result = solver.captchafox(
sitekey=website_key,
pageurl=TARGET_URL,
userAgent=user_agent,
proxy=PROXY_API,
api_server=API_SERVER,
)
token = result["code"]
print("Token received:", token[:20], "…")
page.evaluate("""(token) => {
if (!window.__cfOptions || typeof window.__cfOptions.onVerify !== 'function') {
throw new Error('The captcha callback function was not found');
}
window.__cfOptions.onVerify(token);
}""", token)
print("Token passed to the page, the password step comes next")
except Exception as e:
print(f"Error: {e}")
finally:
browser.close()
If the page skipped the captcha and went straight to the password field, the wait will time out. That is not a script error: the server decided the check was not needed.
How to use the received token (Callback function)
On the GMX sign-up page it is enough to put the token into a hidden field and submit the form. On the login page that does not work.
The CaptchaFox widget creates a hidden field here too — a textarea named cf-captcha-response inside a cf-widget-ID block. But the login page does not read it and does not submit any form. Instead it passes an onVerify callback function to the widget, and when the captcha is solved the widget calls that function with the token. The page then builds the request itself:
json
{
"captcha": {
"response": "MAM_...",
"siteKey": "sk_MkbSHwQwyv1NxVi2fogWFiHu2hx6q"
},
"sessionId": "login session id"
}
and sends it by POST to the captchafox-verification address of its service.
Two conclusions from this. Writing the token into the hidden field is pointless — nobody reads it. And building this request yourself is not needed either: it is simpler to call the same function the page has already prepared and let it do the rest. That is exactly what the Playwright example does.
In the response to its request the page gets a flowState field. SUCCESS means the check passed, FAILED means the login failed, ONGOING means one more step is needed. The type of the next step comes in the nextStep field. It may be the password, or another captcha: the site may show a second check in a row, including reCAPTCHA instead of CaptchaFox.
Common errors and solutions
| Error / Problem | Cause | Solution |
|---|---|---|
| The site answers with HTTP 424 | The token came in the standard format, without the MAM_ prefix | Pass apiServer with the value https://s.uicdn.com/mampkg/@mamdev/core.frontend.libs.captchafox/ |
| The token does not start with MAM_ | apiServer was not passed or has a wrong value | Check the value against the address the page loads the captcha script from |
| The token is accepted by the service, but the login does not go through | The wrong variant's key was used: the page has two | Take the key from the widget settings at the moment it is drawn, not from the code |
| The captcha is solved, but nothing happens on the page | The token was written into the hidden field, which the page does not read | Pass the token to the onVerify callback function |
| The wait for the captcha times out | The server decided the check was not needed and went straight to the password field | Handle both cases: with a captcha and without one |
| The task goes out without a proxy | An empty dictionary or None was passed in proxy | The SDK drops such values silently. Pass a filled dictionary: a proxy is required for CaptchaFox |
| After a solved captcha the page shows another one | The response returned flowState ONGOING and a new step in nextStep | Solve again for the new key. If reCAPTCHA came, use the reCAPTCHA method |
| The script did not read the widget settings | It ran after the page had already loaded | Run it through add_init_script, before navigating to the page |
Useful links
- CaptchaFox API documentation: https://2captcha.com/api-docs/captchafox
- How to bypass CaptchaFox: https://2captcha.com/p/captchafox-solver
- CaptchaFox on register.gmx.net: https://2captcha.com/h/how-to-solve-captchafox-captcha-on-registergmxnet
- CaptchaFox on signup.mail.com: https://2captcha.com/h/how-to-solve-captchafox-captcha-on-signupmailcom
- Solving reCAPTCHA v2: https://2captcha.com/api-docs/recaptcha-v2
- Using proxies: https://2captcha.com/api-docs/proxy
- Python SDK on GitHub: https://github.com/2captcha/2captcha-python
- More captcha bypass examples for specific sites: https://2captcha.com/h?category=sites