mirror of
https://github.com/mukul975/Anthropic-Cybersecurity-Skills.git
synced 2026-06-12 22:24:56 +03:00
c47eed6a64
- Fix 25 shell=True subprocess calls with list-based commands - Fix 49 verify=False in defensive skills (env-var override) - Add timeout to 231 HTTP/subprocess/socket calls - Fix 6 SQL injection patterns with whitelist validation - Replace 8 __import__() with standard imports - Remove 701 unused imports across 442 files - Add authorized-testing disclaimers to all offensive skills - Complete 11 incomplete skill directories - Expand 10 stub SKILL.md files with full content - Fix 2 YAML parse errors in frontmatter - Fix 5 pre-existing syntax errors - Convert 22 hardcoded paths/ports to environment variables - Back up 21 redundant skill pairs to .bak - Fix 2 global declaration errors - 724/724 skills with full folder anatomy (SKILL.md + agent.py + api-reference.md + LICENSE) - 0 compile errors across all 724 agent.py files
163 lines
6.6 KiB
Python
163 lines
6.6 KiB
Python
#!/usr/bin/env python3
|
|
"""DNS Persistence Hunting Agent - detects DNS hijacking, dangling CNAMEs, and unauthorized zone changes."""
|
|
|
|
import json
|
|
import argparse
|
|
import logging
|
|
import requests
|
|
import subprocess
|
|
from datetime import datetime
|
|
|
|
logging.basicConfig(level=logging.INFO, format="%(asctime)s [%(levelname)s] %(message)s")
|
|
logger = logging.getLogger(__name__)
|
|
|
|
DANGLING_CNAME_SERVICES = [
|
|
"s3.amazonaws.com", "cloudfront.net", "azurewebsites.net", "herokuapp.com",
|
|
"github.io", "pantheonsite.io", "readme.io", "surge.sh",
|
|
"unbouncepages.com", "wordpress.com", "shopify.com",
|
|
]
|
|
|
|
|
|
def query_securitytrails(domain, api_key):
|
|
"""Query SecurityTrails API for passive DNS history."""
|
|
headers = {"APIKEY": api_key, "Content-Type": "application/json"}
|
|
url = f"https://api.securitytrails.com/v1/history/{domain}/dns/a"
|
|
resp = requests.get(url, headers=headers, timeout=30)
|
|
resp.raise_for_status()
|
|
return resp.json()
|
|
|
|
|
|
def get_subdomains(domain, api_key):
|
|
"""Enumerate subdomains via SecurityTrails API."""
|
|
headers = {"APIKEY": api_key}
|
|
url = f"https://api.securitytrails.com/v1/domain/{domain}/subdomains"
|
|
resp = requests.get(url, headers=headers, timeout=30)
|
|
resp.raise_for_status()
|
|
data = resp.json()
|
|
return [f"{sub}.{domain}" for sub in data.get("subdomains", [])]
|
|
|
|
|
|
def resolve_record(hostname, record_type="A"):
|
|
"""Resolve DNS record using dig command."""
|
|
cmd = ["dig", "+short", record_type, hostname]
|
|
result = subprocess.run(cmd, capture_output=True, text=True, timeout=10)
|
|
return [line.strip() for line in result.stdout.strip().split("\n") if line.strip()]
|
|
|
|
|
|
def detect_dangling_cnames(subdomains):
|
|
"""Detect CNAME records pointing to decommissioned services (subdomain takeover risk)."""
|
|
findings = []
|
|
for subdomain in subdomains:
|
|
cname_records = resolve_record(subdomain, "CNAME")
|
|
for cname in cname_records:
|
|
for service in DANGLING_CNAME_SERVICES:
|
|
if cname.rstrip(".").endswith(service):
|
|
a_records = resolve_record(cname, "A")
|
|
if not a_records:
|
|
findings.append({
|
|
"subdomain": subdomain, "cname": cname.rstrip("."),
|
|
"service": service, "resolves": False,
|
|
"severity": "critical",
|
|
"issue": "Dangling CNAME - subdomain takeover possible",
|
|
"mitre": "T1584.001",
|
|
})
|
|
return findings
|
|
|
|
|
|
def detect_wildcard_abuse(domain):
|
|
"""Check for wildcard DNS records that resolve all subdomains."""
|
|
findings = []
|
|
random_sub = f"randomtestxyz123.{domain}"
|
|
results = resolve_record(random_sub, "A")
|
|
if results:
|
|
findings.append({
|
|
"domain": domain, "wildcard_target": results,
|
|
"severity": "high",
|
|
"issue": "Wildcard DNS record detected - all subdomains resolve",
|
|
"detail": f"Random subdomain {random_sub} resolves to {results}",
|
|
})
|
|
return findings
|
|
|
|
|
|
def check_ns_delegation(domain):
|
|
"""Verify NS records haven't been modified to point to unauthorized servers."""
|
|
findings = []
|
|
ns_records = resolve_record(domain, "NS")
|
|
known_providers = ["awsdns", "azure-dns", "cloudflare", "google", "route53", "ns1."]
|
|
for ns in ns_records:
|
|
ns_lower = ns.lower().rstrip(".")
|
|
is_known = any(provider in ns_lower for provider in known_providers)
|
|
if not is_known:
|
|
findings.append({
|
|
"domain": domain, "nameserver": ns_lower,
|
|
"severity": "high",
|
|
"issue": "NS record points to unrecognized nameserver",
|
|
"recommendation": "Verify this is an authorized nameserver for the domain",
|
|
})
|
|
return findings
|
|
|
|
|
|
def analyze_dns_history(domain, api_key):
|
|
"""Analyze historical DNS changes for signs of hijacking."""
|
|
findings = []
|
|
try:
|
|
history = query_securitytrails(domain, api_key)
|
|
records = history.get("records", [])
|
|
if len(records) >= 2:
|
|
for i in range(1, len(records)):
|
|
prev_ips = set(v.get("ip", "") for v in records[i - 1].get("values", []))
|
|
curr_ips = set(v.get("ip", "") for v in records[i].get("values", []))
|
|
changed = curr_ips - prev_ips
|
|
if changed:
|
|
findings.append({
|
|
"domain": domain,
|
|
"first_seen": records[i].get("first_seen", ""),
|
|
"new_ips": list(changed),
|
|
"severity": "medium",
|
|
"issue": f"DNS A record changed: added {changed}",
|
|
})
|
|
except requests.RequestException as e:
|
|
logger.warning("SecurityTrails query failed: %s", e)
|
|
return findings
|
|
|
|
|
|
def generate_report(domain, dangling, wildcard, ns_findings, history_findings):
|
|
all_findings = dangling + wildcard + ns_findings + history_findings
|
|
critical = sum(1 for f in all_findings if f.get("severity") == "critical")
|
|
return {
|
|
"timestamp": datetime.utcnow().isoformat(),
|
|
"target_domain": domain,
|
|
"dangling_cnames": dangling,
|
|
"wildcard_abuse": wildcard,
|
|
"ns_delegation_issues": ns_findings,
|
|
"historical_anomalies": history_findings,
|
|
"total_findings": len(all_findings),
|
|
"critical_findings": critical,
|
|
"risk_level": "critical" if critical > 0 else "high" if all_findings else "low",
|
|
}
|
|
|
|
|
|
def main():
|
|
parser = argparse.ArgumentParser(description="DNS Persistence Hunting Agent")
|
|
parser.add_argument("--domain", required=True, help="Target domain to hunt")
|
|
parser.add_argument("--api-key", required=True, help="SecurityTrails API key")
|
|
parser.add_argument("--output", default="dns_persistence_report.json")
|
|
args = parser.parse_args()
|
|
|
|
subdomains = get_subdomains(args.domain, args.api_key)
|
|
logger.info("Discovered %d subdomains for %s", len(subdomains), args.domain)
|
|
dangling = detect_dangling_cnames(subdomains[:200])
|
|
wildcard = detect_wildcard_abuse(args.domain)
|
|
ns_findings = check_ns_delegation(args.domain)
|
|
history_findings = analyze_dns_history(args.domain, args.api_key)
|
|
report = generate_report(args.domain, dangling, wildcard, ns_findings, history_findings)
|
|
with open(args.output, "w") as f:
|
|
json.dump(report, f, indent=2, default=str)
|
|
logger.info("DNS hunt: %s - %d findings (%d critical), %d subdomains checked",
|
|
args.domain, report["total_findings"], report["critical_findings"], len(subdomains))
|
|
print(json.dumps(report, indent=2, default=str))
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|