Making dns tunnels unusable


is a way to transport data inside the dns protocol layer

In a nutshell:
If somebody is allowed to do arbitrary dns queries, it is possible to send and receive arbitrary data.
To send data, just query for domains like this:

dig TXT my-payload-1234.tunnel.mylab @any-dns-server
and whatever is inside the TXT record is the returned payload.
This is not limited to TXT records..

Test setup
I used 'iodine' for testing. It makes fine tuning of stuff like 'what record type to use' and 'what is the max size of my payload' quite easy. It also has a detection mode that tests automatically until the right configuration is found. First it tries to use port 53 for direct raw data connections. If that is not working, it degrades to real dns queries, tries different packet sizes and record types until it finds some working settings. It will then bring up a new interface called 'dns0' that you can use transparently within your normal software stack.

Playing with dns-tunnels is quite easy. All you need is a domain (one in your test lab will work) and a server to run the tunnel endpoint. Your zone file should look like this:
tunnel                   IN NS      nstun.mylab.
nstun                    IN A
In words: The domain 'mylab' has a subdomain 'tunnel'. The responsible nameserver for that subdomain is called 'nstun' and is reachable at ''.

The nameserver that is running on is actually the iodine server:
iodined -f -c tunnel.mylab
Make sure that the network gets somehow connected to the internet. In my setup I used iptables and source nat. But you may be able to use masquerading or something else.

Once that is working, you should be able to run the iodine client to start testing.

During my experiments tunnels via googles prominent dns first failed, but with the right settings:
iodine  -T TXT -m 768 -rfP password tunnel.mylab
it worked like expected.

Again, you need to setup the correct routing on your client to make it use the new interface. In this example is my 'normal' default gateway:
route add -host gw
route del default gw
route add default gw 

enable the unusability mode
While dns-tunnels are a nice option to use commercial hotspots for free, they do suck if you happen to run commercial hotspots.
Many captive portals, take chillispot as an example, redirect their users by manipulation on the ip layer. The user resolves a domain and receives the real ip address. If the user is not authenticated the hotspot system redirects requests to the real ip to the portal page where the user can obtain authorization.
You could argue that those captive portals should enforce usage of a special domain server that answers with the ip of the captive portal page until a user is authorized, but this could cause harm in devices that do dns caching. Even if you set the TTL down to a minimum.

So, to avoid problems in customer devices, those 'hotspots' will allow you to query arbitrary domains and they will give you real results.
Since DNS is required for a normal surf experience, it is no option to disable the service. Just limiting packet count per time is a dangerous option, since a visit to a modern news site without some sort of add-blocker (who does that anyway?!) can easily result in a $high_number of dns requests.
A pure iptables solution will not work or cause too many negative side effects.

This is where tc (traffic control) comes into play. What you need to do is to create a bucket for each client that might use dns-tunnels against you. A rate of 2kbit with a ceil of 6kbit seems to be good enough for normal user dns traffic, even in bursts. Then use iptables to mark the traffic for each client and use tc filter to send those marked packets through the specific bucket. This makes traffic on port 53 good enough for real dns, but not good enough to have a dns-tunnel capable of transferring more than a few pings.

# outgoing interface
# Then for each individual client ip with a unique ip_index:
$TC class add dev $DEV parent 1: classid 1:$ip_index htb rate 2kbit ceil 6kbit
$TC filter add dev $DEV protocol ip parent 1:0 prio 1 handle $ip_index fw flowid 1:$ip_index
$IPT -A POSTROUTING -t mangle -o $DEV -p udp --dport 53 -s $ip/32 -j MARK --set-mark $ip_index
$IPT -A PREROUTING -t mangle -i $DEV -p udp --sport 53 -d $ip/32 -j MARK --set-mark $ip_index

Questions, suggestions or feedback? Please let me know.

Talk to me

IT-Dienstleistungen Sven Tantau
Drostestrasse 3
53819 Neunkirchen
USt-Id-Nr.: DE203610693

skype: sven2342
phone: +49 22 47 90 80 250
mobile/signal: +49 157 3131 4424

OTR-Fingerprint: 7849BD93B65F9E4BC1206B06C09B7445721063BC
GPG/PGP-Key: (pub 4096R/069DD13C 2014-02-13) local copy
GPG/PGP-Key: fingerprint: 9BAD 94D3 9176 5BD1 F64F 542E 37E4 3542 069D D13C