This repository has been archived on 2026-06-11. You can view files and clone it, but cannot push or open issues or pull requests.
Diablo_ClaudeMD_Ricing_example/skills/recon-osint/scripts/dns_recon.py
diablo 50fa79407d
Some checks are pending
CI — CoM Config Validation / Validate JSON Configs (push) Waiting to run
CI — CoM Config Validation / Validate YAML Configs (push) Waiting to run
CI — CoM Config Validation / Lint Shell Scripts (push) Waiting to run
CI — CoM Config Validation / Secret Detection (push) Waiting to run
CI — CoM Config Validation / Lint Markdown (push) Waiting to run
CI — CoM Config Validation / Validate CODEOWNERS (push) Waiting to run
CoM Claude Command Center — sanitized public configuration
Public, sanitized mirror of an AI orchestration command center: agents, skills,
MCP servers, slash-command workflows. All infrastructure identifiers, hostnames,
mesh IPs/subnets, repo paths, maintainer identity, and hardware fleet specifics
scrubbed to <placeholders>; session debug logs and host-specific memory removed.
No live credentials. Verified clean by automated leak sweep. See SANITIZATION.md.

churchofmalware.org . authorized research only
2026-06-10 02:02:03 -04:00

239 lines
9.0 KiB
Python

#!/usr/bin/env python3
"""
DNS Reconnaissance Tool
Comprehensive DNS record analysis and misconfiguration detection.
Repository: https://github.com/Masriyan/Claude-Code-CyberSecurity-Skill
"""
import argparse
import json
import logging
import sys
import time
from typing import Any, Dict, List, Optional
try:
import dns.resolver
import dns.query
import dns.zone
import dns.reversename
except ImportError:
print("[!] 'dnspython' module required: pip install dnspython")
sys.exit(1)
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s [%(levelname)s] %(message)s",
datefmt="%Y-%m-%d %H:%M:%S",
)
logger = logging.getLogger(__name__)
class DNSRecon:
"""Comprehensive DNS reconnaissance engine."""
RECORD_TYPES = ["A", "AAAA", "MX", "NS", "TXT", "SOA", "SRV", "CNAME", "CAA", "PTR"]
def __init__(self, domain: str, nameserver: Optional[str] = None, timeout: int = 5):
self.domain = domain.lower().strip()
self.resolver = dns.resolver.Resolver()
self.resolver.timeout = timeout
self.resolver.lifetime = timeout
if nameserver:
self.resolver.nameservers = [nameserver]
self.results: Dict[str, Any] = {"domain": self.domain, "records": {}}
def enumerate_records(self) -> Dict[str, List[str]]:
"""Enumerate all DNS record types."""
logger.info("[DNS] Enumerating records for %s", self.domain)
records = {}
for rtype in self.RECORD_TYPES:
try:
answers = self.resolver.resolve(self.domain, rtype)
records[rtype] = [str(r) for r in answers]
logger.info("[DNS] %s records: %d found", rtype, len(records[rtype]))
except (dns.resolver.NXDOMAIN, dns.resolver.NoAnswer, dns.resolver.NoNameservers):
records[rtype] = []
except Exception as e:
logger.debug("[DNS] %s lookup error: %s", rtype, str(e))
records[rtype] = []
self.results["records"] = records
return records
def check_zone_transfer(self) -> Dict[str, Any]:
"""Attempt AXFR zone transfer on all nameservers."""
logger.info("[AXFR] Testing zone transfer for %s", self.domain)
axfr_results = {"vulnerable": False, "nameservers": {}}
try:
ns_answers = self.resolver.resolve(self.domain, "NS")
for ns in ns_answers:
ns_host = str(ns).rstrip(".")
try:
zone = dns.zone.from_xfr(
dns.query.xfr(ns_host, self.domain, timeout=5)
)
zone_records = []
for name, node in zone.nodes.items():
zone_records.append(str(name))
axfr_results["vulnerable"] = True
axfr_results["nameservers"][ns_host] = {
"status": "VULNERABLE",
"records_count": len(zone_records),
"records": zone_records[:50],
}
logger.warning("[AXFR] Zone transfer SUCCESSFUL on %s!", ns_host)
except Exception:
axfr_results["nameservers"][ns_host] = {"status": "REFUSED"}
logger.info("[AXFR] Zone transfer refused on %s", ns_host)
except Exception as e:
logger.error("[AXFR] Error: %s", str(e))
self.results["zone_transfer"] = axfr_results
return axfr_results
def analyze_email_security(self) -> Dict[str, Any]:
"""Analyze SPF, DKIM, and DMARC records."""
logger.info("[Email] Analyzing email security for %s", self.domain)
email_security = {"spf": None, "dmarc": None, "dkim_selector_test": None}
# SPF
try:
txt_records = self.resolver.resolve(self.domain, "TXT")
for record in txt_records:
text = str(record).strip('"')
if text.startswith("v=spf1"):
email_security["spf"] = {
"record": text,
"mechanisms": self._parse_spf(text),
}
break
except Exception:
pass
# DMARC
try:
dmarc_domain = f"_dmarc.{self.domain}"
dmarc_records = self.resolver.resolve(dmarc_domain, "TXT")
for record in dmarc_records:
text = str(record).strip('"')
if text.startswith("v=DMARC1"):
email_security["dmarc"] = {
"record": text,
"policy": self._parse_dmarc(text),
}
break
except Exception:
pass
# DKIM (common selectors)
selectors = ["default", "google", "selector1", "selector2", "mail", "dkim"]
for selector in selectors:
try:
dkim_domain = f"{selector}._domainkey.{self.domain}"
dkim_records = self.resolver.resolve(dkim_domain, "TXT")
email_security["dkim_selector_test"] = {
"selector": selector,
"found": True,
"record": str(list(dkim_records)[0]).strip('"')[:200],
}
break
except Exception:
continue
# Security assessment
issues = []
if not email_security["spf"]:
issues.append("No SPF record found — domain vulnerable to email spoofing")
if not email_security["dmarc"]:
issues.append("No DMARC record found — no email authentication policy")
elif email_security["dmarc"]["policy"].get("p") == "none":
issues.append("DMARC policy set to 'none' — no enforcement")
email_security["issues"] = issues
self.results["email_security"] = email_security
return email_security
def _parse_spf(self, record: str) -> Dict[str, Any]:
"""Parse SPF record into components."""
parts = record.split()
return {
"version": parts[0] if parts else "",
"mechanisms": [p for p in parts[1:] if not p.startswith("~") and not p.startswith("-")],
"all_policy": parts[-1] if parts and parts[-1] in ["+all", "-all", "~all", "?all"] else "unknown",
}
def _parse_dmarc(self, record: str) -> Dict[str, str]:
"""Parse DMARC record into components."""
policy = {}
for part in record.split(";"):
part = part.strip()
if "=" in part:
key, value = part.split("=", 1)
policy[key.strip()] = value.strip()
return policy
def reverse_dns(self, ip_addresses: List[str]) -> Dict[str, str]:
"""Perform reverse DNS lookups."""
logger.info("[rDNS] Performing reverse lookups on %d IPs", len(ip_addresses))
rdns_results = {}
for ip in ip_addresses:
try:
rev_name = dns.reversename.from_address(ip)
answers = self.resolver.resolve(rev_name, "PTR")
rdns_results[ip] = str(list(answers)[0]).rstrip(".")
except Exception:
rdns_results[ip] = "No PTR record"
self.results["reverse_dns"] = rdns_results
return rdns_results
def run(self, check_axfr: bool = True) -> Dict[str, Any]:
"""Execute full DNS reconnaissance."""
logger.info("=" * 60)
logger.info("DNS Reconnaissance: %s", self.domain)
logger.info("=" * 60)
self.enumerate_records()
if check_axfr:
self.check_zone_transfer()
self.analyze_email_security()
# Reverse DNS on discovered A records
a_records = self.results["records"].get("A", [])
if a_records:
self.reverse_dns(a_records)
self.results["timestamp"] = time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime())
return self.results
def main():
parser = argparse.ArgumentParser(
description="DNS Reconnaissance Tool",
epilog="https://github.com/Masriyan/Claude-Code-CyberSecurity-Skill",
)
parser.add_argument("--domain", "-d", required=True, help="Target domain")
parser.add_argument("--output", "-o", help="Output file (JSON)")
parser.add_argument("--nameserver", "-n", help="Custom nameserver")
parser.add_argument("--timeout", type=int, default=5, help="DNS timeout (default: 5)")
parser.add_argument("--check-zone-transfer", action="store_true", default=True, help="Test zone transfers")
parser.add_argument("--verbose", "-v", action="store_true")
args = parser.parse_args()
if args.verbose:
logging.getLogger().setLevel(logging.DEBUG)
recon = DNSRecon(domain=args.domain, nameserver=args.nameserver, timeout=args.timeout)
results = recon.run(check_axfr=args.check_zone_transfer)
if args.output:
with open(args.output, "w") as f:
json.dump(results, f, indent=2)
logger.info("Results saved to %s", args.output)
else:
print(json.dumps(results, indent=2))
if __name__ == "__main__":
main()