From e26731b712f9bcf6cd4e2df2243fab5832f9eb5f Mon Sep 17 00:00:00 2001 From: Anthony Ettinger Date: Tue, 28 Jul 2026 17:44:52 +0000 Subject: [PATCH] =?UTF-8?q?docs(prd):=20add=20PRD=200011=20=E2=80=94=20Cat?= =?UTF-8?q?ch=20the=20exfiltration=20channel=20that=20survives=20egress=20?= =?UTF-8?q?filtering?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit DNS is the one protocol almost nobody blocks, which is why it is the durable channel for C2 and data exfiltration. A compromised process encodes data into subdomain labels and reads instructions from the answers, and no outbound TCP connection appears anywhere. That makes it the blind spot in the coverage the other modules give: network-monitor sees a connection to the local resolver and nothing more, log-watcher sees nothing, and egress rules permit it by design. The signatures are distinctive enough to justify a narrow module — long high-entropy labels, TXT-heavy traffic, query rates no human browsing produces, and DGA bursts with high NXDOMAIN rates. Encrypted DNS defeats the whole thing, which is itself worth reporting as posture: the module should say when it is blind. Co-Authored-By: Claude Opus 4.8 --- ...-channel-that-survives-egress-filtering.md | 145 ++++++++++++++++++ prd/README.md | 1 + 2 files changed, 146 insertions(+) create mode 100644 prd/0011-catch-the-exfiltration-channel-that-survives-egress-filtering.md diff --git a/prd/0011-catch-the-exfiltration-channel-that-survives-egress-filtering.md b/prd/0011-catch-the-exfiltration-channel-that-survives-egress-filtering.md new file mode 100644 index 0000000..37eb965 --- /dev/null +++ b/prd/0011-catch-the-exfiltration-channel-that-survives-egress-filtering.md @@ -0,0 +1,145 @@ +--- +openprd: "0.2" +id: "0011" +title: "Catch the exfiltration channel that survives egress filtering" +status: Draft +authors: + - anthony@profullstack.com +created: 2026-07-28 +updated: 2026-07-28 +repo: profullstack/threatcrush +discussion: +implementation: +tags: dns-monitor, exfiltration, tunneling, dga, c2, modules +supersedes: +superseded-by: +--- + +## Problem + +DNS is the one protocol almost nobody blocks. A host that cannot reach the +internet on any other port can usually still resolve names — which is precisely +why DNS is the durable channel for **command-and-control and data +exfiltration**. A compromised process encodes data into subdomain labels, +queries a domain the attacker controls, and reads instructions back from the +answers. No outbound TCP connection appears anywhere. + +This makes DNS the blind spot in the coverage the other modules provide: +`network-monitor` sees a connection to the local resolver and nothing more; +`log-watcher` sees nothing at all; egress firewall rules permit it by design. + +The detectable signatures are distinctive, which is what makes a narrow module +worthwhile: + +- **Tunneling**: abnormally long or high-entropy labels, unusual record types + (`TXT`, `NULL`), and a query rate to one domain far above anything a human + browsing produces. +- **DGA**: bursts of algorithmically-generated names with high `NXDOMAIN` rates, + as malware hunts for whichever rendezvous domain is currently registered. +- **Newly-observed domains** contacted by a server process that has resolved the + same handful of names for months. + +## Goals + +- DNS tunneling from a compromised host is detected within minutes, before a + meaningful volume of data leaves. +- DGA behaviour is distinguished from ordinary resolution failure. +- A **new domain** contacted by a server workload is surfaced, since servers + have far more predictable resolution patterns than user machines. +- Operates without a resolver plugin where possible, so it works on hosts using + systemd-resolved, dnsmasq or a cloud resolver. + +## Non-Goals + +- **Not a DNS server or filtering resolver.** Pi-hole and NextDNS block; this + observes. +- **Not blocklist-based content filtering.** Ad and tracker blocking is a + different product. +- **Not DNSSEC validation.** +- **Not blocking** — `firewall-rules` (PRD 0010) acts if configured. + +## Users + +- **Solo operators** running application servers, whose DNS should be boring + and predictable. +- **Platform engineers** watching for C2 across a fleet. +- **Incident responders** determining whether data left, and where to. + +## Requirements + +### Observe + +- R1 [P0] **Query capture** from whichever source is available: systemd-resolved + logs, dnsmasq logs, a local resolver's query log, or packet capture on port 53 + where permitted. **State which source is in use** — coverage differs sharply + between them and an operator must know what is not being seen. +- R2 [P0] Extract query name, type, response code and the querying process where + the source allows attribution. +- R3 [P0] Honest degradation: if no source is available the module reports that + it cannot observe, rather than reporting nothing found. + +### Detect + +- R4 [P0] **Tunneling heuristics**: label length above a threshold, high entropy + in labels, sustained query rate to a single registered domain, and a high + proportion of `TXT`/`NULL` records. Individually weak, jointly strong — + require a combination before alerting. +- R5 [P0] **DGA detection**: `NXDOMAIN` rate and burst pattern, plus character + distribution unlike natural language. +- R6 [P1] **New-domain detection** against a learned baseline, weighted by the + querying process. A database process resolving a new domain is far more + interesting than a browser doing so. +- R7 [P1] **Volume anomaly** per process and per domain against baseline. +- R8 [P2] Optional threat-intelligence correlation for known C2 domains, kept + optional because feeds age badly and create a false sense of coverage. + +### Report + +- R9 [P0] Route through `alert-system` (PRD 0006), leading with **what left**, + estimated where possible: query count, bytes encoded, destination domain. +- R10 [P1] Dedupe per (domain, detector) so a long-running tunnel produces one + incident with escalation, not one alert per query. + +## UX Notes + +```bash +threatcrush dns-monitor top # most-queried domains, by process +threatcrush dns-monitor baseline +threatcrush dns-monitor source # what is being read, and what is missed +``` + +``` +[CRITICAL] dns-monitor · probable DNS tunnel + + domain: x7k2.exfil.example queries: 4,182 in 11m + labels avg 47 chars, entropy 4.6 bits/char, 96% TXT + process: node (pid 3311) + + Roughly 180 KB encoded outbound. This does not resemble name resolution. + + Now: isolate the process. DNS is not blocked by your egress rules. +``` + +## Success Metrics + +- Tunneling detected within 5 minutes on a fixture generating iodine/dnscat-style + traffic. +- Zero alerts from ordinary resolution — CDNs, package registries, telemetry — + over a week on a normal host. +- DGA fixture detected with <1 false positive per host per week. +- The active capture source is always reported, including "none available". + +## Risks & Open Questions + +- **Query attribution to a process is the hard part** and is unavailable from + most log sources. Without it, "a new domain was resolved" is much weaker. + **Open:** is eBPF attribution worth the privilege and portability cost? +- **Legitimate services look like tunnels.** Some CDNs, anti-virus telemetry and + certificate-transparency lookups use long, high-entropy, high-rate DNS. An + allowlist will be necessary and will need maintaining. +- **Encrypted DNS defeats this entirely.** A host using DoH to a third party + bypasses every local observation point. That is worth reporting as a posture + finding in its own right — the module should say when it is blind. +- **Packet capture needs privilege**, and log sources vary by distribution. + Coverage will be uneven across hosts, so the fleet view must show which hosts + are actually observable rather than implying uniform coverage. diff --git a/prd/README.md b/prd/README.md index 2bfd8f9..c4e0dd9 100644 --- a/prd/README.md +++ b/prd/README.md @@ -23,3 +23,4 @@ these numbered PRDs cover individual changes to it. | [0008](./0008-notice-what-changed-on-the-network-not-everything-on-it.md) | Notice what changed on the network, not everything on it | Draft | network-monitor, listeners, egress, port-scan, baseline, modules | | [0009](./0009-read-the-logs-for-evidence-including-the-logs-that-stopped.md) | Read the logs for evidence, including the logs that stopped | Draft | log-watcher, logs, web-attacks, tampering, gaps, modules | | [0010](./0010-block-attackers-without-locking-out-the-operator.md) | Block attackers without locking out the operator | Draft | firewall-rules, blocking, nftables, iptables, containment, modules | +| [0011](./0011-catch-the-exfiltration-channel-that-survives-egress-filtering.md) | Catch the exfiltration channel that survives egress filtering | Draft | dns-monitor, exfiltration, tunneling, dga, c2, modules |