Cybersecurity-Projects/PROJECTS/beginner/dns-lookup/learn/04-CHALLENGES.md

18 KiB

Challenges and Extensions

Beginner Challenges

Challenge 1: Add DNSSEC Validation

Goal: Verify DNSSEC signatures on DNS responses.

Current State: The tool retrieves records but doesn't validate signatures. Line resolver.py:200-201 just fetches data without cryptographic verification.

Implementation Tasks:

  1. Enable DNSSEC in resolver (resolver.py:91-107)
   resolver = dns.asyncresolver.Resolver()
   resolver.use_edns(0, dns.flags.DO, 4096)  # Request DNSSEC records
  1. Check RRSIG records in responses
  2. Validate signatures against DNSKEY records
  3. Walk chain of trust from root to target domain

Resources:

  • RFC 4033, 4034, 4035 (DNSSEC specs)
  • dnspython DNSSEC validation examples
  • Test with dnssec-tools.org (signed domain)

Security Impact: Prevents accepting forged DNS responses. Critical for high-security environments.

Challenge 2: Implement DNS Query Logging

Goal: Log all queries to a file for analysis.

What to Log:

  • Timestamp
  • Domain queried
  • Record types requested
  • DNS server used
  • Response time
  • Number of records returned

Implementation:

  1. Create logger in resolver.py:213-250
  2. Log before query: logging.info(f"Querying {domain} for {record_types}")
  3. Log after query: logging.info(f"Got {len(result.records)} records in {result.query_time_ms}ms")
  4. Add CLI flag: --log-file queries.log

Analysis Ideas:

  • Which domains take longest to resolve?
  • Which record types fail most often?
  • Time series analysis of response times

Security Application: Detect reconnaissance by analyzing query patterns.

Challenge 3: Add CSV Output Format

Goal: Export results to CSV for spreadsheet analysis.

Current State: JSON output exists (output.py:379-410), but CSV would be more useful for some workflows.

Implementation:

  1. Add results_to_csv() function in output.py
  2. Flatten nested structure:
   domain,record_type,value,ttl,priority,query_time_ms
   example.com,A,93.184.216.34,86400,,45.2
   example.com,MX,mail.example.com,3600,10,45.2
  1. Handle multiple records per domain
  2. Add --csv flag to commands

Use Case: Import into Excel/Google Sheets for analysis, sorting, filtering.

Intermediate Challenges

Challenge 4: Subdomain Enumeration

Goal: Discover subdomains through various techniques.

Techniques to Implement:

1. Brute Force (cli.py:266-350 as starting point)

common_subdomains = ['www', 'mail', 'ftp', 'admin', 'test', 'dev', 'staging']
for sub in common_subdomains:
    fqdn = f"{sub}.{domain}"
    result = await lookup(fqdn, [RecordType.A])
    if result.records:
        print(f"Found: {fqdn}")

2. Certificate Transparency Logs Query crt.sh API for domains in SSL certificates:

import requests
response = requests.get(f"https://crt.sh/?q=%.{domain}&output=json")

3. DNS Zone Transfer (AXFR)

try:
    zone = dns.zone.from_xfr(dns.query.xfr(nameserver, domain))
    for name in zone:
        print(f"{name}.{domain}")
except:
    print("Zone transfer refused")

Security Note: Zone transfers are often blocked. Only test on domains you own.

Real World: Subdomain enumeration is first step in reconnaissance (MITRE T1590.002).

Challenge 5: DNS Monitoring and Alerting

Goal: Continuously monitor domains and alert on changes.

Architecture:

Periodic Queries (every 5 min)
        ↓
Compare to Previous State
        ↓
Detect Changes
        ↓
Send Alerts (email/Slack/webhook)

What to Monitor:

  • A/AAAA record changes (infrastructure moved)
  • MX record changes (email routing modified)
  • NS record changes (DNS provider changed)
  • New/removed records

Implementation:

  1. Store previous state in SQLite database
  2. Query on schedule (use schedule library or cron)
  3. Compare current vs previous
  4. Trigger alerts on differences

Security Application: Detect DNS hijacking attempts. If NS records suddenly change, someone might have compromised the registrar account.

Real Incident: MyEtherWallet (2018) - BGP hijack changed DNS to point to phishing site. Monitoring would have detected this.

Challenge 6: DNS Performance Analysis

Goal: Measure and visualize DNS resolver performance.

Metrics to Track:

  • Query latency percentiles (p50, p95, p99)
  • Success rate by record type
  • Timeout frequency
  • Resolver comparison (8.8.8.8 vs 1.1.1.1 vs ISP)

Implementation:

  1. Extend DNSResult with more metrics
  2. Query same domains from multiple resolvers
  3. Store results in time-series database (InfluxDB)
  4. Visualize with Grafana

Add to resolver.py:

@dataclass
class DNSResult:
    # ... existing fields ...
    resolver_ip: str | None = None
    tcp_fallback: bool = False
    truncated: bool = False

Analysis Questions:

  • Which resolver is fastest for your location?
  • Does performance vary by time of day?
  • Which domains have the longest resolution times?

Advanced Challenges

Challenge 7: DNS Tunnel Detection

Goal: Identify DNS tunneling for data exfiltration.

DNS Tunneling Characteristics:

  • High volume of queries to a single domain
  • Large TXT record queries/responses
  • Unusual subdomain lengths
  • Base64/hex-encoded subdomains
  • Regular query intervals

Implementation:

1. Entropy Analysis

import math
from collections import Counter

def calculate_entropy(subdomain: str) -> float:
    """High entropy suggests encoded data"""
    counter = Counter(subdomain)
    length = len(subdomain)
    return -sum((count/length) * math.log2(count/length) 
                for count in counter.values())

# Legitimate: www.example.com (low entropy)
# Tunneling: aGVsbG8=.attacker.com (high entropy)

2. Query Volume Detection

# Track queries per domain
query_counts = defaultdict(int)

for domain in queries:
    query_counts[domain] += 1
    if query_counts[domain] > threshold:
        alert(f"Possible tunneling: {domain}")

3. Subdomain Length Analysis

if len(subdomain) > 63:  # DNS label limit
    alert("Suspicious long subdomain")

Real Malware: Houdini worm, Feederbot, DNScat2 all use DNS tunneling.

References:

  • CWE-406: Insufficient Control of Network Message Volume
  • MITRE T1071.004: Application Layer Protocol: DNS

Challenge 8: Passive DNS Database

Goal: Build a historical database of DNS resolutions.

Architecture:

DNS Queries (from monitoring/batch)
        ↓
Store in Database
        ↓
Query Historical Data

Schema:

CREATE TABLE dns_records (
    id INTEGER PRIMARY KEY,
    domain TEXT,
    record_type TEXT,
    value TEXT,
    ttl INTEGER,
    first_seen TIMESTAMP,
    last_seen TIMESTAMP,
    seen_count INTEGER
);

CREATE INDEX idx_domain ON dns_records(domain);
CREATE INDEX idx_value ON dns_records(value);

Queries to Support:

  • When did example.com first resolve to 1.2.3.4?
  • What domains have resolved to this IP?
  • How often does this domain change IPs?

Security Application: Track malware C2 infrastructure. Malicious domains often change IPs frequently.

Commercial Examples: VirusTotal, PassiveTotal, Farsight DNSDB.

Challenge 9: Real-time DNS Query Stream Analysis

Goal: Analyze live DNS query streams (from router/firewall logs).

Input Sources:

  • pcap files from network captures
  • Syslog from DNS servers
  • Netflow/IPFIX data
  • Cloud DNS query logs (AWS Route53, Google Cloud DNS)

Detection Patterns:

1. DGA (Domain Generation Algorithm)

def is_dga(domain: str) -> bool:
    """DGA domains are often random-looking"""
    labels = domain.split('.')
    if len(labels) < 2:
        return False
    
    sld = labels[-2]  # second-level domain
    
    # High consonant ratio
    consonants = sum(1 for c in sld if c in 'bcdfghjklmnpqrstvwxyz')
    if consonants / len(sld) > 0.7:
        return True
    
    # No dictionary words
    # Low vowel ratio
    # High entropy
    
    return False

2. Fast Flux Detection Many IPs for one domain, changing frequently:

# Track A records over time
if len(unique_ips_for_domain) > 10 and avg_ttl < 300:
    alert("Possible fast flux")

3. C2 Beaconing Regular periodic queries:

# Calculate query intervals
intervals = [queries[i+1].time - queries[i].time 
             for i in range(len(queries)-1)]

# If intervals are suspiciously regular
if std_dev(intervals) < threshold:
    alert("Possible C2 beaconing")

Tools to Study: Zeek (formerly Bro), Suricata DNS analysis modules.

Challenge 10: DNS Firewall / Sinkhole

Goal: Block malicious domains by serving fake responses.

Architecture:

Client Query
    ↓
Your DNS Server
    ↓
Check Blocklist
    ↓
If Blocked: Return 0.0.0.0
If Allowed: Forward to Upstream Resolver

Implementation:

1. Basic DNS Server (use dnslib library)

from dnslib import DNSRecord, RR, QTYPE, A
from dnslib.server import DNSServer

class BlocklistResolver:
    def __init__(self, blocklist):
        self.blocklist = set(blocklist)
    
    def resolve(self, request, handler):
        qname = str(request.q.qname)
        
        if qname.rstrip('.') in self.blocklist:
            # Return sinkhole IP
            reply = request.reply()
            reply.add_answer(RR(qname, QTYPE.A, rdata=A("0.0.0.0")))
            return reply
        else:
            # Forward to real resolver
            return proxy_request(request)

2. Blocklist Sources

  • Malware domain lists (abuse.ch)
  • Phishing feeds (PhishTank)
  • Ad/tracker lists (EasyList)
  • Threat intel feeds

3. Logging and Analytics

  • Track blocked query attempts
  • Identify infected machines
  • Measure effectiveness

Real Products: Pi-hole, AdGuard Home, Cisco Umbrella.

Expert Challenges

Challenge 11: DNS Amplification Attack Detection

Goal: Detect when your DNS server is being used for amplification attacks.

Attack Pattern:

  1. Attacker sends queries with spoofed source IP (victim's address)
  2. Your server sends large responses to victim
  3. Victim gets flooded

Detection Signals:

# 1. High volume from single IP
queries_per_ip = defaultdict(int)

# 2. Queries for large record types
if record_type in [RecordType.TXT, RecordType.ANY]:
    suspicious_count += 1

# 3. Responses much larger than queries
if response_size / query_size > 50:
    alert("High amplification factor")

# 4. No follow-up queries (victim doesn't actually want data)
if client_ip in one_shot_queries:
    alert("Possible spoofed source")

Mitigation:

  • Rate limit queries per IP
  • Disable ANY queries (RFC 8482)
  • Response rate limiting (RRL)
  • BCP38 filtering (prevent spoofed source IPs)

Challenge 12: Custom DNS Record Types

Goal: Add support for newer/specialized record types.

Record Types to Add:

CAA (Certification Authority Authorization)

# In resolver.py:24-33
class RecordType(StrEnum):
    # ... existing types ...
    CAA = "CAA"  # RFC 8659

Specifies which CAs can issue certificates for domain. Security-critical.

SSHFP (SSH Fingerprint)

SSHFP = "SSHFP"  # RFC 4255

Publishes SSH server key fingerprints in DNS for verification.

TLSA (DANE/TLSA)

TLSA = "TLSA"  # RFC 6698

DNS-based Authentication of Named Entities. Publishes TLS certificate hashes.

DNSKEY, DS, RRSIG (DNSSEC records) For challenge 1 (DNSSEC validation).

Challenge 13: Geographic DNS Analysis

Goal: Determine where DNS servers are located and analyze geographic distribution.

Implementation:

1. GeoIP Lookup

import geoip2.database

reader = geoip2.database.Reader('GeoLite2-City.mmdb')

for nameserver_ip in result.nameserver_ips:
    response = reader.city(nameserver_ip)
    print(f"{nameserver_ip}: {response.city.name}, {response.country.name}")

2. Latency-based Geolocation Ping from multiple vantage points, use speed-of-light calculations to estimate location.

3. Anycast Detection If same IP resolves to different locations, it's anycast (like root servers, Google DNS, Cloudflare DNS).

Security Analysis:

  • Is DNS infrastructure geographically diverse? (resilience)
  • Are responses coming from unexpected countries? (hijacking)
  • Latency analysis for optimal resolver selection

Challenge 14: DNS Covert Channel Communication

Goal: Implement bidirectional communication over DNS (for research/education only).

How It Works:

Client → Server (via queries)

Encode message in subdomain:
aGVsbG8=.tunnel.attacker.com

Server → Client (via responses)

Encode response in TXT record:
"d29ybGQ="

Implementation:

def encode_query(data: bytes, domain: str) -> str:
    """Encode data in subdomain"""
    encoded = base64.b64encode(data).decode()
    # Split into DNS labels (63 char max)
    labels = [encoded[i:i+63] for i in range(0, len(encoded), 63)]
    return '.'.join(labels) + '.' + domain

def decode_response(txt_record: str) -> bytes:
    """Decode data from TXT record"""
    return base64.b64decode(txt_record)

Challenges:

  • DNS has size limits (512 bytes UDP, 4096 TCP)
  • Must handle fragmentation
  • Packet loss requires retransmission
  • High latency (1-2s per request)

Countermeasures to Study:

  • Entropy analysis (challenge 7)
  • Query volume limits
  • Subdomain length restrictions
  • TXT record size monitoring

Ethical Note: Only test on infrastructure you own. DNS tunneling without authorization is illegal.

Challenge 15: Build a Recursive DNS Resolver

Goal: Implement a full recursive resolver from scratch (like BIND, Unbound).

What It Does:

  1. Accept queries from clients
  2. Iterate through DNS hierarchy (like trace command)
  3. Cache results
  4. Return answers

Core Components:

1. Cache

from functools import lru_cache
import time

class DNSCache:
    def __init__(self):
        self.cache = {}
    
    def get(self, key):
        if key in self.cache:
            value, expiry = self.cache[key]
            if time.time() < expiry:
                return value
        return None
    
    def set(self, key, value, ttl):
        self.cache[key] = (value, time.time() + ttl)

2. Recursive Resolution (extend trace_dns() from resolver.py:293-426)

3. Server Loop

from dnslib.server import DNSServer

class RecursiveResolver:
    def resolve(self, request, handler):
        qname = str(request.q.qname)
        
        # Check cache
        cached = self.cache.get(qname)
        if cached:
            return cached
        
        # Perform recursive resolution
        result = trace_and_resolve(qname)
        
        # Cache result
        self.cache.set(qname, result, ttl=300)
        
        return result

resolver = RecursiveResolver()
server = DNSServer(resolver, port=5353)
server.start_thread()

Challenges:

  • Handle CNAME chains
  • Implement negative caching (NXDOMAIN)
  • Deal with timeouts and retries
  • DNSSEC validation
  • Handle TCP fallback for large responses

Testing:

dig @localhost -p 5353 example.com

Research Challenges

Challenge 16: Machine Learning for Malicious Domain Detection

Goal: Train ML model to classify domains as malicious or benign.

Features to Extract:

  • Domain length
  • Character entropy
  • N-gram frequencies
  • Vowel/consonant ratio
  • TLD (.com vs .xyz vs .tk)
  • Registration age (from WHOIS)
  • Subdomain count
  • Historical IP changes
  • ASN reputation

Training Data:

  • Benign: Alexa/Majestic top sites
  • Malicious: abuse.ch, PhishTank, malware feeds

Models to Try:

  • Random Forest
  • XGBoost
  • Neural networks for sequence data

Evaluation:

  • Precision/recall on test set
  • False positive rate (critical for blocklists)
  • Performance on DGA domains

Challenge: Low false positives while catching novel threats.

Challenge 17: DNS Privacy Analysis

Goal: Measure information leakage from DNS queries.

What Leaks:

  • Browsing history from query logs
  • Location from resolver choice
  • Device type from query patterns
  • App usage from CDN queries

Experiments:

1. Reconstruct Browsing Session Collect DNS queries for 1 hour. Can you infer:

  • Which websites visited?
  • Which apps used?
  • User's interests/demographics?

2. DNS Fingerprinting Different devices make different queries:

  • Android queries android.googleapis.com
  • iOS queries apple.com services
  • Windows queries microsoft.com

3. Correlation Attacks Even with DoH, timing analysis can reveal sites:

# Time between query and HTTP request
# Query pattern (images, scripts, API calls)
# Size of responses

References:

  • "The Effect of DNS on Tor's Anonymity" (research paper)
  • CWE-201: Information Exposure Through Sent Data

Challenge 18: Develop DNS Benchmarking Suite

Goal: Create comprehensive DNS performance testing toolkit.

Benchmarks:

1. Latency

  • Measure p50, p95, p99 query latency
  • Test from multiple geographic locations
  • Compare resolvers (Google, Cloudflare, Quad9)

2. Throughput

  • Queries per second capacity
  • Concurrent query handling

3. Reliability

  • Success rate over 24 hours
  • NXDOMAIN handling
  • Timeout frequency

4. Privacy

  • Query minimization support (RFC 7816)
  • DNSSEC validation
  • DoH/DoT support

5. Censorship Resistance

  • Are queries filtered?
  • Are results manipulated?

Tools to Build:

  • Automated test runner
  • Result aggregation and visualization
  • Historical tracking
  • Alerting on degradation

Public Service: Publish results like DNSPerf.com

Wrap-Up

Each challenge builds specific skills:

  • Challenges 1-3: Tool development, output formats
  • Challenges 4-6: Security monitoring, performance analysis
  • Challenges 7-10: Threat detection, defensive tools
  • Challenges 11-15: Advanced DNS concepts, covert channels
  • Challenges 16-18: Research, ML, measurement studies

Next Steps:

  1. Pick challenges matching your skill level
  2. Start with beginner challenges to learn codebase
  3. Progress to security-focused intermediate challenges
  4. Tackle advanced challenges for deep expertise

Resources:

  • DNS RFCs (1034, 1035, and extensions)
  • OWASP Testing Guide (DNS sections)
  • MITRE ATT&CK Framework (T1590, T1071.004, T1584.002)
  • DNSViz for visualizing DNSSEC
  • Wireshark for packet analysis

Happy hacking! Remember: only test on systems you own or have explicit permission to test.