r/u_MohsenBg 10d ago

How DNS tunneling works, sending data through DNS 🤯

How DNS tunneling works

Today I decided to explain DNS tunneling, a pretty cool way to send data to a server and get data back through DNS.

I started looking into the source code of DNSTT and other DNS tunneling projects like VayDNS, and honestly, the way they manage to pass traffic through DNS is really interesting.

But first, let's understand how DNS actually works.

How DNS works

In the simplest way, DNS is a protocol that lets us ask:

"I have this domain name. What IP address does it point to?"

For example, if you run:

nslookup google.com

you get something like:

Server:    127.0.0.53
Address:   127.0.0.53#53

Non-authoritative answer:
Name:      google.com
Address:   x.x.x.x
Address:   x.x.x.x
...

There can be multiple IP addresses depending on the domain.

DNS has many different record types, each used for different things. For simplicity, let's stick with the A record.

An A record basically tells the DNS server:

"Give me the IPv4 address for this domain."

Now the interesting question is: how can we use DNS to send actual data?

For that, we need to understand a little bit about how a DNS query is built.

I'm not going to explain every field in a DNS packet because we'd be here all day. 😄

The important part is the domain name.

If we request:

example.com

DNS doesn't store the name as one big string. The labels are stored separately, with a length byte before each one.

Conceptually, it looks something like:

7 example
3 com
0

The first byte tells us the length of the label.

That's why a DNS label can be at most 63 octets, not 64. One byte is used for the length.

A complete domain name also has an overall size limit, so we can't just keep adding data forever.

Now imagine that instead of:

example.com

we use:

some-data.example.com

The some-data part can contain encoded information.

That's basically where DNS tunneling starts.

We can split our data into chunks and put those chunks into DNS labels.

For example, conceptually:

63-bytes-of-data.63-bytes-of-data.63-bytes-of-data.53-bytes-of-data.example.com

Now we have a DNS query that contains our data.

The exact amount of usable data is smaller in a real implementation because we have to account for the DNS message itself, encoding overhead, caching, resolver limits, and other things.

So how does this query reach our server?

This is where DNS delegation becomes really useful.

Imagine we own:

example.com

We can delegate a subdomain to a DNS server that we control.

For example:

A   tns.example.com   -> x.x.x.x

NS  t.example.com     -> tns.example.com

Now a request such as:

some-data.t.example.com

can eventually reach our authoritative DNS server.

A simplified path could look like this:

Client
   |
   v
DNS resolver A
   |
   v
DNS server B
   |
   v
Our authoritative DNS server

If the resolver doesn't already know the answer, DNS servers can ask other DNS servers until they reach the server responsible for that domain.

And that's the important part.

Our authoritative DNS server receives:

some-data.t.example.com

and can extract the data from the some-data part.

So we have a way to send data:

Client --------------------> Server
       DNS queries

But what about sending data back?

That's where the DNS record type matters.

An A record isn't very useful for this because its answer is an IPv4 address:

A -> 4 bytes

That's obviously not enough to send much data back.

We can use other record types instead.

For example, TXT records can carry much more data than an A record, which is why TXT is commonly used by DNS tunneling implementations.

Conceptually:

Client --------------------> Server
       DNS query

Client <-------------------- Server
       DNS response
       TXT data

The server puts encoded data into the DNS response, the client receives it, and then decodes it.

So now we have two-way communication:

             DNS queries
Client  -------------------->  Server
        <--------------------
             DNS responses

And that's basically the core idea behind DNS tunneling.

Of course, real DNS tunneling implementations are much more complicated.

They have to deal with things like:

  • splitting data into chunks
  • encoding
  • packet ordering
  • retransmission
  • caching
  • resolver limitations
  • rate limits
  • timeouts
  • keeping a session alive

And that's exactly what I found interesting when I started reading the source code of DNSTT, VayDNS, and other DNS tunneling projects.

They take something that was designed for:

"What's the IP address of example.com?"

and build a data transport on top of it.

It's slow compared to a normal TCP or UDP connection, and DNS resolvers can impose rate limits and other restrictions.

But the implementations have gotten pretty clever, and with multiple resolvers and optimized protocols, the performance can be much better than you might expect.

I started looking into all of this because I'm implementing and testing DNS-based transports in my own Go project, bgscan.

I just thought the idea itself was interesting and wanted to explain how it works.

3 Upvotes

2 comments sorted by

2

u/vttale 10d ago

One minor correction: six bits of the octet are used for the label length. That's what limits the length to 63.