Opened 4 months ago

Last modified 3 months ago

#25874 new project

DNS-based rendezvous for Snowflake

Reported by: dcf Owned by:
Priority: Medium Milestone:
Component: Obfuscation/Snowflake Version:
Severity: Normal Keywords:
Cc: dcf, arlolra Actual Points:
Parent ID: Points:
Reviewer: Sponsor:


From #25594:
An idea to use DNS over HTTPS:!topic/traffic-obf/ZQohlnIEWM4

The circumvention idea is to take any existing DNS tunneling scheme and send it through DNS over HTTPS. To be a bit more specific: you send recursive DNS queries (encoding your upstream traffic) to the DNS-over-HTTPS server, which then forwards the queries to another specialized server that decodes them and proxies the data they contain.

Even if not a general-purpose transport, DNS-over-HTTPS could be an ideal rendezvous mechanism for a system like Snowflake or Moat. One where you only need to send/receive a small amount of very hard-to-block data in order to bootstrap a connection.

The way I see it, there are two parts of this:

  1. Using DNS as an underlying transport: the client sends a DNS request containing its encoded offer; the broker sends back a DNS response containing an encoded proxy answer.
  2. Sending via DNS-over-HTTPS in order to avoid blocking of the DNS messages themselves.

Child Tickets

Change History (4)

comment:1 Changed 4 months ago by dcf

For testing purposes, I set up to be the authoritative nameserver for the subdomain (I think; I'm not too good at this DNS stuff.) There is no responder running there yet, but you can use tcpdump on the broker to watch requests arrive:

tcpdump -n -X port 53

Then, from somewhere else, try a normal DNS query. In the tcpdump you should see requests arrive from your ISP's recursive nameserver.

dig message${RANDOM}

Here is sample Python 2 code for doing requests over the DNS-over-HTTPS server.

#!/usr/bin/env python

NAME = ""

from scapy.all import *
import base64
import requests

print("POST application/dns-udpwireformat")
udpwireformat = str(DNS(rd=True, qd=DNSQR(qtype="A", qname=NAME)))
r ="",
    headers = {
        "Accept": "application/dns-udpwireformat",
        "Content-Type": "application/dns-udpwireformat",
    data = udpwireformat,

print("POST application/dns-udpwireformat")
udpwireformat = str(DNS(rd=True, qd=DNSQR(qtype="A", qname=NAME)))
r = requests.get("",
    params = {
        "dns": base64.urlsafe_b64encode(udpwireformat),
        "ct": "application/dns-udpwireformat",

print("GET application/dns-json")
r = requests.get("",
    params = {
        "name": NAME,
        "type": "A",
        "ct": "application/dns-json",
print r.text

If you run this, you will see requests arrive at the broker and responses come back with rcode=server-failure, which is expected because there's nothing running at yet.

comment:2 Changed 4 months ago by sysrqb

If it gains in popularity, DNS-over-TLS is another option. There are already quite a few providers[1], but I doubt any of them are sufficient for domain fronting at this point, although I see Cloudflare is now on the list.


comment:3 Changed 4 months ago by cypherpunks

Bulletproof transport. Joke, based on real life.

comment:4 Changed 3 months ago by cypherpunks

To be considered: rendezvous over dns makes enumeration easier.

Note: See TracTickets for help on using tickets.