mirror of
https://github.com/mukul975/Anthropic-Cybersecurity-Skills.git
synced 2026-06-16 16:03:17 +03:00
27c6414ca5
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
169 lines
6.4 KiB
Python
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()
|