Skip to content

Commit f3b0a6d

Browse files
committed
ja3 browser fingerprint double check to bypass 403
1 parent fd9274d commit f3b0a6d

2 files changed

Lines changed: 106 additions & 15 deletions

File tree

Cargo.toml

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "cloud_fade"
3-
version = "0.1.4"
3+
version = "0.1.5"
44
authors = ["boring <boringthegod@tutanota.com>"]
55
edition = "2021"
66
description = "Unmask real IP address of a domain hidden behind Cloudflare by IPs bruteforcing"
@@ -18,7 +18,9 @@ futures = "0.3"
1818
indicatif = "0.16"
1919
num_cpus = "1.13"
2020
rand = "0.8"
21-
reqwest = { version = "0.11", features = ["json", "gzip", "brotli", "deflate", "stream", "native-tls", "rustls-tls"] }
21+
reqwest = { version = "0.11", default-features = false, features = ["json", "gzip", "brotli", "deflate", "stream", "rustls-tls"] }
2222
tokio = { version = "1", features = ["macros", "rt-multi-thread", "time"] }
2323
colored = "2.0"
2424
scraper = "0.14"
25+
rquest = "0.25"
26+
flate2 = "1.0"

src/main.rs

Lines changed: 102 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ use reqwest::header::HeaderMap;
88
use std::collections::HashMap;
99
use std::fs::File;
1010
use std::io::{BufRead, BufReader};
11+
use std::io::Read;
1112
use std::net::{Ipv4Addr, SocketAddr};
1213
use std::process::Command;
1314
use std::str::FromStr;
@@ -18,6 +19,8 @@ use tokio::sync::Semaphore;
1819
use tokio::time::timeout;
1920
use colored::*;
2021
use scraper::{Html, Selector};
22+
use rquest::tls::Impersonate;
23+
use flate2::read::GzDecoder;
2124

2225
#[tokio::main]
2326
async fn main() {
@@ -325,6 +328,7 @@ async fn test_ip(
325328
None
326329
}
327330

331+
328332
async fn is_domain_valid(domain: &str, timeout_duration: Duration) -> bool {
329333
let client = reqwest::Client::builder()
330334
.danger_accept_invalid_certs(true)
@@ -338,17 +342,58 @@ async fn is_domain_valid(domain: &str, timeout_duration: Duration) -> bool {
338342
if let Ok(Ok(resp)) = response {
339343
let status = resp.status();
340344
if status == 200 {
341-
true
345+
return true;
346+
} else if status == 403 {
347+
return retry_with_rquest(domain).await;
342348
} else {
343349
eprintln!("Domain {} returned status code {}", domain, status);
344-
false
350+
return false;
345351
}
346352
} else {
347353
eprintln!("Domain {} is unresponsive", domain);
348-
false
354+
return false;
349355
}
350356
}
351357

358+
359+
async fn retry_with_rquest(domain: &str) -> bool {
360+
let client2 = match rquest::Client::builder()
361+
.impersonate(Impersonate::Chrome129)
362+
.build()
363+
{
364+
Ok(client) => client,
365+
Err(e) => {
366+
eprintln!("Erreur lors de la création du client: {}", e);
367+
return false;
368+
}
369+
};
370+
371+
let url = format!("https://{}", domain);
372+
373+
let request_builder = client2.get(&url);
374+
let request = match request_builder.build() {
375+
Ok(req) => req,
376+
Err(e) => {
377+
eprintln!("Erreur lors de la construction de la requête: {}", e);
378+
return false;
379+
}
380+
};
381+
382+
let resp = match client2.execute(request).await {
383+
Ok(response) => response,
384+
Err(e) => {
385+
eprintln!("Erreur lors de l'exécution de la requête: {}", e);
386+
return false;
387+
}
388+
};
389+
390+
let status = resp.status();
391+
392+
status.is_success()
393+
}
394+
395+
396+
352397
async fn get_target_pattern(domain: &str, timeout_duration: Duration) -> Option<String> {
353398
let client = reqwest::Client::builder()
354399
.danger_accept_invalid_certs(true)
@@ -359,22 +404,68 @@ async fn get_target_pattern(domain: &str, timeout_duration: Duration) -> Option<
359404
let request = client.get(&url);
360405

361406
let response = timeout(timeout_duration, request.send()).await;
407+
362408
if let Ok(Ok(resp)) = response {
363-
if resp.status() != 200 {
364-
eprintln!("Domain {} returned status code {}", domain, resp.status());
365-
return None;
409+
if resp.status() == 200 {
410+
if let Ok(text) = resp.text().await {
411+
if let Some(title) = extract_title(&text) {
412+
return Some(title);
413+
}
414+
}
415+
} else if resp.status() == 403 {
416+
return match fetch_title_with_rquest(domain).await {
417+
Ok(title) => Some(title),
418+
Err(e) => {
419+
eprintln!("Failed to fetch title via rquest: {}", e);
420+
None
421+
}
422+
};
366423
}
367-
if let Ok(text) = resp.text().await {
368-
if let Some(title) = extract_title(&text) {
369-
return Some(title);
424+
}
425+
426+
eprintln!("Domain {} is unresponsive or returned an error.", domain);
427+
None
428+
}
429+
430+
async fn fetch_title_with_rquest(domain: &str) -> Result<String, Box<dyn std::error::Error>> {
431+
let client = rquest::Client::builder()
432+
.impersonate(Impersonate::Chrome129)
433+
.build()?;
434+
435+
let url = format!("https://{}/", domain);
436+
let resp = client.get(&url).send().await?;
437+
438+
let status = resp.status();
439+
if status == 200 {
440+
let body = if let Some(encoding) = resp.headers().get("Content-Encoding") {
441+
if encoding == "gzip" {
442+
let body = resp.bytes().await?;
443+
let mut decoder = GzDecoder::new(&body[..]);
444+
let mut decompressed_body = String::new();
445+
decoder.read_to_string(&mut decompressed_body)?;
446+
decompressed_body
447+
} else {
448+
resp.text().await?
370449
}
450+
} else {
451+
resp.text().await?
452+
};
453+
454+
let document = Html::parse_document(&body);
455+
let selector = Selector::parse("title").unwrap();
456+
457+
if let Some(title_element) = document.select(&selector).next() {
458+
let title = title_element.inner_html();
459+
return Ok(title);
460+
} else {
461+
return Err("No <title> found.".into());
371462
}
372463
} else {
373-
eprintln!("Domain {} is unresponsive", domain);
464+
return Err(format!("Request failed with status: {}", status).into());
374465
}
375-
None
376466
}
377467

468+
378469
fn extract_title(html: &str) -> Option<String> {
379470
let document = Html::parse_document(html);
380471
let selector = Selector::parse("title").unwrap();
@@ -399,12 +490,10 @@ fn read_lines(filename: &str) -> Vec<String> {
399490
fn parse_ip_range(ip_range: &str) -> Result<Vec<String>, String> {
400491
let parts: Vec<&str> = ip_range.split('-').collect();
401492
if parts.len() == 1 {
402-
// Traiter comme une seule adresse IP
403493
let ip = parts[0];
404494
let ip_addr = Ipv4Addr::from_str(ip).map_err(|_| "Adresse IP invalide.".to_string())?;
405495
Ok(vec![ip_addr.to_string()])
406496
} else if parts.len() == 2 {
407-
// Traiter comme une plage d'adresses IP
408497
let start_ip =
409498
Ipv4Addr::from_str(parts[0]).map_err(|_| "Adresse IP de début invalide.".to_string())?;
410499
let end_ip =

0 commit comments

Comments
 (0)