DNS-over-HTTPS C2
Malware tunnels its DNS-based C2 through DoH resolvers, wrapping resolution in HTTPS so on-network DNS inspection and sinkholing never see the queries.
DNS-over-HTTPS (DoH) carries ordinary DNS queries inside HTTPS requests to a resolver endpoint such as https://cloudflare-dns.com/dns-query or https://dns.google/dns-query. For a defender, this collapses two of their best DNS controls at once: the queries no longer traverse UDP/53 where passive DNS sensors and egress filters watch, and they no longer hit the organisation's recursive resolver, so RPZ sinkholes and query-volume baselines never see them. The lookups vanish into a stream of HTTPS to a major provider.
Malware uses DoH to harden the same channels it already abuses — domain-generation algorithms, DNS tunneling, and plain domain resolution for fast-flux C2 — by hiding the resolution step. Godlua was the first documented family to adopt DoH; PsiXBot moved its C2 lookups to Google's DoH endpoint, and DoH increasingly fronts DGA-driven resolution in modern loaders. Because the queries are encrypted to a trusted third party, the only on-network signal is the connection to the DoH endpoint itself.
For an analyst the task is to recover the DoH endpoint URL and the query content, then pivot detection onto the destination (a known DoH resolver reached by a non-browser process) rather than the now-invisible DNS payload.
How it works
The implant builds a DNS query message, base64url-encodes it (or sends it as application/dns-message via POST), and issues an HTTPS request to a DoH endpoint, parsing the answer out of the response body:
// Illustrative DoH resolution — descriptive, not deployable.
// A DNS query is wrapped in HTTPS to a DoH endpoint.
void resolve_over_doh(const char *qname)
{
uint8_t wire[512];
size_t n = build_dns_query(qname, TYPE_TXT, wire); // raw DNS message
char b64[1024];
base64url_encode(wire, n, b64); // RFC 8484 GET form
char url[1280];
snprintf(url, sizeof url,
"https://dns.google/dns-query?dns=%s", b64);
uint8_t resp[2048];
https_get(url, resp, sizeof resp); // answer in body
parse_dns_answer(resp); // TXT carries tasking
}Tells for a reverser: a hard-coded DoH endpoint URL, a Content-Type: application/dns-message or ?dns= parameter, base64url helpers feeding raw DNS wire format, and an Accept: application/dns-message header. The endpoint string and the wire-format builder together prove the implant resolves out-of-band rather than through the system resolver.
Detection & analysis
Static analysis:
- Recover the DoH endpoint URL(s). A hard-coded
*/dns-querypath, theapplication/dns-messagemedia type, or a?dns=query parameter is conclusive: the binary speaks RFC 8484 DoH. Multiple endpoints suggest resolver failover. - Identify the DNS wire-format builder feeding a base64url encoder. A function that assembles DNS headers and questions but hands them to an HTTP client rather than a UDP socket is the signature of DoH, and reveals the record types (often
TXT) used for tasking. - Determine whether resolution bypasses the system resolver entirely. An implant that constructs DNS messages itself and never calls
getaddrinfo/DnsQueryis deliberately evading on-network DNS visibility.
Dynamic analysis:
- Detonate with TLS interception. Decrypted, the
application/dns-messagebodies expose the actual queries and answers — decode them to recover tunneled data or DGA domains. Without interception, the only tell is HTTPS to a DoH endpoint from a process that is not a browser or the OS resolver. - Fingerprint the destination: maintain a list of public DoH endpoints (Cloudflare, Google, Quad9, NextDNS and others) and their JA3/JA3S, and alert when a workstation reaches one directly instead of through its configured resolver. Compare request cadence against DGA/beacon timing.
- Baseline which processes legitimately use DoH (the browser, the OS resolver service). Any other process issuing
/dns-queryrequests is anomalous.
Detection rule hint:
Maintain an allow-list of sanctioned DoH endpoints and the processes permitted to reach them, then alert on HTTPS requests to any */dns-query path or with Content-Type: application/dns-message originating from a non-browser, non-resolver process — and on direct connections to known public DoH resolver IPs from hosts whose policy routes DNS through an internal recursive resolver. Out-of-policy DoH from an unexpected process is the marker of DoH-based C2.