# TLS Desync via Fake ClientHello

A client-side technique for getting past SNI-inspecting DPI middleboxes without a tunnel. Instead of hiding what you're connecting to, you confuse the middlebox about what it already saw.

## Mechanism

The same TCP connection carries two ClientHello packets, in order:

1. A **fake** ClientHello with `SNI = www.google.com` (or any whitelisted hostname), TTL set low — typically 8.
2. The **real** ClientHello with the actual SNI, normal TTL.

The fake reaches the in-path DPI box, which records "this connection is going to google.com" and stops paying attention. The TTL expires before the packet reaches the real server, so the server never sees it. The real ClientHello arrives at the server normally, but the DPI's per-connection state is already poisoned — by the time it sees the real SNI, it's already decided to let the flow through.

Two assumptions have to hold:

- **The DPI inspects only the first ClientHello-shaped TCP segment.** If it tracks the whole flow, the real SNI catches up.
- **TTL is tunable enough to fit between the middlebox and the server.** TSPU and similar boxes sit at the ISP, so a TTL of 8 lands somewhere between them and any non-trivial Internet destination. `traceroute` is the tuning tool.

## Companion: MSS clamping for fragmentation

The technique is usually paired with MSS clamping. The kernel is told to use a small TCP_MAXSEG (88 bytes in [[gecit]]'s default), which forces the real ClientHello to fragment across multiple TCP segments. Naive DPI parses only the first segment, where the SNI extension is now truncated. So even a DPI that *would* re-examine the flow can't find the SNI field. This is the same trick zapret/GoodbyeDPI/ByeDPI rely on, and it works against a different class of middlebox than the desync — many real deployments need both.

## Sequence and ack numbers must be real

The fake has to ship with the live connection's `snd_nxt` / `rcv_nxt`. DPI boxes that bother to track sequence space drop fakes that don't fit the window. On Linux, `bpf_sock_ops` exposes those fields directly via the `BPF_SOCK_OPS_ACTIVE_ESTABLISHED_CB` callback. On platforms without an in-kernel hook, you have to scrape SYN-ACKs off the wire with pcap, which is racier and fails more often.

## Where the technique sits in the bypass landscape

This is a **client-side** bypass: you do not get a foreign exit IP. The destination IP is unchanged, and the censor's IP-block list is untouched. So this only works against blocks where the censor's *only* signal is the SNI. Against the [[whitelist-internet-blocking|CIDR whitelist]] regime that [[tspu|TSPU]] runs on Russian mobile carriers, fake-ClientHello desync alone fails — you need a tunnel to a whitelisted IP.

Against an SNI-only block — typical of corporate networks, hotel Wi-Fi, university filters, and many national-level filters that aren't quite as aggressive as TSPU — desync plus MSS clamping can be enough on its own. This is the bracket [[bypassing-dpi-with-ebpf|gecit]] is built for.

## Implementations

- **[[gecit]]** — eBPF `sock_ops` on Linux, TUN+gVisor on macOS/Windows; default fake SNI `www.google.com`, default TTL 8, default MSS 88.
- **zapret / GoodbyeDPI / ByeDPI** — the older family of client-side desync tools, mentioned in [[russia-vpn-bypass-state-2026-04]] as orthogonal companions to a real tunnel. They use NFQUEUE / WinDivert hooks rather than eBPF, which is why none of them have a clean in-kernel Linux story.

## Related

- [[domain-fronting]] — sister technique that lies to the DPI via inner-vs-outer hostname mismatch on a CDN, rather than packet-level desync
- [[tspu]] — DPI system this technique partially defeats; the SNI inspection layer specifically
- [[whitelist-internet-blocking]] — the layer this technique cannot defeat (fakes don't move your exit IP)
- [[russia-vpn-bypass-state-2026-04]] — places this in the larger Russian-circumvention picture
