#!/usr/bin/env python3 """ No-API Vulnerability Hunter Discovers vulnerable websites using only free/open data sources Pipeline: Target Discovery → Tech Detection → CVE Scanning → Exploitation """ import subprocess, json, sys, os, time from pathlib import Path from collections import Counter from urllib.parse import urlparse ANALYZER_DIR = os.path.expanduser("~/the-analyzer") REPORTS_DIR = f"{ANALYZER_DIR}/reports" TARGETS_DIR = f"{ANALYZER_DIR}/targets" WORK_DIR = "/tmp/analyzer-hunter" HTTPX = os.path.expanduser("~/go/bin/httpx") NUCLEI_TEMPLATES = os.path.expanduser("~/nuclei-templates") os.makedirs(WORK_DIR, exist_ok=True) os.makedirs(REPORTS_DIR, exist_ok=True) os.makedirs(TARGETS_DIR, exist_ok=True) def log(msg): print(f"\033[94m[*]\033[0m {msg}") def ok(msg): print(f"\033[92m[✓]\033[0m {msg}") def warn(msg): print(f"\033[93m[!]\033[0m {msg}") def bad(msg): print(f"\033[91m[✗]\033[0m {msg}") def run(cmd, timeout=120): """Run a shell command and return output""" try: r = subprocess.run(cmd, shell=True, capture_output=True, text=True, timeout=timeout) return r.stdout, r.stderr, r.returncode except subprocess.TimeoutExpired: return "", "TIMEOUT", 124 # ============================================================ # PHASE 1: TARGET DISCOVERY # ============================================================ def discover_tranco_deep(count=2000, offset=50000): """Get mid-tier sites from Tranco (50K-52K range)""" log(f"Fetching Tranco sites (offset={offset}, count={count})...") # Download if not cached csv_file = f"{WORK_DIR}/top-1m.csv" if not os.path.exists(csv_file): out, _, _ = run("curl -skL https://tranco-list.eu/top-1m.csv.zip -o /tmp/t1m.zip && unzip -o /tmp/t1m.zip -d /tmp/tranco/ 2>/dev/null && echo OK", timeout=30) if "OK" in out: os.system("cp /tmp/tranco/top-1m.csv " + csv_file) if os.path.exists(csv_file): targets = [] with open(csv_file) as f: for i, line in enumerate(f): if i < offset: continue if i >= offset + count: break parts = line.strip().split(",") if len(parts) >= 2: targets.append(parts[1].strip()) outfile = f"{WORK_DIR}/tranco_deep_targets.txt" with open(outfile, "w") as f: f.write("\n".join(targets)) ok(f"{len(targets)} targets from Tranco (offset {offset})") return outfile def discover_bug_bounty_targets(): """Add known bug bounty targets for subdomain recon""" targets = [ # High-value targets that often have bug bounty programs "hackerone.com", "bugcrowd.com", "intigriti.com", "yeswehack.com", "synack.com", "cobalt.io", # Major platforms with bounty programs "facebook.com", "twitter.com", "instagram.com", "linkedin.com", "github.com", "gitlab.com", "atlassian.com", "slack.com", "shopify.com", "stripe.com", "square.com", "paypal.com", "discord.com", "reddit.com", "twitch.com", "spotify.com", "cloudflare.com", "digitalocean.com", "heroku.com", # E-commerce (user's niche) "magento.com", "shopware.com", "woocommerce.com", "bigcommerce.com", "salesforce.com", "hubspot.com", # Google "google.com", "youtube.com", "gmail.com", "android.com", # Microsoft "microsoft.com", "office.com", "azure.com", "live.com", # Apple "apple.com", "icloud.com", ] # Also add targets from the Analyzer's list analyzer_targets = f"{TARGETS_DIR}/top50.txt" if os.path.exists(analyzer_targets): with open(analyzer_targets) as f: targets.extend([l.strip() for l in f if l.strip()]) outfile = f"{WORK_DIR}/bb_targets.txt" with open(outfile, "w") as f: f.write("\n".join(sorted(set(targets)))) return outfile def discover_commoncrawl_vuln_patterns(): """Search CommonCrawl for URLs matching vulnerable software patterns""" log("Searching CommonCrawl for vulnerable software patterns...") CC_INDEX = "CC-MAIN-2026-21" BASE = f"http://index.commoncrawl.org/{CC_INDEX}-index" patterns = [ ("phpMyAdmin", "phpmyadmin"), ("WordPress admin", "wp-admin"), ("WordPress plugins", "wp-content/plugins"), ("Jenkins", "jenkins"), ("phpinfo()", "phpinfo.php"), ("Server status", "server-status"), (".env files", ".env"), ("Actuator/Spring", "actuator"), ("Git exposure", ".git/config"), ("Laravel debug", "laravel/debug"), ] targets = set() for name, pattern in patterns: try: import urllib.request, urllib.parse, json url = f"{BASE}?url=*.{pattern}/*&output=json&limit=20" r = urllib.request.urlopen(url, timeout=10) data = r.read().decode() for line in data.strip().split("\n"): if not line.strip(): continue try: d = json.loads(line) u = d.get("url", "") if u: parsed = urlparse(u) domain = parsed.netloc or parsed.path.split("/")[0] targets.add(domain) except: pass except Exception as e: pass if targets: outfile = f"{WORK_DIR}/commoncrawl_targets.txt" with open(outfile, "w") as f: f.write("\n".join(sorted(targets))) ok(f"{len(targets)} targets from CommonCrawl patterns") return outfile else: warn("CommonCrawl returned no targets (API limiting)") return None # ============================================================ # PHASE 2: SUBDOMAIN ENUMERATION # ============================================================ def enumerate_subdomains_shodan(domain): """Use free Shodan DNS API for subdomain discovery""" try: import urllib.request, json key = "8IPLCrASad9cLHqo6xwzGNxOPcldnGDG" url = f"https://api.shodan.io/dns/domain/{domain}?key={key}" r = urllib.request.urlopen(url, timeout=10) data = json.loads(r.read().decode()) subdomains = data.get("subdomains", []) full_domains = [f"{sd}.{domain}" for sd in subdomains] if full_domains: ok(f"Found {len(full_domains)} subdomains for {domain}") return full_domains return [] except Exception as e: warn(f"Shodan DNS for {domain}: {e}") return [] # ============================================================ # PHASE 3: TECH DETECTION # ============================================================ def tech_detect(targets_file, output_file=None): """Run httpx tech detection on targets""" log(f"Tech detection on targets from {targets_file}...") if not output_file: output_file = f"{WORK_DIR}/tech_detected.json" stdout, stderr, rc = run( f"cat {targets_file} | {HTTPX} -rl 30 -timeout 5 -tech-detect -j -o {output_file} -silent 2>/dev/null", timeout=180 ) count = 0 if os.path.exists(output_file): with open(output_file) as f: count = sum(1 for _ in f) return output_file, count def tech_summary(tech_file): """Summarize detected technologies""" techs = Counter() targets = [] with open(tech_file) as f: for line in f: try: d = json.loads(line) url = d.get("url", "?") targets.append(url) for t in d.get("tech", []): techs[t] += 1 except: pass return targets, techs # ============================================================ # PHASE 4: TARGETED CVE SCANNING # ============================================================ def scan_with_nuclei(targets_file, severity="critical,high", output_file=None): """Run nuclei CVE scan on targets""" log(f"Nuclei CVE scan (severity: {severity})...") if not output_file: output_file = f"{REPORTS_DIR}/hunt_scan_{int(time.time())}.json" stdout, stderr, rc = run( f"nuclei -l {targets_file} -j -rl 15 -c 8 " f"-t {NUCLEI_TEMPLATES}/http/cves/ " f"-severity {severity} " f"-o {output_file} " f"-silent 2>/dev/null", timeout=300 ) count = 0 if os.path.exists(output_file): with open(output_file) as f: count = sum(1 for _ in f) return output_file, count def tech_targeted_scan(tech_file): """Run technology-specific CVE scans based on detected tech""" log("Running tech-targeted CVE scans...") # Read tech data tech_targets = {} with open(tech_file) as f: for line in f: try: d = json.loads(line) url = d.get("url", "") for t in d.get("tech", []): tech_targets.setdefault(t, []).append(url) except: pass # Map tech to relevant CVE template tags tech_cve_map = { "WordPress": ["wordpress", "cves"], "Apache HTTP Server": ["apache", "cves"], "Nginx": ["nginx", "cves"], "PHP": ["php", "cves"], "Jenkins": ["jenkins", "cves"], "jQuery": [], "Bootstrap": [], "MySQL": ["mysql", "cves"], "OpenSSL": ["openssl", "cves"], "OpenSSH": ["openssh", "cves"], "phpMyAdmin": ["phpmyadmin", "cves"], "Tomcat": ["tomcat", "cves"], "Drupal": ["drupal", "cves"], "Joomla": ["joomla", "cves"], "GitLab": ["gitlab", "cves"], "Jenkins": ["jenkins", "cves"], } all_findings = [] for tech, urls in tech_targets.items(): # Check if this tech has known CVE templates matched_tech = None for known_tech, tags in tech_cve_map.items(): if known_tech.lower() in tech.lower() or tech.lower() in known_tech.lower(): matched_tech = known_tech break if not matched_tech: continue # Write targets for this tech tech_file = f"{WORK_DIR}/tech_{tech.lower().replace(' ', '_')}.txt" with open(tech_file, "w") as f: f.write("\n".join(urls[:20])) # Run focused scan tech_output = f"{WORK_DIR}/scan_{tech.lower().replace(' ', '_')}.json" stdout, stderr, rc = run( f"nuclei -l {tech_file} -j -rl 10 -c 5 " f"-t {NUCLEI_TEMPLATES}/http/cves/ " f"-severity critical,high,medium " f"-o {tech_output} -silent 2>/dev/null", timeout=120 ) if os.path.exists(tech_output): with open(tech_output) as f: findings = [l for l in f if l.strip()] if findings: all_findings.extend(findings) ok(f"{tech}: {len(findings)} findings") # Merge all findings merged_file = f"{REPORTS_DIR}/tech_targeted_{int(time.time())}.json" with open(merged_file, "w") as f: f.write("\n".join(all_findings)) return merged_file, len(all_findings) # ============================================================ # PHASE 5: REPORT # ============================================================ def generate_report(findings_file, tech_summary_data, targets_count): """Generate a comprehensive report""" report_file = f"{REPORTS_DIR}/hunter_report_{int(time.time())}.md" findings = [] cves = Counter() severities = Counter() target_urls = set() if os.path.exists(findings_file): with open(findings_file) as f: for line in f: if not line.strip(): continue try: d = json.loads(line) sev = d.get("info", {}).get("severity", "unknown") severities[sev] += 1 # Extract CVE cve_list = [] for ref in d.get("info", {}).get("classification", {}).get("cve", []): cve_list.append(ref.get("id", "")) cve_id = cve_list[0] if cve_list else "N/A" for c in cve_list: cves[c] += 1 findings.append({ "url": d.get("matched-at", d.get("host", "?")), "cve": cve_id, "severity": sev, "name": d.get("info", {}).get("name", "?"), "extracted": d.get("extracted-results", []), }) target_urls.add(d.get("matched-at", d.get("host", "?"))) except: pass # Build report icons = {"critical": "🔴", "high": "🟠", "medium": "🟡", "low": "🔵", "unknown": "⚪"} lines = [] lines.append(f"# Mass Vulnerability Hunter Report") lines.append(f"Generated: {time.strftime('%Y-%m-%d %H:%M')}") lines.append(f"") lines.append(f"## Summary") lines.append(f"| Metric | Value |") lines.append(f"|--------|-------|") lines.append(f"| Targets Probed | {targets_count} |") if tech_summary_data: _, techs = tech_summary_data lines.append(f"| Live Hosts | {len(tech_summary_data[0])} |") lines.append(f"| Technologies Detected | {len(techs)} |") lines.append(f"| Total Findings | {len(findings)} |") lines.append(f"| Unique Vulnerable Hosts | {len(target_urls)} |") for sev in ["critical", "high", "medium", "low"]: if severities[sev]: lines.append(f"| {icons.get(sev, '?')} {sev.capitalize()} | {severities[sev]} |") if cves: lines.append(f"") lines.append(f"## CVEs Detected") for cve, cnt in cves.most_common(30): lines.append(f"- [{cve}](https://nvd.nist.gov/vuln/detail/{cve}): {cnt} occurrences") if findings: lines.append(f"") lines.append(f"## All Findings") for f_data in findings: icon = icons.get(f_data["severity"], "?") lines.append(f"- {icon} [{f_data['cve']}] {f_data['name']} @ {f_data['url']}") if f_data["extracted"]: for ex in f_data["extracted"][:3]: lines.append(f" - `{ex}`") if tech_summary_data: _, techs = tech_summary_data if techs: lines.append(f"") lines.append(f"## Detected Technologies") for tech, cnt in techs.most_common(20): lines.append(f"- {tech}: {cnt} targets") with open(report_file, "w") as f: f.write("\n".join(lines)) return report_file, findings # ============================================================ # MAIN # ============================================================ def main(): mode = sys.argv[1] if len(sys.argv) > 1 else "auto" print() print("╔═══════════════════════════════════════════╗") print("║ NO-API VULNERABILITY HUNTER ║") print("╚═══════════════════════════════════════════╝") print() merged_targets = f"{WORK_DIR}/merged_targets.txt" if mode == "target" and len(sys.argv) >= 3: # Hunt a specific target domain domain = sys.argv[2] log(f"Hunting target domain: {domain}") # Phase 1: Subdomain enumeration log("[1/4] Subdomain enumeration...") subdomains = enumerate_subdomains_shodan(domain) if subdomains: ok(f"Discovered {len(subdomains)} subdomains for {domain}") with open(merged_targets, "w") as f: f.write("\n".join(subdomains)) else: warn(f"No subdomains found via Shodan. Using root domain.") with open(merged_targets, "w") as f: f.write(domain) elif mode == "deep": # Deep scan - target mid-tier Tranco sites log("[1/4] Target discovery (Tranco deep)...") targets_file = discover_tranco_deep(count=2000, offset=50000) if not targets_file: bad("Failed to get target list") return # Also try CommonCrawl cc_file = discover_commoncrawl_vuln_patterns() # Merge targets all_targets = [] with open(targets_file) as f: all_targets.extend([l.strip() for l in f if l.strip()]) if cc_file and os.path.exists(cc_file): with open(cc_file) as f: all_targets.extend([l.strip() for l in f if l.strip()]) with open(merged_targets, "w") as f: f.write("\n".join(sorted(set(all_targets)))) ok(f"{len(set(all_targets))} unique targets") else: # Auto mode - balanced approach log("[1/4] Target discovery...") # Get targets from multiple sources tranco_file = discover_tranco_deep(count=500, offset=50000) # Merge all_targets = [] if tranco_file and os.path.exists(tranco_file): with open(tranco_file) as f: all_targets.extend([l.strip() for l in f if l.strip()]) with open(merged_targets, "w") as f: f.write("\n".join(sorted(set(all_targets)))) ok(f"{len(set(all_targets))} unique targets") # Phase 2: Tech Detection targets_count = 0 if os.path.exists(merged_targets): with open(merged_targets) as f: targets_count = sum(1 for _ in f) if targets_count == 0: bad("No targets to scan") return print() log(f"[2/4] Tech detection on {targets_count} targets...") tech_file, live_count = tech_detect(merged_targets) if live_count == 0: warn(f"No live hosts found. Try different target range.") return ok(f"{live_count} live hosts detected") # Show tech summary targets, techs = tech_summary(tech_file) print(f"\nTop technologies:") for tech, cnt in techs.most_common(15): print(f" {tech:35} {cnt} targets") # Phase 3: CVE Scanning print() log("[3/4] CVE scanning...") # Run mass CVE scan (critical/high) mass_output, mass_count = scan_with_nuclei(merged_targets, "critical,high") ok(f"Mass CVE scan: {mass_count} findings") # Run tech-targeted scans (medium too, since we know the tech) tech_output, tech_count = tech_targeted_scan(tech_file) ok(f"Tech-targeted scan: {tech_count} findings") # Merge findings merged_findings = f"{REPORTS_DIR}/hunt_all_findings.json" all_findings = [] for f in [mass_output, tech_output]: if os.path.exists(f): with open(f) as fh: all_findings.extend([l for l in fh if l.strip()]) with open(merged_findings, "w") as f: f.write("\n".join(all_findings)) # Phase 4: Report print() log("[4/4] Generating report...") report_file, findings = generate_report(merged_findings, (targets, techs), targets_count) print() print("=" * 60) print(f" ✅ HUNT COMPLETE") print(f" Targets scanned: {targets_count}") print(f" Live hosts: {live_count}") print(f" Total findings: {len(findings)}") print(f" Report: {report_file}") print("=" * 60) # Output findings summary if findings: print("\nTop CVEs found:") cves = Counter(f["cve"] for f in findings) for cve, cnt in cves.most_common(10): print(f" {cve}: {cnt} occurrences") return report_file if __name__ == "__main__": main()