Files
mukul975 27c6414ca5 Add folder anatomy (scripts/agent.py + references/api-reference.md) for 648 cybersecurity skills
Complete skill folder anatomy across all cybersecurity skills:
- scripts/agent.py: 80-150 line Python agents using real libraries (impacket,
  boto3, azure-mgmt-*, kubernetes, pefile, yara, scapy, shodan, stix2, etc.)
- references/api-reference.md: real API documentation with method signatures
- LICENSE: MIT license for all skill folders
2026-03-10 21:02:12 +01:00

169 lines
6.4 KiB
Python

#!/usr/bin/env python3
"""Agent for subdomain enumeration using subfinder and httpx.
Runs ProjectDiscovery subfinder for passive subdomain discovery,
validates live hosts with httpx, resolves DNS, and generates
an attack surface report.
"""
import subprocess
import json
import sys
from datetime import datetime
from pathlib import Path
from collections import defaultdict
class SubdomainEnumerationAgent:
"""Enumerates subdomains using subfinder and validates with httpx."""
def __init__(self, domain, output_dir="./recon"):
self.domain = domain
self.output_dir = Path(output_dir)
self.output_dir.mkdir(parents=True, exist_ok=True)
self.subdomains = []
self.live_hosts = []
def run_subfinder(self, all_sources=False, recursive=False):
"""Run subfinder for passive subdomain enumeration."""
out_file = self.output_dir / f"{self.domain}_subdomains.txt"
cmd = ["subfinder", "-d", self.domain, "-o", str(out_file), "-silent"]
if all_sources:
cmd.append("-all")
if recursive:
cmd.append("-recursive")
try:
result = subprocess.run(cmd, capture_output=True, text=True, timeout=300)
if out_file.exists():
self.subdomains = [
line.strip() for line in out_file.read_text().splitlines()
if line.strip()
]
return {"count": len(self.subdomains),
"output_file": str(out_file),
"stderr": result.stderr[:200] if result.stderr else ""}
except FileNotFoundError:
return {"error": "subfinder not installed. Install: go install -v "
"github.com/projectdiscovery/subfinder/v2/cmd/subfinder@latest"}
except subprocess.TimeoutExpired:
return {"error": "subfinder timed out after 300s"}
def run_subfinder_json(self):
"""Run subfinder with JSON output for source tracking."""
cmd = ["subfinder", "-d", self.domain, "-oJ", "-silent"]
try:
result = subprocess.run(cmd, capture_output=True, text=True, timeout=300)
entries = []
for line in result.stdout.strip().splitlines():
try:
entry = json.loads(line)
entries.append(entry)
host = entry.get("host", "")
if host and host not in self.subdomains:
self.subdomains.append(host)
except json.JSONDecodeError:
continue
return entries
except (FileNotFoundError, subprocess.TimeoutExpired):
return []
def validate_with_httpx(self, ports="80,443"):
"""Validate discovered subdomains with httpx probe."""
if not self.subdomains:
return []
input_file = self.output_dir / f"{self.domain}_to_probe.txt"
input_file.write_text("\n".join(self.subdomains))
out_file = self.output_dir / f"{self.domain}_live.json"
cmd = ["httpx", "-l", str(input_file), "-ports", ports,
"-status-code", "-title", "-json", "-o", str(out_file), "-silent"]
try:
subprocess.run(cmd, capture_output=True, text=True, timeout=600)
if out_file.exists():
for line in out_file.read_text().splitlines():
try:
self.live_hosts.append(json.loads(line))
except json.JSONDecodeError:
continue
return self.live_hosts
except (FileNotFoundError, subprocess.TimeoutExpired):
return []
def resolve_dns(self):
"""Resolve subdomains to IP addresses using dnsx."""
if not self.subdomains:
return []
input_file = self.output_dir / f"{self.domain}_to_resolve.txt"
input_file.write_text("\n".join(self.subdomains))
cmd = ["dnsx", "-l", str(input_file), "-a", "-resp", "-json", "-silent"]
try:
result = subprocess.run(cmd, capture_output=True, text=True, timeout=300)
records = []
for line in result.stdout.strip().splitlines():
try:
records.append(json.loads(line))
except json.JSONDecodeError:
continue
return records
except (FileNotFoundError, subprocess.TimeoutExpired):
return []
def detect_takeover_candidates(self):
"""Identify subdomains potentially vulnerable to takeover."""
candidates = []
cloud_cnames = ["amazonaws.com", "azurewebsites.net", "cloudfront.net",
"herokuapp.com", "github.io", "s3.amazonaws.com",
"blob.core.windows.net", "cloudapp.azure.com"]
dns_records = self.resolve_dns()
for record in dns_records:
cname = record.get("cname", "")
if any(cloud in cname for cloud in cloud_cnames):
candidates.append({
"subdomain": record.get("host", ""),
"cname": cname,
"risk": "Potential subdomain takeover"
})
return candidates
def generate_report(self):
"""Generate attack surface enumeration report."""
status_dist = defaultdict(int)
for host in self.live_hosts:
code = host.get("status_code", 0)
status_dist[str(code)] += 1
report = {
"domain": self.domain,
"report_date": datetime.utcnow().isoformat(),
"total_subdomains": len(self.subdomains),
"live_hosts": len(self.live_hosts),
"status_distribution": dict(status_dist),
"subdomains": self.subdomains[:100],
"live_host_details": self.live_hosts[:50],
"takeover_candidates": self.detect_takeover_candidates(),
}
report_path = self.output_dir / f"{self.domain}_report.json"
with open(report_path, "w") as f:
json.dump(report, f, indent=2)
print(json.dumps(report, indent=2))
return report
def main():
if len(sys.argv) < 2:
print("Usage: agent.py <domain> [output_dir]")
sys.exit(1)
domain = sys.argv[1]
output_dir = sys.argv[2] if len(sys.argv) > 2 else "./recon"
agent = SubdomainEnumerationAgent(domain, output_dir)
agent.run_subfinder(all_sources=True)
agent.validate_with_httpx()
agent.generate_report()
if __name__ == "__main__":
main()