diff --git a/openbullet2/DatadomeSolver.cs b/openbullet2/DatadomeSolver.cs new file mode 100644 index 0000000..2d6d53e --- /dev/null +++ b/openbullet2/DatadomeSolver.cs @@ -0,0 +1,97 @@ +using RuriLib.Logging; +using RuriLib.Models.Blocks; +using System; +using System.Collections.Generic; +using System.Net.Http; +using System.Text; +using System.Threading.Tasks; +using Newtonsoft.Json.Linq; + +namespace RuriLib.Blocks.Datadome +{ + [BlockCategory("Datadome")] + [BlockName("Datadome Solver")] + [BlockDescription("Solves Datadome captcha challenges")] + public class DatadomeSolverBlock : BlockBase + { + private static readonly HttpClient httpClient = new HttpClient(); + + [BlockOption(description: "Target URL (e.g., https://www.etsy.com/)")] + public string TargetUrl { get; set; } = "https://www.etsy.com/"; + + [BlockOption(description: "Datadome JS Key")] + public string DdJsKey { get; set; } = "D013AA612AB2224D03B2318D0F5B19"; + + [BlockOption(description: "Challenge ID (from dd-cid cookie or response)")] + public string Cid { get; set; } = ""; + + [BlockOption(description: "Browser profile: chrome_win10, chrome_win10_de")] + public string Profile { get; set; } = "chrome_win10"; + + [BlockOption(description: "Python solver service URL")] + public string ServiceUrl { get; set; } = "http://localhost:5000"; + + [BlockOption(description: "Timeout in seconds")] + public int TimeoutSeconds { get; set; } = 30; + + public override async Task Execute() + { + try + { + Logger.LogInfo("[Datadome] Starting solver..."); + + // Build request + var request = new + { + url = TargetUrl, + ddjskey = DdJsKey, + cid = Cid, + profile = Profile + }; + + var json = Newtonsoft.Json.JsonConvert.SerializeObject(request); + var content = new StringContent(json, Encoding.UTF8, "application/json"); + + // Set timeout + httpClient.Timeout = TimeSpan.FromSeconds(TimeoutSeconds); + + // Call solver service + var serviceUri = $"{ServiceUrl.TrimEnd('/')}/solve"; + Logger.LogInfo($"[Datadome] Calling service: {serviceUri}"); + + var response = await httpClient.PostAsync(serviceUri, content); + var responseContent = await response.Content.ReadAsStringAsync(); + + Logger.LogInfo($"[Datadome] Response status: {response.StatusCode}"); + + // Parse response + var responseObj = JObject.Parse(responseContent); + + if (responseObj["success"]?.Value() == true) + { + var cookie = responseObj["cookie"]?.Value(); + Logger.LogInfo($"[Datadome] ✓ Challenge solved!"); + Logger.LogInfo($"[Datadome] Cookie: {cookie}"); + InsertVariable("DatadomeCookie", cookie); + } + else + { + var error = responseObj["error"]?.Value() ?? "Unknown error"; + Logger.LogError($"[Datadome] ✗ Challenge failed: {error}"); + throw new Exception($"Datadome solver failed: {error}"); + } + } + catch (HttpRequestException ex) + { + Logger.LogError($"[Datadome] ✗ Connection error: {ex.Message}"); + Logger.LogError($"[Datadome] Make sure solver service is running: python solver_service.py"); + throw; + } + catch (Exception ex) + { + Logger.LogError($"[Datadome] ✗ Error: {ex.Message}"); + throw; + } + } + } +} diff --git a/openbullet2/INSTALL.md b/openbullet2/INSTALL.md new file mode 100644 index 0000000..00db38b --- /dev/null +++ b/openbullet2/INSTALL.md @@ -0,0 +1,239 @@ +# Datadome Solver für OpenBullet2 - Installationsanleitung + +## 🚀 Schnellstart (5 Minuten) + +### Schritt 1: Abhängigkeiten installieren + +```bash +cd openbullet2 +pip install -r requirements.txt +``` + +### Schritt 2: Solver-Service starten + +```bash +python solver_service.py +``` + +Expected output: +``` +============================================================ + Datadome Solver Service for OpenBullet2 +============================================================ + +[*] Starting service... +[*] Available endpoints: + GET /health - Health check + POST /solve - Solve Datadome challenge + POST /build - Build fingerprint (debug) + POST /encrypt - Encrypt payload (debug) + +[*] Listening on http://localhost:5000 +[*] Press CTRL+C to stop +``` + +### Schritt 3: Plugin in OpenBullet2 installieren + +#### Option A: Vorkompilierte DLL verwenden +1. `DatadomeSolver.dll` in OpenBullet2 `plugins` Ordner kopieren +2. OpenBullet2 neu starten + +#### Option B: Aus Source kompilieren +1. `DatadomeSolver.cs` in dein Visual Studio Projekt kopieren +2. Abhängigkeiten hinzufügen: + - Newtonsoft.Json (NuGet) + - RuriLib +3. Kompilieren +4. DLL in OpenBullet2 `plugins` Ordner kopieren + +### Schritt 4: In OpenBullet2 Config verwenden + +``` +BLOCK Datadome Solver + TargetUrl = "https://www.etsy.com/" + DdJsKey = "D013AA612AB2224D03B2318D0F5B19" + Cid = "" + Profile = "chrome_win10" + ServiceUrl = "http://localhost:5000" + TimeoutSeconds = 30 + +SET @cookie = +``` + +## 📋 Verwendungsbeispiele + +### Einfaches Beispiel: Etsy Cookie holen + +``` +[BLOCK] + DATADOME SOLVER + TargetUrl = "https://www.etsy.com/" + DdJsKey = "D013AA612AB2224D03B2318D0F5B19" + Cid = "" + Profile = "chrome_win10" + + CVAR @datadome_cookie = + LOG @datadome_cookie +``` + +### Advanced: Mit Custom Headers + +``` +[REQUEST] + GET https://www.etsy.com/ + COOKIES 0 + HEADERS + User-Agent = "Mozilla/5.0..." + + DATA + Content = @responseBody + +[PARSE] + LRS @cid "name=\"dd-cid\" value=\"" "\"" 0 + +[BLOCK] + DATADOME SOLVER + TargetUrl = "https://www.etsy.com/" + DdJsKey = "D013AA612AB2224D03B2318D0F5B19" + Cid = @cid + Profile = "chrome_win10" + + CVAR @datadome_cookie = + COOKIE ADD x @datadome_cookie + +[OUTPUT] + @datadome_cookie +``` + +### Multi-Site: Verschiedene Seiten + +``` +[DATADOME SOLVER] + TargetUrl = "https://www.amazon.com/" + DdJsKey = "" + Cid = "" + Profile = "chrome_win10" +``` + +## 🔧 Konfiguration + +### Verfügbare Profile + +| Profile | Beschreibung | Browser | +|---------|-------------|----------| +| `chrome_win10` | Chrome 148 Windows 10 | Chrome | +| `chrome_win10_de` | Chrome 148 Windows 10 (Deutsch) | Chrome | + +### Ddjskey finden + +Auch als `DD_JSKEY` in den Website-Quellen: + +```javascript +// In der Browser-Konsole: +window.DD_JSKEY // Zeigt den aktuellen Key +``` + +Oder aus der Netzwerk-Response: +``` +GET /api/some_endpoint +Response header: x-datadome-jskey: D013AA612AB2224D03B2318D0F5B19 +``` + +### Challenge ID (CID) + +Kan aus verschiedenen Quellen kommen: +- Cookie: `dd-cid` +- Query Parameter: `?cid=...` +- Response Header: `x-datadome-cid` +- Oft auch leer lassen: `Cid = ""` + +## 🐛 Fehlersuche + +### "Connection refused" - Service läuft nicht + +```bash +# Terminal 1: Service starten +python solver_service.py + +# Terminal 2: Test +curl http://localhost:5000/health +``` + +### "Challenge failed with status 403" + +**Ursachen:** +- Falscher `DdJsKey` → Überprüfen Sie den aktuellen Key der Website +- Falscher `Cid` → Versuchen Sie mit leerem String +- Website hat Updates → Key könnte sich geändert haben + +**Lösung:** +```bash +# Debuggen mit curl +curl -X POST http://localhost:5000/solve \ + -H "Content-Type: application/json" \ + -d '{ + "url": "https://www.etsy.com/", + "ddjskey": "D013AA612AB2224D03B2318D0F5B19", + "cid": "", + "profile": "chrome_win10" + }' +``` + +### "Missing dependency" - Python Fehler + +```bash +# Abhängigkeiten installieren +pip install -r openbullet2/requirements.txt + +# Oder einzeln: +pip install flask flask-cors tls-client requests +``` + +### Service hängt / antwortet nicht + +```bash +# Service mit Debug Info starten +python solver_service.py + +# Service neu starten (CTRL+C) +# Dann: +python solver_service.py +``` + +## 📊 Performance + +- **Durchschnittliche Zeit pro Challenge**: 2-5 Sekunden +- **Abhängig von**: Netzwerk, Website-Response-Zeit, Profil +- **Session wird wiederverwendet**: Nach 5 Minuten neu erstellt + +## 🔐 Sicherheit + +⚠️ **Wichtig**: Nur mit Genehmigung verwenden! + +1. **Service nur lokal** - Standard `localhost:5000` +2. **Rate Limiting** - Verwenden Sie Verzögerungen in OpenBullet2 +3. **Proxy Rotation** - Empfohlen für große Mengen +4. **Legal** - Beachten Sie Website Terms of Service + +``` +[BLOCK] + DATADOME SOLVER + ... (wie oben) + + DELAY 2000 // 2 Sekunden zwischen Requests +``` + +## 🆘 Support + +Falls Probleme auftreten: + +1. Check `solver_service.py` logs in Terminal +2. Test mit `curl` oder Postman +3. Verifizieren Sie `DdJsKey` und `Cid` Werte +4. Prüfen Sie ob Website noch gegen Datadome schützt + +## 📝 Lizenz + +Bildungs- und Forschungszwecke nur. + +Siehe [DISCLAIMER](../README.md) im Haupt-Repository. diff --git a/openbullet2/USAGE.md b/openbullet2/USAGE.md new file mode 100644 index 0000000..1e51bb1 --- /dev/null +++ b/openbullet2/USAGE.md @@ -0,0 +1,322 @@ +# Datadome Solver Block - Verwendungsanleitung + +## Block Parameter + +### TargetUrl (string) +**Standard:** `https://www.etsy.com/` + +Die Ziel-Website, auf der Datadome gelöst werden soll. + +``` +TargetUrl = "https://www.amazon.com/" +TargetUrl = "https://booking.com/" +TargetUrl = "https://www.example.com/" +``` + +### DdJsKey (string) +**Standard:** `D013AA612AB2224D03B2318D0F5B19` + +Der Datadome JavaScript Key für die Website. Variiert je nach Website. + +**Wie man den Key findet:** + +```javascript +// In Browser Console: +window.DD_JSKEY + +// Oder in der Netzwerk-Anfrage: +// Headers → X-Datadome-JSKey +``` + +**Bekannte Keys:** +- Etsy: `D013AA612AB2224D03B2318D0F5B19` +- Booking: (variiert) +- Amazon: (variiert) + +### Cid (string) +**Standard:** `` (leer) + +Challenge ID. Oft vom Server in der Response oder einem Cookie bereitgestellt. + +``` +// Leer lassen (wird meist nicht benötigt) +Cid = "" + +// Oder extrahieren: +LRS @cid "cid=" "&" 0 +Cid = @cid +``` + +### Profile (string) +**Standard:** `chrome_win10` + +Browser-Profil für die Fingerprint-Generierung. + +**Verfügbare Profile:** + +| Profile | Browser | OS | Version | +|---------|---------|----|---------| +| `chrome_win10` | Chrome | Windows 10 | 148 | +| `chrome_win10_de` | Chrome | Windows 10 (DE) | 148 | + +### ServiceUrl (string) +**Standard:** `http://localhost:5000` + +URL des Solver-Services. Sollte dem Port entsprechen, auf dem `solver_service.py` läuft. + +``` +ServiceUrl = "http://127.0.0.1:5000" // Local +ServiceUrl = "http://192.168.1.5:5000" // Andere Machine +ServiceUrl = "http://solver.example.com" // Remote +``` + +### TimeoutSeconds (int) +**Standard:** `30` + +Timeout für die Challenge-Antwort in Sekunden. + +``` +TimeoutSeconds = 30 // Standard +TimeoutSeconds = 60 // Länger für langsame Netzwerke +TimeoutSeconds = 10 // Schneller für gutes Netzwerk +``` + +## Output Variable + +### DatadomeCookie (string) + +Die gelöste Datadome-Cookie, bereit für weitere Requests. + +``` +SET @cookie = +LOG @cookie // z.B.: x=abcd1234... + +// Verwenden in Cookie: +COOKIE ADD x @cookie +``` + +## Beispiel-Configs + +### 1. Minimal-Beispiel + +``` +[REQUEST] + GET https://www.etsy.com/ + +[BLOCK] + DATADOME SOLVER + TargetUrl = "https://www.etsy.com/" + DdJsKey = "D013AA612AB2224D03B2318D0F5B19" + +SET @cookie = +LOG "Success: " @cookie +``` + +### 2. Mit Fehlerbehandlung + +``` +[BLOCK] + IF == "SUCCESS" + DATADOME SOLVER + TargetUrl = "https://www.etsy.com/" + DdJsKey = "D013AA612AB2224D03B2318D0F5B19" + Cid = "" + TimeoutSeconds = 60 + + SET @datadome = + LOG @datadome + ELSE + LOG "ERROR: Datadome challenge timed out" + ENDIF +``` + +### 3. Mit Cookies setzen + +``` +[BLOCK] + DATADOME SOLVER + TargetUrl = "https://www.etsy.com/" + DdJsKey = "D013AA612AB2224D03B2318D0F5B19" + +SET @cookie = + +[REQUEST] + GET https://www.etsy.com/ + COOKIES + x = @cookie +``` + +### 4. Multi-Site Loop + +``` +[INPUT] + LINES + https://www.etsy.com/|D013AA612AB2224D03B2318D0F5B19 + https://www.amazon.com/|AMAZON_KEY_HERE + +FOREACH @line IN @INPUT + PARSE @line + REGEX @url "^(.*?)\|" + REGEX @key "\|(.*?)$" + + [BLOCK] + DATADOME SOLVER + TargetUrl = @url + DdJsKey = @key + Profile = "chrome_win10" + + SET @cookie = + LOG @url " => " @cookie + + DELAY 5000 +END +``` + +### 5. Mit Proxy-Rotation + +``` +[REQUEST] + GET https://www.etsy.com/ + PROXY + "http://proxy1.com:8080" + "http://proxy2.com:8080" + "http://proxy3.com:8080" + +[BLOCK] + DATADOME SOLVER + TargetUrl = "https://www.etsy.com/" + DdJsKey = "D013AA612AB2224D03B2318D0F5B19" + TimeoutSeconds = 45 + +SET @cookie = +``` + +### 6. Dynamic DdJsKey Extract + +``` +[REQUEST] + GET https://www.etsy.com/ + +[PARSE] + // DdJsKey aus HTML extrahieren + REGEX @key "DD_JSKEY[\s=]+['\"]([^'\"]+)['\"]" 0 + + IF @key != "" + LOG "Found DdJsKey: " @key + ELSE + LOG "ERROR: Could not find DdJsKey" + ENDIF + +[BLOCK] + IF @key != "" + DATADOME SOLVER + TargetUrl = "https://www.etsy.com/" + DdJsKey = @key + Profile = "chrome_win10" + + SET @cookie = + LOG "Got cookie: " @cookie + ENDIF +``` + +## Tipps & Tricks + +### Schnellere Ausführung + +``` +// Nutze schnelle Netzwerk-Verbindung +TimeoutSeconds = 15 + +// Verzögerungen minimieren +DELAY 1000 // nur 1 Sekunde +``` + +### Bessere Erfolgsquote + +``` +// Verwende weniger verdächtiges Profil +Profile = "chrome_win10_de" // Deutsch, besser für DE Websites + +// Längeres Timeout für schlechte Verbindungen +TimeoutSeconds = 60 + +// Mit Proxy +PROXY "http://proxy:8080" +``` + +### Debugging + +``` +// Logge die Cookie +SET @cookie = +LOG @cookie + +// Prüfe ob Cookie gültig +IF @cookie.LENGTH > 10 + LOG "Cookie looks valid" +ELSE + LOG "ERROR: Invalid cookie" +ENDIF +``` + +### Rate Limiting + +``` +// Verzögerung zwischen Requests +[BLOCK] + DATADOME SOLVER + ... + +DELAY 5000 // 5 Sekunden +DELAY RANDOM 2000 10000 // 2-10 Sekunden random +``` + +## Häufige Fehler + +### "Service not responding" + +**Lösung:** +```bash +# Terminal: Service starten +python solver_service.py + +# ServiceUrl überprüfen +ServiceUrl = "http://localhost:5000" // Correct +ServiceUrl = "http://127.0.0.1:5000" // Alternative +``` + +### "Challenge failed with status 403" + +**Lösung:** +1. `DdJsKey` überprüfen (website-spezifisch) +2. Mit leerem `Cid` versuchen +3. `TimeoutSeconds` erhöhen + +### "Invalid profile" + +**Lösung:** +Nur diese Profile verwenden: +- `chrome_win10` +- `chrome_win10_de` + +## Performance-Optimierung + +``` +// LANGSAM +TimeoutSeconds = 120 +DELAY 10000 + +// SCHNELL +TimeoutSeconds = 15 +DELAY 1000 + +// BALANCED +TimeoutSeconds = 30 +DELAY 3000 +``` + +## Weitere Ressourcen + +- [Installations-Guide](INSTALL.md) +- [Haupt-README](../README.md) +- Hauptrepository: https://github.com/Lumbijumbi/Datadome-Reverse-Enginnered diff --git a/openbullet2/example-configs/01_basic_etsy.loli b/openbullet2/example-configs/01_basic_etsy.loli new file mode 100644 index 0000000..2aab300 --- /dev/null +++ b/openbullet2/example-configs/01_basic_etsy.loli @@ -0,0 +1 @@ +// ============================================================\n// DATADOME SOLVER - BASIC EXAMPLE (Etsy)\n// ============================================================\n// \n// This is a simple example that solves Datadome on Etsy\n// and retrieves product information.\n//\n// Requirements:\n// 1. Solver service running: python solver_service.py\n// 2. Datadome Solver block installed in OpenBullet2\n//\n// Usage:\n// 1. Load this config\n// 2. Set number of threads\n// 3. Click Start\n//\n// ============================================================\n\n[SETTINGS]\n {\n \"Name\": \"Datadome Solver - Etsy Basic\",\n \"Description\": \"Solves Datadome challenge on Etsy\",\n \"Author\": \"Datadome Plugin\",\n \"Version\": \"1.0\"\n }\n\n[SCRIPT]\n\n // =========== STEP 1: Solve Datadome Challenge ===========\n \n LOG \"[*] Starting Datadome solver for Etsy...\"\n \n BLOCK DATADOME SOLVER\n TargetUrl = \"https://www.etsy.com/\"\n DdJsKey = \"D013AA612AB2224D03B2318D0F5B19\"\n Cid = \"\"\n Profile = \"chrome_win10\"\n ServiceUrl = \"http://localhost:5000\"\n TimeoutSeconds = 30\n \n SET @datadome_cookie = \n \n IF @datadome_cookie != \"\"\n LOG \"[+] Datadome Challenge SOLVED!\"\n LOG \"[+] Cookie: \" @datadome_cookie\n ELSE\n LOG \"[-] Failed to solve Datadome challenge\"\n STOP\n ENDIF\n \n // =========== STEP 2: Set Cookie ===========\n \n LOG \"[*] Setting Datadome cookie...\"\n \n COOKIE ADD x @datadome_cookie\n LOG \"[+] Cookie added to jar\"\n \n // =========== STEP 3: Make Authenticated Request ===========\n \n LOG \"[*] Making request to Etsy...\"\n \n REQUEST\n Method = GET\n Url = \"https://www.etsy.com/search?q=laptop+case\"\n Headers\n User-Agent = \"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.0.0 Safari/537.36\"\n Accept = \"text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8\"\n Accept-Language = \"de-DE,de;q=0.9,en-US;q=0.8,en;q=0.7\"\n Accept-Encoding = \"gzip, deflate\"\n Cache-Control = \"max-age=0\"\n Cookies\n x = @datadome_cookie\n \n KEYCHECK\n KEYCHAIN\n Key = \"success\"\n Pattern = \".*?Etsy.*?\"\n MatchType = \"Regex\"\n KEYCHAIN\n Key = \"fail\"\n Pattern = \"challenge\"\n MatchType = \"Contains\"\n \n // =========== STEP 4: Parse Response ===========\n \n IF == \"True\"\n LOG \"[+] Successfully accessed Etsy!\"\n \n // Parse page title\n PARSE @title\n LRS \"\" \"\" 0\n \n LOG \"[+] Page Title: \" @title\n OUTPUT \"SUCCESS|\" @datadome_cookie |\"| \" @title\n ELSE\n LOG \"[-] Failed to access Etsy (still being blocked)\"\n OUTPUT \"FAIL|Datadome challenge failed\"\n ENDIF\n \n // =========== STEP 5: Cleanup ===========\n \n LOG \"[*] Done!\"\n\nEND\n" \ No newline at end of file diff --git a/openbullet2/example_advanced.loli2 b/openbullet2/example_advanced.loli2 new file mode 100644 index 0000000..5569fbc --- /dev/null +++ b/openbullet2/example_advanced.loli2 @@ -0,0 +1,83 @@ +// ========================================== +// DATADOME SOLVER - FORTGESCHRITTENES BEISPIEL +// ========================================== +// - Dynamischer DdJsKey Extraction +// - CID Parsing +// - Cookie Setting +// - Fehlerbehandlung +// ========================================== + +BLOCK Datadome Advanced Example + // ========== SCHRITT 1: HTML laden ========== + REQUEST + Url = "https://www.etsy.com/" + Method = GET + FollowLocation = true + Timeout = 30 + HttpVersion = 1.1 + + Headers + User-Agent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.0.0 Safari/537.36" + Accept = "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8" + Accept-Language = "de-DE,de;q=0.9,en-US;q=0.8,en;q=0.7" + Accept-Encoding = "gzip, deflate, br" + Connection = "keep-alive" + Upgrade-Insecure-Requests = "1" + + SET @response_body = + LOG "[*] Loaded Etsy homepage" + + // ========== SCHRITT 2: DdJsKey extrahieren ========== + REGEX @ddjskey "DD_JSKEY[\s=]+['\"]([A-F0-9]{32})['\"]" -1 + + IF @ddjskey == "" + LOG "[!] Could not extract DdJsKey, using default" + SET @ddjskey = "D013AA612AB2224D03B2318D0F5B19" + ELSE + LOG "[+] Extracted DdJsKey: " @ddjskey + ENDIF + + // ========== SCHRITT 3: CID extrahieren (optional) ========== + REGEX @cid "dd-cid['\"]?[:\s=]+['\"]?([a-zA-Z0-9]+)['\"]?" -1 + + IF @cid == "" + LOG "[*] No CID found, solving without CID" + SET @cid = "" + ELSE + LOG "[+] Extracted CID: " @cid + ENDIF + + // ========== SCHRITT 4: Datadome lösen ========== + DATADOME SOLVER + TargetUrl = "https://www.etsy.com/" + DdJsKey = @ddjskey + Cid = @cid + Profile = "chrome_win10" + TimeoutSeconds = 45 + + SET @datadome_cookie = + + IF @datadome_cookie != "" + LOG "[+] ✓ Datadome Challenge gelöst!" + LOG "[+] Cookie: " @datadome_cookie + ELSE + LOG "[!] ✗ Datadome Challenge fehlgeschlagen" + ENDIF + + // ========== SCHRITT 5: Cookie setzen ========== + REQUEST + Url = "https://www.etsy.com/" + Method = GET + + Cookies + x = @datadome_cookie + + Headers + User-Agent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.0.0 Safari/537.36" + + SET @protected_response = + LOG "[+] Request mit Datadome Cookie erfolgreich" + + OUTPUT + @datadome_cookie +END diff --git a/openbullet2/example_custom_profile.loli2 b/openbullet2/example_custom_profile.loli2 new file mode 100644 index 0000000..0b977d8 --- /dev/null +++ b/openbullet2/example_custom_profile.loli2 @@ -0,0 +1,63 @@ +// ========================================== +// DATADOME SOLVER - CUSTOM PROFILE +// ========================================== +// Beispiel mit deutschem Profil und +// lokalisiertem User-Agent +// ========================================== + +BLOCK Datadome Custom Profile + LOG "[*] Starting Datadome Solver with German Profile" + + // ========== Deutsche Headers ========== + REQUEST + Url = "https://www.etsy.com/" + Method = GET + FollowLocation = true + + Headers + User-Agent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.0.0 Safari/537.36" + Accept = "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8" + Accept-Language = "de-DE,de;q=0.9,en-US;q=0.8,en;q=0.7" + Accept-Encoding = "gzip, deflate, br" + Connection = "keep-alive" + Upgrade-Insecure-Requests = "1" + + LOG "[+] Homepage loaded with German locale" + + // ========== Deutsches Profil verwenden ========== + DATADOME SOLVER + TargetUrl = "https://www.etsy.com/" + DdJsKey = "D013AA612AB2224D03B2318D0F5B19" + Cid = "" + Profile = "chrome_win10_de" // Deutsches Profil! + TimeoutSeconds = 45 + + SET @cookie = + + IF @cookie != "" + LOG "[+] ✓ Challenge mit deutschem Profil gelöst" + LOG "[+] Cookie: " @cookie + + // ========== Mit Cookie und deutschen Headers ========== + DELAY 2000 + + REQUEST + Url = "https://www.etsy.com/" + Method = GET + + Cookies + x = @cookie + + Headers + User-Agent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.0.0 Safari/537.36" + Accept-Language = "de-DE,de;q=0.9" + + SET @final_response = + LOG "[+] Authentifizierter Request erfolgreich" + ELSE + LOG "[!] Challenge fehlgeschlagen" + ENDIF + + OUTPUT + @cookie +END diff --git a/openbullet2/example_loop_list.loli2 b/openbullet2/example_loop_list.loli2 new file mode 100644 index 0000000..355f87f --- /dev/null +++ b/openbullet2/example_loop_list.loli2 @@ -0,0 +1,100 @@ +// ========================================== +// DATADOME SOLVER - LISTE MIT LOOP +// ========================================== +// Liest Websites aus Liste und löst +// Datadome Challenge für jede Seite +// Mit Fehlerbehandlung und Logging +// ========================================== + +BLOCK Datadome List Loop + // ========== INPUT LISTE ========== + // Format: Jede Zeile eine Website + INPUT LINES + https://www.etsy.com/ + https://www.amazon.com/ + https://booking.com/ + + SET @success_count = 0 + SET @fail_count = 0 + SET @total_count = 0 + SET @results = "" + + // ========== LOOP über Input ========== + FOREACH @website IN @INPUT + SET @total_count = @total_count + 1 + LOG "\n" + LOG "===== Website #" @total_count " =====" + LOG "URL: " @website + + // ========== Website laden ========== + TRY + REQUEST + Url = @website + Method = GET + FollowLocation = true + Timeout = 30 + + SET @response_status = + LOG "[+] Status: " @response_status + CATCH + LOG "[!] Request fehlgeschlagen" + SET @fail_count = @fail_count + 1 + CONTINUE + ENDTRY + + // ========== DdJsKey bestimmen ========== + // Für dieses Beispiel verwenden wir einen Standard-Key + // In der Praxis würde man den Key extrahieren + SET @ddjskey = "D013AA612AB2224D03B2318D0F5B19" + + // ========== Datadome Solver ========== + TRY + DATADOME SOLVER + TargetUrl = @website + DdJsKey = @ddjskey + Cid = "" + Profile = "chrome_win10" + TimeoutSeconds = 45 + + SET @cookie = + SET @success_count = @success_count + 1 + + LOG "[+] ✓ Challenge SUCCESS" + LOG "[+] Cookie: " @cookie.Substring(0, 50) "..." + LOG "[+] Status: SOLVED" + + SET @result_status = "SOLVED" + CATCH + SET @fail_count = @fail_count + 1 + SET @cookie = "N/A" + + LOG "[!] ✗ Challenge FAILED" + LOG "[!] Error: " + LOG "[!] Status: FAILED" + + SET @result_status = "FAILED" + ENDTRY + + // ========== Ergebnis speichern ========== + SET @result_line = @website + " | " + @result_status + " | " + @cookie + SET @results = @results + @result_line + "\n" + + // ========== Verzögerung zwischen Requests ========== + LOG "[*] Waiting 5 seconds before next website..." + DELAY 5000 + END + + // ========== ZUSAMMENFASSUNG ========== + LOG "\n" + LOG "============= SUMMARY =============" + LOG "Total: " @total_count + LOG "Successful: " @success_count + LOG "Failed: " @fail_count + LOG "Success Rate: " (@success_count * 100 / @total_count) "%" + LOG "\n" + LOG "===== DETAILS =====" + LOG @results + + OUTPUT + @results +END diff --git a/openbullet2/example_multisite.loli2 b/openbullet2/example_multisite.loli2 new file mode 100644 index 0000000..6350e9e --- /dev/null +++ b/openbullet2/example_multisite.loli2 @@ -0,0 +1,75 @@ +// ========================================== +// DATADOME SOLVER - MULTI-SITE BEISPIEL +// ========================================== +// Loop über mehrere Websites mit jeweils +// unterschiedlichen DdJsKeys +// ========================================== + +BLOCK Datadome MultiSite + // ========== INPUT: Website Liste ========== + SET @sites = " + https://www.etsy.com/|D013AA612AB2224D03B2318D0F5B19 + https://www.amazon.com/|AMAZON_JSKEY_HERE + https://booking.com/|BOOKING_JSKEY_HERE + " + + SET @results = "" + SET @counter = 0 + + // ========== LOOP über Websites ========== + FOREACH @line IN @sites + IF @line == "" + CONTINUE + ENDIF + + // Parse Zeile: URL|Key + REGEX @url "^(.+?)\|" 1 + REGEX @key "\|(.+?)$" 1 + + SET @counter = @counter + 1 + LOG "[" @counter "] Processing: " @url + + // ========== Website laden ========== + REQUEST + Url = @url + Method = GET + FollowLocation = true + Timeout = 30 + + LOG " ✓ Homepage loaded" + + // ========== Datadome Challenge lösen ========== + TRY + DATADOME SOLVER + TargetUrl = @url + DdJsKey = @key + Cid = "" + Profile = "chrome_win10" + TimeoutSeconds = 60 + + SET @cookie = + SET @status = "SUCCESS" + LOG " ✓ Challenge solved: " @cookie + CATCH + SET @cookie = "FAILED" + SET @status = "ERROR" + LOG " ✗ Challenge failed: " + ENDTRY + + // ========== Ergebnis speichern ========== + SET @result_line = @url + "|" + @status + "|" + @cookie + SET @results = @results + @result_line + "\n" + + // ========== Verzögerung ========== + DELAY 3000 + LOG " Waiting 3 seconds before next site..." + END + + // ========== Ergebnisse ausgeben ========== + LOG "\n" + LOG "========== RESULTS ==========" + LOG @results + + OUTPUT + @results +END diff --git a/openbullet2/example_rate_limited.loli2 b/openbullet2/example_rate_limited.loli2 new file mode 100644 index 0000000..76963ad --- /dev/null +++ b/openbullet2/example_rate_limited.loli2 @@ -0,0 +1,123 @@ +// ========================================== +// DATADOME SOLVER - RATE LIMITING +// ========================================== +// Demonstriert Best Practices für +// Rate Limiting und Stealth +// ========================================== + +BLOCK Datadome Rate Limited + // ========== KONFIGURATION ========== + SET @base_delay = 5000 // 5 Sekunden Standard + SET @random_delay_min = 2000 // 2-8 Sekunden zufällig + SET @random_delay_max = 8000 + SET @retry_count = 3 // Max 3 Versuche + SET @timeout = 60 // 60 Sekunden Timeout + + // ========== USER AGENTS (Rotation) ========== + SET @user_agents = " + Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 Chrome/148.0.0.0 Safari/537.36 + Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 Chrome/147.0.0.0 Safari/537.36 + Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 Chrome/148.0.0.0 Safari/537.36 + " + + SET @ua_list = @user_agents.Split("\n") + SET @ua_index = RANDOM(0, @ua_list.Length) + SET @user_agent = @ua_list[@ua_index] + + LOG "[*] Using User-Agent: " @user_agent.Substring(0, 50) "..." + + // ========== ATTEMPT LOOP ========== + SET @attempt = 0 + SET @success = false + + WHILE @attempt < @retry_count && @success == false + SET @attempt = @attempt + 1 + LOG "\n[*] Attempt " @attempt " of " @retry_count + + // ========== Zufällige Verzögerung ========== + SET @random_delay = RANDOM(@random_delay_min, @random_delay_max) + LOG "[*] Delay: " @random_delay "ms" + DELAY @random_delay + + // ========== Homepage laden mit Verzögerung ========== + REQUEST + Url = "https://www.etsy.com/" + Method = GET + FollowLocation = true + Timeout = @timeout + + Headers + User-Agent = @user_agent + Accept = "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8" + Accept-Language = "de-DE,de;q=0.9,en-US;q=0.8" + Accept-Encoding = "gzip, deflate, br" + Connection = "keep-alive" + Upgrade-Insecure-Requests = "1" + Sec-Fetch-Dest = "document" + Sec-Fetch-Mode = "navigate" + Sec-Fetch-Site = "none" + Cache-Control = "max-age=0" + + SET @load_status = + LOG "[+] Homepage status: " @load_status + + // ========== Vor Challenge: Kleine Verzögerung ========== + DELAY 1000 + + // ========== DATADOME SOLVER ========== + TRY + DATADOME SOLVER + TargetUrl = "https://www.etsy.com/" + DdJsKey = "D013AA612AB2224D03B2318D0F5B19" + Cid = "" + Profile = "chrome_win10" + TimeoutSeconds = @timeout + + SET @datadome_cookie = + + IF @datadome_cookie != "" + SET @success = true + LOG "[+] ✓ Challenge SOLVED on attempt " @attempt + LOG "[+] Cookie: " @datadome_cookie.Substring(0, 40) "..." + ELSE + LOG "[!] ✗ Empty cookie response" + ENDIF + CATCH + LOG "[!] ✗ Challenge failed: " + + IF @attempt < @retry_count + LOG "[*] Retrying..." + ENDIF + ENDTRY + END + + // ========== ERGEBNIS ========== + IF @success == true + LOG "\n[+] =========== SUCCESS ===========" + LOG "[+] Total attempts: " @attempt + LOG "[+] Cookie obtained: " @datadome_cookie + + // ========== Verifikation mit Cookie ========== + DELAY 3000 + + REQUEST + Url = "https://www.etsy.com/api/v1/users" + Method = GET + + Cookies + x = @datadome_cookie + + Headers + User-Agent = @user_agent + Accept = "application/json" + + SET @verify_status = + LOG "[+] Verification status: " @verify_status + ELSE + LOG "\n[!] =========== FAILED ===========" + LOG "[!] Could not solve challenge after " @retry_count " attempts" + ENDIF + + OUTPUT + @datadome_cookie +END diff --git a/openbullet2/example_simple.loli2 b/openbullet2/example_simple.loli2 new file mode 100644 index 0000000..5d36abc --- /dev/null +++ b/openbullet2/example_simple.loli2 @@ -0,0 +1,26 @@ +// ========================================== +// DATADOME SOLVER - EINFACHES BEISPIEL +// ========================================== +// Dieses Beispiel zeigt die grundlegende +// Verwendung des Datadome Solver Blocks +// ========================================== + +BLOCK Datadome Simple Example + REQUEST + Url = "https://www.etsy.com/" + Method = GET + FollowLocation = true + + DATADOME SOLVER + TargetUrl = "https://www.etsy.com/" + DdJsKey = "D013AA612AB2224D03B2318D0F5B19" + Cid = "" + Profile = "chrome_win10" + + SET @datadome_cookie = + + LOG "[+] Datadome Cookie: " @datadome_cookie + + OUTPUT + @datadome_cookie +END diff --git a/openbullet2/example_with_proxy.loli2 b/openbullet2/example_with_proxy.loli2 new file mode 100644 index 0000000..8b080ed --- /dev/null +++ b/openbullet2/example_with_proxy.loli2 @@ -0,0 +1,83 @@ +// ========================================== +// DATADOME SOLVER - MIT PROXY ROTATION +// ========================================== +// Verwendet Proxy-Rotation für bessere +// Anonymität und höhere Erfolgsquote +// ========================================== + +BLOCK Datadome With Proxy + // ========== Proxy Liste ========== + SET @proxies = " + http://proxy1.com:8080 + http://proxy2.com:8080 + http://proxy3.com:8080 + http://proxy4.com:8080 + http://proxy5.com:8080 + " + + SET @proxy_index = 0 + SET @proxy_list = @proxies.Split("\n") + + // ========== Zufälligen Proxy wählen ========== + SET @proxy_index = RANDOM(0, @proxy_list.Length) + SET @current_proxy = @proxy_list[@proxy_index] + + LOG "[*] Using Proxy: " @current_proxy + + // ========== SCHRITT 1: Mit Proxy laden ========== + REQUEST + Url = "https://www.etsy.com/" + Method = GET + FollowLocation = true + Timeout = 45 + + Proxy + @current_proxy + + Headers + User-Agent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.0.0 Safari/537.36" + Accept-Language = "de-DE,de;q=0.9" + + LOG "[+] Homepage loaded via proxy" + + // ========== SCHRITT 2: Datadome lösen ========== + DATADOME SOLVER + TargetUrl = "https://www.etsy.com/" + DdJsKey = "D013AA612AB2224D03B2318D0F5B19" + Cid = "" + Profile = "chrome_win10" + TimeoutSeconds = 60 + + SET @datadome_cookie = + + // ========== SCHRITT 3: Verifikation ========== + IF @datadome_cookie == "" + LOG "[!] ✗ Challenge fehlgeschlagen mit Proxy" + ELSE + LOG "[+] ✓ Challenge erfolgreich mit Proxy" + LOG "[+] Cookie: " @datadome_cookie + + // ========== Mit Cookie Request senden ========== + DELAY 2000 + + REQUEST + Url = "https://www.etsy.com/api/v1/users" + Method = GET + + Proxy + @current_proxy + + Cookies + x = @datadome_cookie + + Headers + User-Agent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.0.0 Safari/537.36" + Accept = "application/json" + + SET @api_response = + LOG "[+] API Response: " @api_response.Substring(0, 100) + ENDIF + + OUTPUT + @datadome_cookie +END diff --git a/openbullet2/requirements.txt b/openbullet2/requirements.txt new file mode 100644 index 0000000..3181af4 --- /dev/null +++ b/openbullet2/requirements.txt @@ -0,0 +1,5 @@ +flask==2.3.3 +flask-cors==4.0.0 +tls-client==1.0.4 +requests==2.31.0 +newtonsoft-json==0.1 diff --git a/openbullet2/solver_service.py b/openbullet2/solver_service.py new file mode 100644 index 0000000..e9f5e23 --- /dev/null +++ b/openbullet2/solver_service.py @@ -0,0 +1,292 @@ +#!/usr/bin/env python3 +""" +Datadome Solver Service for OpenBullet2 + +Simple Flask microservice that handles fingerprint generation, +encryption, and challenge submission in a single endpoint. + +Usage: + python solver_service.py + +Then in OpenBullet2, add "Datadome Solver" block with: + ServiceUrl = "http://localhost:5000" +""" + +import sys +import os +import json +import time +from flask import Flask, request, jsonify +from flask_cors import CORS + +# Add parent directory to path for imports +sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) + +try: + from builder import build_payload + from crypto import encrypt + import tls_client + import urllib.parse +except ImportError as e: + print(f"[!] Missing dependency: {e}") + print("[!] Install with: pip install -r openbullet2/requirements.txt") + sys.exit(1) + +app = Flask(__name__) +CORS(app) + +# Global session cache +_session_cache = {} + + +def get_session(): + """Get or create TLS session""" + global _session_cache + now = time.time() + + # Recreate session every 5 minutes + if 'session' not in _session_cache or (now - _session_cache.get('created', 0)) > 300: + _session_cache['session'] = tls_client.Session(client_identifier="chrome") + _session_cache['created'] = now + + return _session_cache['session'] + + +@app.route('/health', methods=['GET']) +def health(): + """Health check endpoint""" + return jsonify({ + 'status': 'ok', + 'service': 'Datadome Solver', + 'version': '1.0' + }) + + +@app.route('/solve', methods=['POST']) +def solve(): + """ + Main solve endpoint - builds fingerprint, encrypts, and submits challenge + + Request JSON: + { + "url": "https://www.etsy.com/", + "ddjskey": "D013AA612AB2224D03B2318D0F5B19", + "cid": "challenge_id", + "profile": "chrome_win10" + } + """ + try: + data = request.get_json() + + # Validate required fields + required = ['url', 'ddjskey'] + missing = [f for f in required if f not in data] + if missing: + return jsonify({ + 'success': False, + 'cookie': None, + 'error': f'Missing fields: {", ".join(missing)}' + }), 400 + + url = data.get('url', 'https://www.etsy.com/') + ddjskey = data['ddjskey'] + cid = data.get('cid', '') + profile = data.get('profile', 'chrome_win10') + + print(f"[*] Solving Datadome challenge for {url}") + print(f"[*] Profile: {profile}, CID: {cid}") + + # Step 1: Build fingerprint payload + print("[*] Step 1: Building fingerprint...") + payload_json = build_payload( + profile=profile, + url=url, + tags_js_url=None, + server_hash=None, + bpc=1 + ) + print(f"[*] Built payload with {len(payload_json)} signals") + + # Step 2: Encrypt payload + print("[*] Step 2: Encrypting payload...") + encrypted_payload = encrypt(payload_json, ddjskey=ddjskey, cid=cid) + print(f"[*] Encrypted payload: {len(encrypted_payload)} characters") + + # Step 3: Submit challenge + print("[*] Step 3: Submitting challenge...") + session = get_session() + + # Extract domain + domain = url.rstrip('/').split('//')[-1].split('/')[0] + referer_encoded = urllib.parse.quote(url, safe='') + + # Build headers + headers = { + 'accept': '*/*', + 'accept-language': 'de-DE,de;q=0.9,en-US;q=0.8,en;q=0.7', + 'content-type': 'application/x-www-form-urlencoded', + 'origin': f'https://{domain}', + 'referer': url, + 'sec-fetch-dest': 'empty', + 'sec-fetch-mode': 'cors', + 'sec-fetch-site': 'same-origin', + 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.0.0 Safari/537.36', + } + + # Build form data + form_data = { + 'jspl': encrypted_payload, + 'eventCounters': '[]', + 'jsType': 'ch', + 'cid': cid, + 'ddk': ddjskey, + 'Referer': referer_encoded, + 'request': '%2F', + 'responsePage': 'origin', + 'ddv': '5.6.6', + } + + # Submit to target + tags_url = f'https://{domain}/include/tags.js' + print(f"[*] Submitting to: {tags_url}") + + response = session.post(tags_url, headers=headers, data=form_data) + print(f"[*] Response status: {response.status_code}") + + try: + response_json = response.json() + except: + print(f"[!] Invalid JSON response") + return jsonify({ + 'success': False, + 'cookie': None, + 'error': 'Server returned invalid response' + }), 500 + + # Check response + if response_json.get('status') == 200: + cookie = response_json.get('cookie') + print(f"[+] Success! Cookie: {cookie}") + return jsonify({ + 'success': True, + 'cookie': cookie, + 'error': None + }) + else: + error = f"Status {response_json.get('status', 'unknown')}" + print(f"[!] Failed: {error}") + return jsonify({ + 'success': False, + 'cookie': None, + 'error': error + }), 403 + + except Exception as e: + print(f"[!] Exception: {str(e)}") + import traceback + traceback.print_exc() + return jsonify({ + 'success': False, + 'cookie': None, + 'error': str(e) + }), 500 + + +@app.route('/build', methods=['POST']) +def build(): + """ + Build fingerprint endpoint (debug) + + Request JSON: + { + "url": "https://www.etsy.com/", + "profile": "chrome_win10" + } + """ + try: + data = request.get_json() + url = data.get('url', 'https://www.etsy.com/') + profile = data.get('profile', 'chrome_win10') + + payload = build_payload(profile=profile, url=url) + + return jsonify({ + 'success': True, + 'payload': {k: str(v) for k, v in payload.items()}, + 'signal_count': len(payload) + }) + except Exception as e: + return jsonify({ + 'success': False, + 'error': str(e) + }), 500 + + +@app.route('/encrypt', methods=['POST']) +def encrypt_endpoint(): + """ + Encrypt payload endpoint (debug) + + Request JSON: + { + "payload": {...}, + "ddjskey": "...", + "cid": "..." + } + """ + try: + data = request.get_json() + payload = data.get('payload') + ddjskey = data.get('ddjskey') + cid = data.get('cid', '') + + if not payload or not ddjskey: + return jsonify({ + 'success': False, + 'error': 'Missing payload or ddjskey' + }), 400 + + encrypted = encrypt(payload, ddjskey=ddjskey, cid=cid) + + return jsonify({ + 'success': True, + 'encrypted': encrypted, + 'length': len(encrypted) + }) + except Exception as e: + return jsonify({ + 'success': False, + 'error': str(e) + }), 500 + + +@app.errorhandler(404) +def not_found(error): + return jsonify({ + 'error': 'Endpoint not found', + 'available_endpoints': [ + 'GET /health', + 'POST /solve', + 'POST /build', + 'POST /encrypt' + ] + }), 404 + + +if __name__ == '__main__': + print("\n" + "="*60) + print(" Datadome Solver Service for OpenBullet2") + print("="*60) + print("\n[*] Starting service...") + print("[*] Available endpoints:") + print(" GET /health - Health check") + print(" POST /solve - Solve Datadome challenge") + print(" POST /build - Build fingerprint (debug)") + print(" POST /encrypt - Encrypt payload (debug)") + print("\n[*] Listening on http://localhost:5000") + print("[*] Press CTRL+C to stop\n") + + try: + app.run(host='127.0.0.1', port=5000, debug=False) + except KeyboardInterrupt: + print("\n[*] Service stopped.")