cfsolve: Cloudflare Turnstile en Challenge Page Solver in Rust

Turnstile-widgets kunnen worden opgelost met behulp van de paginauRL, de sitekey en optioneel de HTML van de pagina (in de meeste gevallen is dit laatste niet nodig). Challenge-pagina's vereisen een directe fetch of de HTML van de pagina.

Browserprofielen

cfsolve imiteert een specifieke browser, die wordt beschreven in een profielbestand. Er is één profiel inbegrepen: profiles/safari-26-ios.json (Safari op iPhone).

Elk profiel verwijst naar geregistreerde reacties op de browserchecks van de challenge, die als JSON in een directory worden opgeslagen. Bewaar elk profiel samen met de bijbehorende directory bij het kopiëren. Het produceren van deze bestanden vereist handmatige inspanning (of AI), maar dit kan worden vergemakkelijkt door challenges op te lossen in een echte webbrowser met de proxy ingesteld op het adres van een draaiende capture-proxy. Deze proxy registreert de gedecodeerde payloads die naar Cloudflare worden verzonden. Momenteel worden alleen Safari- en Chrome(ium)-profieltypes ondersteund.

Bouwen

Om de tool te bouwen, heb je de Rust-versie nodig die is vastgelegd in rust-toolchain.toml, plus CMake, een C/C++ compiler, libclang en Git (om BoringSSL te patchen en te bouwen).

Gebruik het volgende commando: cargo build --release --bin challenge

Command Line Tool

Gebruik

./target/release/challenge --page 'https://example.com/login' /tmp/solve --solve --fingerprint profiles/safari-26-ios.json

Bij succes wordt het token (of cf_clearance=...) geprint en wordt de voortgang naar stderr verzonden. Het directory-argument bevat alle verzoeken en reacties voor debugging; deze directory moet dus leeg zijn of nog niet bestaan.

Standaard lost de tool de eerste Turnstile-widget op de pagina op, of ruimt de challenge-pagina op. Om een specifieke widget te kiezen wanneer de pagina er meerdere heeft, geef je de sitekey op, inclusief de actie en cData indien de site deze gebruikt. Bij het gebruik van --sitekey zal de tool falen op een challenge-pagina.

./target/release/challenge --page 'https://example.com/login' /tmp/solve --solve --fingerprint profiles/safari-26-ios.json --sitekey 'SITEKEY' --action 'login' --cdata 'CUSTOM_DATA'

Als je alleen een sitekey hebt, voeg dan --blank-page toe en stel --page in op de werkelijke URL van de site.

Opties

OptieBeschrijving
--fingerprint <file>Browserprofielbestand dat moet worden gebruikt. Verplicht.
--page <url>De pagina met de widget, of de challenge-pagina.
--solveLos de widget of challenge op. Zonder dit stopt cfsolve na het verzenden van de eerste payload van de widget.
--sitekey <key>Selecteer een widget via de sitekey.
--action <value>, --cdata <value>Overschrijf de actie en cData van de widget. Vereist --sitekey.
--blank-pageSla het laden van de pagina over. Render de widget op een blanco pagina op de paginauRL. Vereist --solve en --sitekey.
--proxy <url>Stuur elk verzoek via een http://, https://, socks5://, of socks5h:// proxy. Voeg inloggegevens toe aan de URL, bijv. socks5h://user:password@host:1080.
-h, --helpToon help.

Bibliotheek

Voeg cfsolve toe als een path-dependency en kopieer de [patch] sectie, aangezien Cargo patches alleen leest uit de top-level Cargo.toml:

[dependencies]
cfsolve = { path = "/path/to/cfsolve", default-features = false }
tokio = { version = "1", features = ["macros", "rt-multi-thread"] }

[patch.crates-io]
btls-sys = { path = "/path/to/cfsolve/vendor/btls-sys" }
wreq = { path = "/path/to/cfsolve/vendor/wreq" }
http2 = { path = "/path/to/cfsolve/vendor/http2" }

default-features = false schakelt de interne tools uit, zodat alleen de solver overblijft. Gebruik de Rust-versie in rust-toolchain.toml of een nieuwere versie.

Voorbeeldgebruik

use cfsolve::{Browser, Options, PageSource, Turnstile};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let browser = Browser::read("/path/to/cfsolve/profiles/safari-26-ios.json")?;
    let options = Options::default();

    let turnstile = Turnstile {
        url: "https://example.com/login".parse()?,
        page: PageSource::Navigate,
        widget: None,
    };

    let token = browser.solve_turnstile(turnstile, &options).await?;
    println!("{token}");

    let cleared = browser.clear(&"https://example.com/".parse()?, &options).await?;
    println!("cf_clearance={}", cleared.clearance);
    println!("{} cookies; protected page: {}", cleared.cookies.len(), cleared.page.url);

    Ok(())
}

Laad een Browser één keer en deel deze. Gelijktijdige operaties krijgen elk hun eigen verbindingen en cookies. Let op: Verstuur de cf_clearance-cookie vanaf hetzelfde IP-adres en met dezelfde user agent als het profiel, anders zal Cloudflare deze weigeren.

Turnstile met een sitekey of eigen HTML

PageSource::Navigate haalt de pagina op. Bij het gebruik van alleen een URL en sitekey, gebruik dan PageSource::Blank en specificeer de widget:

use cfsolve::{PageSource, Turnstile, Widget};

let turnstile = Turnstile {
    url: "https://example.com/login".parse()?,
    page: PageSource::Blank,
    widget: Some(Widget {
        sitekey: "SITEKEY".into(),
        action: Some("login".into()),
        cdata: None,
    }),
};

let token = browser.solve_turnstile(turnstile, &options).await?;

Stel action en cdata in op de waarden die de site gebruikt, of op None wanneer de site deze niet instelt. Je kunt ook een Widget doorgeven met PageSource::Navigate om één widget uit meerdere te kiezen of om er een te leveren die de site via JavaScript toevoegt.

Voor een pagina die je al hebt geladen (bijvoorbeeld achter een login), geef je de URL en HTML door:

PageSource::Html {
    html: std::fs::read_to_string("account.html")?,
    referrer_policy: None, // Of de Referrer-Policy header van de response.
}

De widget draait in een cross-site iframe, dus je hebt je login-cookies niet nodig. Met Html en widget: None wordt de eerste widget op de pagina opgelost, terwijl Blank altijd een Widget vereist en deze rendert op een blanco pagina op de URL van de site.

Challenge-pagina's

browser.clear(&url, &options) haalt de pagina op en lost de challenge op. De geretourneerde Clearance bevat de cf_clearance-cookie, alle cookies voor de pagina en de beschermde pagina. De operatie faalt met Error::NotChallenged als er geen challenge aanwezig is.

Als je de challenge-pagina zelf hebt opgehaald, geef dan de response door als een FetchedPage aan browser.clear_fetched(page, &options). Gebruik hetzelfde IP en dezelfde user agent als het profiel.

Als je niet weet of een URL een widget of een challenge-pagina toont, gebruik dan browser.solve, wat beide gevallen afdekt:

use cfsolve::Outcome;

match browser.solve(&url, &options).await? {
    Outcome::Token(token) => println!("{token}"),
    Outcome::Cleared(cleared) => println!("cf_clearance={}", cleared.clearance),
}

Opties

Stel per operatie een proxy in via Options (HTTP, HTTPS of SOCKS5):

let options = cfsolve::Options {
    proxy: Some("socks5h://127.0.0.1:1080".into()),
    ..Default::default()
};

Bibliotheekoperaties zijn standaard stil. Voor voortgangsberichten en debug-bestanden moet je Sink implementeren en options.sink instellen op Some(Arc::new(your_sink)).

Fouten

FoutOorzaakOpnieuw proberen?
Error::NetworkVerbinding mislukt.Ja
Error::RejectedCloudflare heeft de oplossing geweigerd.Ja
Error::BlockedDe site retourneerde een foutpagina in plaats van een challenge.Nee
Error::ChallengedJe vroeg om een Turnstile-token maar kreeg een challenge-pagina.Ja, na het oplossen ervan
Error::NotChallengedJe vroeg om een challenge-clearance maar er was geen challenge.Nee
Error::ConfigOngeldig profiel of ongeldige opties.Nee
Error::UnsupportedElke andere fout, zoals een nieuwe challenge-build of een pagina zonder widget.Nee

Licentie

MIT, met uitzondering van de gepatchte dependencies in vendor/, die hun eigen licenties behouden.