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