Skip to content
Threats Guide

dnssec_check.py

DNSSEC check

  • prevention
  • python
  • 74 lines

Run security tools only on systems you own or have written permission to test.

Run it

From the repository root, after pip install -r tools/requirements.txt
cd 01-network-security/dns-spoofing/prevention
python3 dnssec_check.py --help

Source

View on GitHub
#!/usr/bin/env python3"""DNSSEC checkLocation: 01-network-security/dns-spoofing/prevention/dnssec_check.pyDNSSEC signs DNS data so a resolver can prove an answer was not forged. This tool asks, for a domain:  1. Is the zone signed? (DNSKEY records exist)  2. Does YOUR resolver validate? (the AD "authenticated data" flag is set on a signed name)Results:  VALIDATED          signed zone, resolver validated it (AD flag set)         -> good  SIGNED_NOT_CHECKED signed zone, but your resolver does not validate         -> switch resolver  UNSIGNED           the domain does not use DNSSEC                           -> ask the owner to sign it  ERROR              lookup failed (network, SERVFAIL = possible validation failure)Exit codes: 0 = VALIDATED, 1 = anything else.Other defences: DNS-over-TLS/HTTPS to a trusted resolver, source-port randomisation, and keepingresolver software patched.""" import argparseimport sys import dns.flagsimport dns.exceptionimport dns.messageimport dns.queryimport dns.rcodeimport dns.resolverimport dns.rdatatype  def default_resolver():    return dns.resolver.Resolver().nameservers[0]  def check_domain(domain, resolver_ip, timeout=5.0, query=dns.query.udp):    """Return (status, detail). `query` is injectable so the logic can be tested offline."""    try:        request = dns.message.make_query(domain, dns.rdatatype.DNSKEY, want_dnssec=True)        response = query(request, resolver_ip, timeout=timeout)    except (dns.exception.DNSException, OSError) as exc:        return "ERROR", f"lookup failed: {exc}"     if response.rcode() == dns.rcode.SERVFAIL:        return "ERROR", "SERVFAIL: for signed domains this can mean DNSSEC validation FAILED (possible tampering)"    has_keys = any(rrset.rdtype == dns.rdatatype.DNSKEY for rrset in response.answer)    if not has_keys:        return "UNSIGNED", f"{domain} publishes no DNSKEY records"    if response.flags & dns.flags.AD:        return "VALIDATED", f"{domain} is signed and {resolver_ip} validated it"    return "SIGNED_NOT_CHECKED", f"{domain} is signed but {resolver_ip} did not set the AD flag"  def main():    parser = argparse.ArgumentParser(description="DNSSEC signing and validation check")    parser.add_argument("domain", nargs="+")    parser.add_argument("--resolver", help="resolver IP (default: system resolver)")    parser.add_argument("--timeout", type=float, default=5.0)    args = parser.parse_args()    resolver_ip = args.resolver or default_resolver()    print(f"[*] Resolver: {resolver_ip}")    worst = 0    for domain in args.domain:        status, detail = check_domain(domain, resolver_ip, args.timeout)        print(f"  {domain:30} {status:20} {detail}")        worst = worst or (0 if status == "VALIDATED" else 1)    return worst  if __name__ == "__main__":    sys.exit(main())