Files
th-analyzer/engine/hunter.py
drjones d2bc52905d The Analyzer v2.0 — 30 attack vectors, 9 new exploiters
New vectors added:
- 22: SSRF Proof — cloud metadata exfiltration (CRITICAL)
- 23: Prototype Pollution — Node.js client/server (HIGH)
- 24: WebSocket Hijack — WS origin bypass + injection (HIGH)
- 25: Mass Assignment — protected field modification (HIGH)
- 26: HTTP Parameter Pollution — WAF bypass (HIGH)
- 27: Insecure Deserialization — PHP/Java/Node (CRITICAL)
- 28: OAuth Takeover — redirect_uri / state / CSRF (CRITICAL)
- 29: Web Cache Poisoning — unkeyed header injection (HIGH)
- 30: CRLF Injection — HTTP response splitting (CRITICAL)

All vectors PROVE exploitation by dumping data/credentials,
not just detecting config issues.
2026-06-21 07:20:21 -07:00

562 lines
20 KiB
Python

#!/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()