DNS Tunneling Fundamentals
IP addresses, not human-readable names,
are used to route Internet data.
Whenever we want to access a domain by its domain name,
we need to first obtain its IP address.
To retrieve, or resolve the IP address of a human-readable address,
we need to ask various DNS servers.
In our lab network,
with MULTISERVER03 as the DNS server,
a request from PGDATABASE01 for the IP address of www.example.com
would follow the flow shown here.
The firewalls have been removed from this diagram for simplicity.
Let’s try out a new scenario in the lab,
which is configured precisely for this purpose.
In this scenario, we have a new server: FELINEAUTHORITY.
This server is situated on the WAN alongside our Kali machine.
This means that MULTISERVER03,
CONFLUENCE01,
and our Kali machine can route to it,
but PGDATABASE01 and HRSHARES cannot.
FELINEAUTHORITY is registered within this network
as the authoritative name server for the feline.corp zone.
We will use it to observe how DNS packets reach an authoritative name server.
In particular,
we will watch DNS packets being exchanged between PGDATABASE01 and FELINEAUTHORITY.
While PGDATABASE01 cannot connect directly to FELINEAUTHORITY,
it can connect to MULTISERVER03.
MULTISERVER03 is also configured as the DNS resolver server for PGDATABASE01.
In order to see how DNS requests will be relayed
to FELINEAUTHORITY from PGDATABASE01,
we need to initiate DNS requests from PGDATABASE01,
and monitor what comes in to FELINEAUTHORITY.
For that reason, we need a shell on each of these machines.
As in previous examples,
we can only access PGDATABASE01 through CONFLUENCE01.
So in order to connect to the SSH server on PGDATABASE01,
we must pivot through CONFLUENCE01.
We’ll compromise CONFLUENCE01 by exploiting CVE-2022-26134
with our reverse shell payload,
and create an SSH remote port forward
to relay a port on our Kali machine
to the SSH service on PGDATABASE01.
We’ll then SSH into PGDATABASE01 as the database_admin user.
Since FELINEAUTHORITY is also on the WAN,
we can SSH directly into FELINEAUTHORITY using the username kali
and the corresponding password.
We now have two open shells.
The first is on PGDATABASE01 as the database_admin user,
and the second is on FELINEAUTHORITY as the kali user.
In order to simulate a real DNS setup,
we can make FELINEAUTHORITY a functional DNS server
using Dnsmasq.
Dnsmasq is DNS server software that requires minimal configuration.
A few Dnsmasq configuration files are stored in the ~/dns_tunneling folder,
which we’ll use as part of our DNS experiments.
For this initial experiment,
we’ll use the very sparse dnsmasq.conf configuration file.
This configuration ignores the /etc/resolv.conf
and /etc/hosts files
and only defines the auth-zone and auth-server variables.
These tell Dnsmasq to act as the authoritative name server
for the feline.corp zone.
We have not configured any records so far.
Requests for anything on the feline.corp domain
will return failure responses.
Now that the configuration is set,
we’ll start the dnsmasq process with the dnsmasq.conf configuration file,
making sure it runs in “no daemon” mode so it runs in the foreground.
We can kill it easily again later.
In another shell on FELINEAUTHORITY,
we’ll set up tcpdump to listen on the ens192 interface
for DNS packets on UDP/53,
using the capture filter udp port 53.
Now that tcpdump is listening and Dnsmasq is running on FELINEAUTHORITY,
we will move to our shell on PGDATABASE01.
From here we will make DNS queries aimed at the feline.corp domain.
First let’s confirm PGDATABASE01’s DNS settings.
Since DNS resolution is handled by systemd-resolved,
we can check the DNS settings using the resolvectl utility.
PGDATABASE01’s DNS server is set to 10.4.50.64,
which is MULTISERVER03.
It will query MULTISERVER03 any time it needs a domain name resolved.
But PGDATABASE01 has no outgoing network connectivity,
so it can’t communicate directly with FELINEAUTHORITY or our Kali machine.
As an experiment, let’s use nslookup
to make a DNS request for exfiltrated-data.feline.com.
This returns an NXDOMAIN response that indicates the DNS request failed.
This is expected though,
as we haven’t configured our DNS server to actually serve any records.
The tcpdump program on FELINEAUTHORITY captured DNS packets from MULTISERVER03.
In this case, we’ve received a DNS A record request
for exfiltrated-data.feline.corp on FELINEAUTHORITY.
This happened because MULTISERVER03
determined the authoritative name server for the feline.corp zone.
All requests for any subdomain of feline.corp
will be forwarded to FELINEAUTHORITY.
We didn’t tell Dnsmasq on FELINEAUTHORITY
what to do with requests for exfiltrateddata.feline.corp
so Dnsmasq just returned an _NXDomain__ response.
We can see this flow in the following diagram.
The steps where MULTISERVER03 sent queries to the root name servers
and TLD name server have been omitted here for simplicity…
But in a normal network situation,
these steps would precede the request made to FELINEAUTHORITY.
An arbitrary DNS query from an internal host,
with no other outbound connectivity,
has found its way to an external server we control.
This may seem subtle,
but it illustrates that we can transfer small amounts of information,
exfiltrated data,
from inside the network to the outside,
without a direct connection, just by making DNS queries.
Now that we have covered the process of exfiltrating data from a network,
let’s consider how we might infiltrate data into a network.
The DNS specification includes various records.
We’ve been making A record requests so far.
An A record response contains an IPv4 address for the requested domain name.
But there are other kinds of records,
some of which we can use to smuggle arbitrary data into a network.
One of these is the TXT record.
The TXT record is designed to be general-purpose,
and contains “arbitrary string information”.
We can serve TXT records from FELINEAUTHORITY using Dnsmasq.
First, we’ll kill our previous dnsmasq process with a Ctrl+c.
Then we’ll check the contents of dnsmasq_txt.conf
and run dnsmasq again with this new configuration.
The dnsmasq_txt.conf contains two extra lines starting with “txt-record=”.
Each of these lines represents a TXT record that Dnsmasq will serve.
Each contains the domain the TXT record is for,
then an arbitrary string attribute, separated by a comma.
From these two definitions,
any TXT record requests for www.feline.corp should return the strings
“here’s something useful!” and “here’s something else less useful.”.
Let’s test this hypothesis.
Back on PGDATABASE01,
we’ll make a request for TXT records for www.feline.corp
with nslookup by passing the -type=txt argument.
Success!
We received the arbitrary string attributes
that were defined in dnsconfig_txt.conf.
This is one way to get data into an internal network using DNS records.
If we wanted to infiltrate binary data,
we could serve it as a series of Base64 or ASCII hex encoded TXT records,
and convert that back into binary on the internal server.
In this section, we discussed how we might infiltrate or exfiltrate data
through various types of DNS records.
In the next section we’ll get some hands-on experience
with the dnscat2 framework,
which leverages these techniques to create a multipurpose DNS tunnel.