Using Iodine for DNS Tunneling C2 to Bypass Egress Filtering
Security-conscious organizations will often restrict the types of traffic allowed out of their networks. Protocols or ports deemed unnecessary for the majority of the organization’s users will simply be blocked, with whitelists established for the few users who may have a business need to get around those restrictions. Restricting outbound traffic in this manner is a practice known as egress filtering, and it can be an effective security control that makes establishing Command and Control (C2) channels much more difficult for attackers.
Let’s assume you want to gain a foothold on an internal network and tunnel traffic back out to your C2 server. Typically, you’d probably choose a channel like HTTPS, since HTTPS traffic will almost certainly be common in any organization. The layer of encryption can make traffic inspection more difficult (though some organizations do perform SSL/TLS inspection). Alternatively, maybe you’d try to establish an SSH tunnel from your foothold to your C2 server for ease of use.
In both these cases, the use of egress filtering and web proxies can foil your plans. Even if HTTPS is allowed out, if your implant isn’t proxy-aware, its tunnel won’t go through the web proxy and will fail to reach your C2 server. Similarly, if SSH isn’t allowed out except for the few users who need it, you won’t get your tunnel (unless you specifically compromise one of those users).
A particularly challenging aspect of bypassing egress filtering is the “blind” nature of things; without preexisting knowledge of the target environment, you’re forced to guess what protocols will be allowed outbound. Therefore, it makes sense to choose the protocols that will have the best chance of working on the first try.
One egress method with a chance of succeeding where others fail is DNS tunneling. In this method, data is exchanged over DNS queries and responses. Since DNS is typically seen as a fairly innocuous protocol, it may be allowed outbound where other protocols, like SSH or HTTPS, will be blocked or sent through a proxy.
In order to overcome the challenge of restrictive egress filtering in assessments, we’ve experimented with a variety of DNS tunneling tools. We ended up settling on the tool Iodine, a DNS tunneling utility written in C that runs on a variety of platforms, including Linux, Windows, and macOS.
We’ve found Iodine to be reasonably stable and performant, well-documented, and easy to set up. Therefore, in this post we’ll examine some considerations to be aware of when preparing to use Iodine in an assessment.
Iodine in Action
Before moving on to considerations involved with using Iodine, let’s see what it looks like in action.
In the video above, the Iodine server component is run on the C2 server. The client then connects to the server, establishing the DNS tunnel. (Note that we’re using a real domain for this, but I’ve changed it in the video and redacted the server IP.)
The server and client components each create a dns0 interface, which uses the 10.0.0.0/24 subnet. Upon client connection, we can see that the IP address 10.0.0.2 has been assigned to the client machine. It’s then possible to connect via SSH to the client, effectively establishing control of the client over a DNS tunnel.
Brief Overview of DNS Tunneling
A very basic overview of DNS tunneling is provided in the diagram below.
Essentially, all data is exchanged through DNS queries and responses; data is encoded in the hostname requests and the server’s responses, and will be decoded into something meaningful by the DNS tunneling tool. Because the size of DNS queries and responses is fairly limited compared to other protocols commonly used for C2, DNS tunnels need to send more traffic to achieve the same result and are thus slower.
Why did we choose Iodine over dnscat or a C2 framework that featured DNS tunneling? There are lots of tools that offer DNS tunneling capabilities, but Iodine proved to be stable, easy to set up, and performant. Some other tools we tried had stability issues or were too slow to realistically use. We didn’t want our testing constantly interrupted by a slow or unstable tunnel – those might be acceptable conditions for occasional data exfiltration, but certainly not for conducting a lengthy pentest.
Iodine does have some weaknesses, such as not encrypting its traffic. It’s important to consider this and use something like SSH within the DNS tunnel to keep traffic encrypted and safe from prying eyes. Overall, Iodine’s positive traits outweighed the few negatives, and we found it suited our purposes well.
To DNS tunnel with Iodine, you’ll need three things:
- A domain name to use for the DNS channel. Ensure that you understand how to manage DNS settings through your chosen DNS registrar, since this will be important for setup. We’ll discuss considerations regarding the domain you choose later in this post
- A server with a public IP address to host the Iodine server component. When you configure DNS records through your domain’s registrar, you’ll point them to this IP address
- Some method of installing the Iodine client on a host in the target’s internal network. For internal pentests, often a host is provisioned as an initial foothold, so most likely you’ll install the Iodine client on that host
Setup and Operational Considerations
You can find the Iodine setup documentation on the official repo. Because the documentation itself is detailed, and because the DNS configuration portion of setup will vary slightly depending on your chosen DNS registrar, there’s little point in reproducing complete setup instructions here. However, here are a few quick notes on setup that may help you avoid common pitfalls:
- If you currently have a DNS server running on your C2 server, you’ll need to move it to another port. The Iodine server component will act as a DNS server and handle incoming requests from the client
- As already mentioned, your exact DNS record setup will vary a bit depending on your chosen registrar. The general idea is to have one A record that points to your C2 server’s public IP address. This A record doesn’t have to be the only one; you can have one for the domain name, and another for a subdomain. You’ll then want an NS record that’s managed by the A record you just set up. The NS subdomain will be the one to which DNS requests are sent from the Iodine client. Here’s an example of how we set up records in Cloudflare to work with Iodine (with the domain name and subdomains redacted):
- It’s in your best interest to make your NS subdomain short (1-3 letters). Since data from the client is sent as a subdomain in each DNS request, you’ll want as much space as possible to be available for the data. The longer the NS subdomain, the less space there is for the client to place its data, which means it’ll need to send more requests and your tunnel will be slower
Next, let’s examine some operational considerations related to Iodine that we’ve discovered through our use of the tool.
Choice of domain: A useful trick when choosing a domain is to purchase a recently-expired domain, rather than getting a brand-new one. This technique was described in detail by Black Hills Information Security in this blog post. This approach is useful because it can help avoid detection based solely on the recency of a domain; often, domains that have been recently registered aren’t trusted, since getting a new domain is so common for setting up C2 infrastructure. Therefore, there’s a chance a brand-new domain will be blocked altogether by an organization, or may be allowed but will trip alerts. Before purchasing an expired domain, you may want to use a tool like Domain Hunter to examine the domain’s reputation.
Subnet choice for DNS interface: When selecting a subnet range for the Iodine server component to use, note that the Iodine client will create a new DNS interface that uses that subnet when it establishes the tunnel. For example, if on the server you choose 10.0.0.1 as the address, a DNS interface will be added that resembles the below screenshot.
When Iodine is run on the client, the client will establish a DNS interface that resembles the screenshot below.
Ordinarily this is perfectly fine, but keep in mind that it’s probably best to avoid conflicts with subnets that exist inside the internal network. If you’re lucky enough to have any knowledge of the internal network’s subnetting, try not to use an existing subnet range (though probably you’ll be forced to guess). Additionally, as noted in the Iodine documentation, make sure that the IP you choose for the server is in a subnet range that isn’t yet in use. For example, if the internal subnet 10.0.0/24 is already in use on the server, you shouldn’t also try to make use of it with Iodine. Pick a subnet that will only be used by Iodine.
Prepare the foothold box as much as possible (if you can): Iodine works well for moment-to-moment pentesting activity, but a DNS tunnel still offers limited bandwidth. Therefore, download-heavy operations such as installing new tools or dependencies can be quite time-consuming. Long downloads often saturate the tunnel to the degree that it’s too slow to do anything else in the meantime. This is frustrating and slows down an operation. If you have control of the foothold box before it’s placed in the internal network, be sure to install every tool you think you might reach for during the assessment; it’s better to err on the side of having too many tools than to stop repeatedly to install things you need.
As you might imagine, sending traffic over a DNS tunnel incurs a significant performance hit compared to SSH or HTTPS. However, Iodine performs well enough that working through the tunnel by using SSH is still entirely viable under most circumstances. You can see a few basic commands and their response times in the short video below.
As you may be able to tell, there’s a slight delay with some commands, and loading larger files can hitch a little, but the tunnel is more than responsive enough for conducting a pentest.
However, there are some caveats to keep in mind. As noted in an earlier section, trying to install packages and dependencies over DNS is an exercise in frustration, and in our experience has saturated the DNS tunnel to such a degree that it was impossible to use it for anything else during a large download.
Also consider that if you want to have multiple users connecting through the DNS tunnel at the same time, performance will degrade further. We’ve had two users making extensive use of a DNS tunnel simultaneously without much issue, but adding on more will probably have a noticeable impact. If you plan to exfiltrate data over the tunnel, be sure to think through how long that’s likely to take. In our experience, commands that return massive amounts of output will also feel sluggish over DNS.
A Word about Detection
DNS tunneling can be an effective method of bypassing egress proxies, but it’s certainly not impossible to detect. The SANS Information Security Reading Room offers multiple papers related to detecting DNS tunneling. There’s a Snort rule designed to detect a handshake generated by Iodine, and almost certainly other IDSs offer similar detection capabilities. Because DNS tunneling data payloads are embedded directly in the DNS queries, anyone examining the DNS traffic will see excessively long DNS queries and likely become suspicious. Additionally, Iodine doesn’t encrypt its traffic, so it can’t hide exactly what data is getting exchanged the way an HTTPS channel would.
As an attacker, be cognizant that DNS tunneling does not make you invisible. If your DNS tunnel is your only channel to your internal foothold, it’s probably beneficial to compromise something else in the environment and establish another C2 channel on that, preventing your foothold from becoming a single point of failure.
As a defender, it may be worthwhile to experiment with Iodine and similar DNS tunneling tools. Use them to test and tune your detection capabilities, and determine if you receive timely alerts related to potential DNS tunneling activity.
To briefly summarize some considerations regarding using Iodine for DNS tunneling:
- DNS tunneling can be a useful method of bypassing restrictive egress filtering
- You need a domain name, server with a public IP address, and target host on which to install the Iodine client
- To increase your chances of success, acquire a recently-expired domain name instead of a brand-new one
- If possible, choose a subnet for the Iodine tunnel that isn’t likely to be in use on the internal network
- If you have access to host that’ll run the Iodine client before you begin your operation, install every tool you think you might need to avoid dealing with slow downloads over DNS
- DNS tunneling is very much possible to detect; be aware of this as an attacker, and deploy DNS tunneling utilities to hone your detection capabilities as a defender
DNS tunneling is a useful technique to add to your arsenal. Consider employing it next time you find yourself up against restrictive egress filtering.