Created
June 24, 2026 07:05
-
-
Save JLFN/e97645746e4563548f1ecfc71bcd5a4c to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| use anyhow::anyhow; | |
| use serde_json::json; | |
| use tracing; | |
| use winnow::{ | |
| ascii::digit1, | |
| token::take_until, | |
| Parser, | |
| }; | |
| /// Parses the initial numeric seed (`i`) from the Akamai JavaScript payload. | |
| /// Looks for `var i = <number>` and extracts the numeric value. | |
| fn parse_i(i: &mut &str) -> winnow::ModalResult<u64> { | |
| let _ = take_until(0.., "var i = ").parse_next(i)?; | |
| let _ = "var i = ".parse_next(i)?; | |
| let num = digit1.parse_next(i)?; | |
| Ok(num.parse::<u64>().unwrap()) | |
| } | |
| /// Parses the secondary numeric seed string parts from the Akamai JavaScript payload. | |
| /// Looks for `var j = i + Number("str1" + "str2");` and extracts `"str1"` and `"str2"`. | |
| fn parse_j_strings<'a>(i: &mut &'a str) -> winnow::ModalResult<(&'a str, &'a str)> { | |
| let _ = take_until(0.., "var j = i + Number(\"").parse_next(i)?; | |
| let _ = "var j = i + Number(\"".parse_next(i)?; | |
| let str1 = digit1.parse_next(i)?; | |
| let _ = "\" + \"".parse_next(i)?; | |
| let str2 = digit1.parse_next(i)?; | |
| let _ = "\");".parse_next(i)?; | |
| Ok((str1, str2)) | |
| } | |
| /// Extracts the `bm-verify` cryptographic token from the JSON payload inside the | |
| /// XMLHttpRequest send call. Looks for `"bm-verify": "<token>"`. | |
| fn parse_bm_verify<'a>(i: &mut &'a str) -> winnow::ModalResult<&'a str> { | |
| let _ = take_until(0.., "\"bm-verify\": \"").parse_next(i)?; | |
| let _ = "\"bm-verify\": \"".parse_next(i)?; | |
| take_until(0.., "\"").parse_next(i) | |
| } | |
| /// Scans the raw HTML string of an Akamai Interstitial challenge response and extracts | |
| /// the mathematical Proof-of-Work (PoW) solution and the validation token (`bm-verify`). | |
| /// | |
| /// The payload expects a calculated `pow` value, which is derived from a base seed `i` | |
| /// and a string-concatenated offset parsed as a number. | |
| /// | |
| /// # Returns | |
| /// Returns a tuple containing: | |
| /// 1. The solved PoW integer (`pow`). | |
| /// 2. The extracted `bm-verify` string token. | |
| pub fn extract_challenge_data(html: &str) -> anyhow::Result<(u64, String)> { | |
| let mut input1 = html; | |
| let i_val = parse_i(&mut input1).map_err(|e| anyhow!("Failed to parse i: {}", e))?; | |
| let mut input2 = html; | |
| let (s1, s2) = parse_j_strings(&mut input2).map_err(|e| anyhow!("Failed to parse j strings: {}", e))?; | |
| let pow_str = format!("{}{}", s1, s2); | |
| let pow_add = pow_str.parse::<u64>().unwrap_or(0); | |
| let pow = i_val + pow_add; | |
| let mut input3 = html; | |
| let bm_verify = parse_bm_verify(&mut input3).map_err(|e| anyhow!("Failed to parse bm-verify: {}", e))?; | |
| Ok((pow, bm_verify.to_string())) | |
| } | |
| /// Autonomously solves an Akamai Interstitial WAF challenge. | |
| /// | |
| /// This function parses the HTML response containing the `triggerInterstitialChallenge` | |
| /// JavaScript block, computes the mathematical Proof-of-Work, extracts the verification | |
| /// token, and automatically submits the valid solution payload to the `/_sec/verify` endpoint. | |
| /// | |
| /// Upon success, the validation cookie (`ak_bmsc` or similar) will be automatically persisted | |
| /// within the provided `primp::Client`'s cookie store, allowing subsequent requests to bypass the WAF. | |
| /// | |
| /// # Arguments | |
| /// * `client` - The `primp::Client` instance (must have `cookie_store(true)` enabled). | |
| /// * `url` - The original requested URL that triggered the challenge. | |
| /// * `response_text` - The raw HTML response body containing the challenge JavaScript. | |
| pub async fn solve_akamai_waf_async( | |
| client: &primp::Client, | |
| url: &str, | |
| response_text: &str, | |
| ) -> anyhow::Result<Vec<(String, String)>> { | |
| tracing::info!("Starting Akamai WAF challenge solving flow for {}", url); | |
| let (pow, bm_verify) = extract_challenge_data(response_text)?; | |
| let parsed_url = primp::Url::parse(url)?; | |
| let domain = parsed_url.host_str().unwrap_or_default(); | |
| let verify_url = format!("https://{}/_sec/verify?provider=interstitial", domain); | |
| let payload = json!({ | |
| "bm-verify": bm_verify, | |
| "pow": pow | |
| }); | |
| tracing::debug!("Sending Akamai challenge solution to {} with payload {:?}", verify_url, payload); | |
| let resp = client | |
| .request(primp::Method::POST, &verify_url) | |
| .json(&payload) | |
| .send() | |
| .await?; | |
| let status = resp.status().as_u16(); | |
| // Extract headers BEFORE calling .text() which moves the response | |
| let mut cookies = Vec::new(); | |
| for cookie_val in resp.headers().get_all("set-cookie") { | |
| if let Ok(cookie_str) = cookie_val.to_str() { | |
| let first_part = cookie_str.split(';').next().unwrap_or(""); | |
| let parts: Vec<&str> = first_part.splitn(2, '=').collect(); | |
| if parts.len() == 2 { | |
| cookies.push((parts[0].to_string(), parts[1].to_string())); | |
| } | |
| } | |
| } | |
| let resp_text = resp.text().await.unwrap_or_default(); | |
| tracing::debug!("Akamai WAF Verify response (status {}): {}", status, resp_text); | |
| if status != 200 { | |
| return Err(anyhow::anyhow!( | |
| "Akamai WAF Verify failed: {}", | |
| status | |
| )); | |
| } | |
| Ok(cookies) | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <!DOCTYPE html><html><head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> <meta http-equiv="refresh" content="5; URL='/s?k=B0BT221JSQ%7CB0H1TJLVC5%7CB0FYDGJ539%7CB0FN79C13N&ref=nb_sb_noss&bm-verify=AAQAAAAN_____8nifE3l59lqy8juZoaPuJwLbvTVf0fzMq_cnlPcVAzeAs-B1xtQ40FTPAt9OfRjdNFyRmY2f7khUdwK9lOQB3xjN67Jrbk2PUjMlhDhM3MLlE-rc_oP_JRxW0ZlyGt4RWf6kbHGRn_wy83eQ-gDEJeSGGVmwYRyQ86XGMd25_SYb0cyMEyyJy5eF6yLqADHLjR5HIO3hgUCsklVs2H_yWJxy4nlZ96Phagv_0T_HE5Y3p6zD-BiehuilNjpBE3EFX3uNPE1-fmr_dzINP5OypSMbFDB-ZNkG57U_Xqcf9QoqFkhtgOmRF3sXR6rV5atSR2uDlBxkT0LBa75wcmpY0ST9w'" /><title> </title><script> var i = 1781368961; var j = i + Number("8795" + "81974"); </script> </head> <noscript> <iframe style="border: none; height: 100%; width: 100%;" src=""></iframe></noscript><body> <iframe style="border: none; width: 100vw; height: 100vh;" src="https://m.media-amazon.com/images/S/sash/6Uh4bsAwUkB3vJb.gif"> </iframe> <script> function triggerInterstitialChallenge() {var xhr = new XMLHttpRequest(); xhr.withCredentials = true; xhr.addEventListener("loadend", function() { try { var data = JSON.parse(xhr.responseText); if (data.hasOwnProperty('reload')) { if (data["reload"] == true) { window.location.replace(window.location.href.replace(/[&?]bm-verify=[^#]*/, "")); if(window.location.hash){ window.location.reload(); } } } else if (data.hasOwnProperty('location')) { window.location.replace(data["location"]); } else { window.location.reload(); } } catch (e) { window.location.reload(); }}); xhr.open("POST", "/_sec/verify?provider=interstitial", false); xhr.setRequestHeader("Content-Type", "application/json"); xhr.send(JSON.stringify({"bm-verify": "AAQAAAAN/////+bJf0D6I/nIQzep+rXn62gdXY6KZuD9JEWpXfyNh0Oh8/iJ7dJO39Jz8bDriKLZ9lSB0gXp26OMtQLK4wC2wPTGwznsOwY35flxWj4rIIXNzJnBx7vhUcrhKI5d3pVSwkhEhuC0KlNDHur9uiahCFwvTEyYWAmR5sZZ0KnXVV0x88SXQaYKoEIrhf9NUnETcdxIllqMMn+VOyIq1psCFJ/r/l7RF1j1zsIge224tywbyMetMQRXeWA3LJoxZACZwBkkOBiCvoC99sAOT6UFcF2PNSf363xdJCxfN8ELzTHGwrZad96kz2YAfQ8PAo9FNqBx+kLAkQ3Fq+EdXnto9mCz6s7QyDoep/R+LR9bhstynrY=", "pow": j}));} try {if (document.getElementById("akam-logo")) {document.getElementById("akam-logo").onload = function () {requestAnimationFrame(triggerInterstitialChallenge);}; document.getElementById("akam-logo").onerror = triggerInterstitialChallenge;} else {triggerInterstitialChallenge()}} catch(e) {triggerInterstitialChallenge();}</script> </body> </html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment