httpz/httpz_scanner/dns.py

116 lines
4.2 KiB
Python
Raw Normal View History

2025-02-11 07:15:39 +00:00
#!/usr/bin/env python3
# HTTPZ Web Scanner - Developed by acidvegas in Python (https://github.com/acidvegas/httpz)
2025-02-11 07:46:01 +00:00
# httpz_scanner/dns.py
2025-02-11 07:15:39 +00:00
import asyncio
import os
2025-02-12 03:30:22 +00:00
try:
import aiohttp
except ImportError:
raise ImportError('missing aiohttp library (pip install aiohttp)')
try:
import dns.asyncresolver
import dns.query
import dns.resolver
import dns.zone
except ImportError:
raise ImportError('missing dnspython library (pip install dnspython)')
2025-02-11 07:15:39 +00:00
from .utils import debug, info, SILENT_MODE
2025-02-12 03:30:22 +00:00
2025-02-11 07:15:39 +00:00
async def resolve_all_dns(domain: str, timeout: int = 5, nameserver: str = None, check_axfr: bool = False) -> tuple:
'''
Resolve all DNS records for a domain
:param domain: Domain to resolve
:param timeout: Timeout in seconds
:param nameserver: Specific nameserver to use
:param check_axfr: Whether to attempt zone transfer
'''
2025-02-12 03:30:22 +00:00
# Setup resolver
2025-02-11 07:15:39 +00:00
resolver = dns.asyncresolver.Resolver()
resolver.lifetime = timeout
if nameserver:
resolver.nameservers = [nameserver]
2025-02-12 03:30:22 +00:00
# Resolve all DNS records
results = await asyncio.gather(*[resolver.resolve(domain, rtype) for rtype in ('NS', 'A', 'AAAA', 'CNAME')], return_exceptions=True)
2025-02-11 07:15:39 +00:00
2025-02-12 03:30:22 +00:00
# Parse results
2025-02-11 07:15:39 +00:00
nameservers = [str(ns).rstrip('.') for ns in results[0]] if isinstance(results[0], dns.resolver.Answer) else []
2025-02-12 03:30:22 +00:00
ips = ([str(ip) for ip in results[1]] if isinstance(results[1], dns.resolver.Answer) else []) + ([str(ip) for ip in results[2]] if isinstance(results[2], dns.resolver.Answer) else [])
cname = str(results[3][0].target).rstrip('.') if isinstance(results[3], dns.resolver.Answer) else None
# Get NS IPs
2025-02-11 07:15:39 +00:00
ns_ips = {}
if nameservers:
2025-02-12 03:30:22 +00:00
ns_results = await asyncio.gather(*[resolver.resolve(ns, rtype) for ns in nameservers for rtype in ('A', 'AAAA')], return_exceptions=True)
2025-02-11 07:15:39 +00:00
for i, ns in enumerate(nameservers):
2025-02-12 03:30:22 +00:00
ns_ips[ns] = [str(ip) for records in ns_results[i*2:i*2+2] if isinstance(records, dns.resolver.Answer) for ip in records]
2025-02-11 07:15:39 +00:00
2025-02-12 03:30:22 +00:00
# Attempt zone transfer
2025-02-11 07:15:39 +00:00
if check_axfr:
await attempt_axfr(domain, ns_ips, timeout)
return sorted(set(ips)), cname, nameservers, ns_ips
2025-02-12 03:30:22 +00:00
2025-02-11 07:15:39 +00:00
async def attempt_axfr(domain: str, ns_ips: dict, timeout: int = 5) -> None:
'''
Attempt zone transfer for a domain
:param domain: Domain to attempt AXFR transfer
:param ns_ips: Dictionary of nameserver hostnames to their IPs
:param timeout: Timeout in seconds
'''
2025-02-12 03:30:22 +00:00
2025-02-11 07:15:39 +00:00
try:
os.makedirs('axfrout', exist_ok=True)
2025-02-12 03:30:22 +00:00
# Loop through each NS
2025-02-11 07:15:39 +00:00
for ns_host, ips in ns_ips.items():
2025-02-12 03:30:22 +00:00
# Loop through each NS IP
2025-02-11 07:15:39 +00:00
for ns_ip in ips:
try:
2025-02-12 03:30:22 +00:00
# Attempt zone transfer
2025-02-11 07:15:39 +00:00
zone = dns.zone.from_xfr(dns.query.xfr(ns_ip, domain, lifetime=timeout))
2025-02-12 03:30:22 +00:00
# Write zone to file
2025-02-11 07:15:39 +00:00
with open(f'axfrout/{domain}_{ns_ip}.zone', 'w') as f:
zone.to_text(f)
2025-02-12 03:30:22 +00:00
2025-02-11 07:15:39 +00:00
info(f'[AXFR SUCCESS] {domain} from {ns_host} ({ns_ip})')
except Exception as e:
debug(f'AXFR failed for {domain} from {ns_ip}: {str(e)}')
except Exception as e:
debug(f'Failed AXFR for {domain}: {str(e)}')
2025-02-12 03:30:22 +00:00
2025-02-11 07:15:39 +00:00
async def load_resolvers(resolver_file: str = None) -> list:
'''
Load DNS resolvers from file or default source
:param resolver_file: Path to file containing resolver IPs
'''
2025-02-12 03:30:22 +00:00
# Load from file
2025-02-11 07:15:39 +00:00
if resolver_file:
try:
with open(resolver_file) as f:
resolvers = [line.strip() for line in f if line.strip()]
if resolvers:
return resolvers
except Exception as e:
debug(f'Error loading resolvers from {resolver_file}: {str(e)}')
2025-02-12 03:30:22 +00:00
# Load from GitHub
2025-02-11 07:15:39 +00:00
async with aiohttp.ClientSession() as session:
async with session.get('https://raw.githubusercontent.com/trickest/resolvers/refs/heads/main/resolvers.txt') as response:
resolvers = await response.text()
if not SILENT_MODE:
info(f'Loaded {len(resolvers.splitlines()):,} resolvers.')
return [resolver.strip() for resolver in resolvers.splitlines()]