One of my favorite Cobalt Strike features is its ability to quietly manage a compromised system with DNS. Being rather proud of this feature, I talk about it a lot. During some conversations, I’ve heard the response “that’ll never work, we don’t allow port 53 out, unless it’s our internal DNS server”. To which I reply, “That does not matter, if I get code execution on a system that can resolve an internet host, then I can control that system”.
Here’s how:
Cobalt Strike ships with a DNS server. When I create a listener for the DNS Beacon, this server is started for me.
Sitting at a random place on the internet, this DNS server is useless. To have use, this server must become part of the “hierarchical distributed naming system for computers, services, and resources connected to the Internet” [ref]. The thing we lovingly refer to as the Domain Name System.
How do I make my server part of the DNS system? I register a domain and delegate my Cobalt Strike system as the authoritative DNS server for that domain (or some subdomain).
malwarec2.losenolove.com is authoritative for several losenolove.com subdomains
To communicate with me, a compromised system will make a DNS request for a host in a domain that I am authoritative for. Likely, this compromised system will send the DNS request to your internal DNS server. If a request is not in your internal DNS server’s cache, your server will either forward the request to another server or work to iteratively find out which DNS server may answer for the requested host. Since I’m authoritative for the domain the requested host is in, I get the request, and I get to provide the answer.
A query in action, try dig +trace whatever.somedomain.com
This capability is all well and good, but how do I use it? Cobalt Strike’s Beacon has two DNS communication strategies. Which strategy makes sense depends on your situation.
By default, DNS Beacon uses DNS as a beacon and HTTP as a data channel. Every sixty seconds (or some other user controlled time), the compromised system will make an A record request for an attacker controlled domain. Embedded in the requested hostname is a number, unique to that Beacon instance.
When Cobalt Strike’s DNS server gets the request, it checks if any tasks are available for that Beacon instance. If no tasks are available, it returns a response that tells Beacon to go to sleep.
If a task is available, the Cobalt Strike DNS server returns an IP address. The compromised system then connects to that IP address and makes an HTTP GET request to download its tasks. The tasks are encrypted.
This is the hybrid communication model. The idea here is that DNS requests on a regular interval are less likely to get noticed than, say, HTTP requests on a regular interval.
The hybrid beacon offers a quieter way to hold access to a compromised system than other tools in the pen tester’s arsenal. But, what happens when DNS is the only way out?
DNS Beacon may use DNS as a data channel. Every sixty seconds (or, again, some user controlled time), the compromised system makes an A record request for an attacker controlled domain. Again, embedded in this request is a number, unique to that Beacon instance.
If a task is available and the user instructed that Beacon to use DNS as a data channel, the Cobalt Strike DNS server returns an IP address with special meaning to the Beacon agent. The compromised system then makes several requests to an attacker controlled domain to push metadata about the compromised host, download tasks, and upload any output.
How does this happen? Well, let’s take a look at an A record request. An IP address is a 4-byte integer. Right? If a compromised system makes 100 requests for hosts in a domain the attacker is authoritative for, then the attacker can push 400 bytes of data.
A records don’t carry a lot of information, but there are alternatives. For example, a AAAA record for an IPv6 address is 16 bytes of information. A TXT record is up to 255 printable characters.
So far, this communication is one way. The compromised system is making many requests to an attacker controlled domain to download information. How do we go the other way?
The requested host is Beacon’s opportunity to send information.
To post output, the Beacon agent encodes its encrypted output into a DNS-safe form and makes a request for [encoded output].someattackercontrolleddomain.com. Each request can only hold so much data. Beacon makes as many DNS requests as necessary to send data back to me.
Of course, there are things that can go wrong. For example, if Beacon constantly requests the same host, another server will cache the response. To manage this, Beacon encodes a nonce into each request. It’s also possible for a query attempt to timeout, disrupting the transaction. Cobalt Strike’s DNS communication code is written to detect this situation and recover from it.
By making requests to an attacker controlled domain, it’s possible to indirectly control a compromised system–egress restrictions be damned.
When I added DNS as a data channel, my intent was to provide a fallback option for situations where the compromised system can’t download tasks over HTTP. The DNS data channel allows you to recover your Beacon in these situations. You may switch between the hybrid DNS/HTTP communication and pure DNS communication as needed.
While not the subject of this post, Cobalt Strike also includes a stager that will download Beacon via DNS TXT record requests and inject it into memory.
Are there mitigations and technologies to detect or stop this form of communication? Absolutely. If you have these mitigations in place, have you tested them? If you have a process to detect anomalous DNS traffic and act on it, have you exercised it? This is the point of a penetration test. To test your assumptions against a thinking attacker. The purpose of a threat emulation tool, like Cobalt Strike, is to arm penetration testers with tools to execute these attacks.