sni-spoofing-rust: How a Rust Tool Gaslights DPI With One Packet That Never Reaches the Server
A deep dive into the out-of-window SNI injection trick, the raw-socket plumbing behind it, and why the project is more about TCP state than TLS itself.
- The project’s core move is to make DPI and the origin server disagree about the same packet, which turns TCP state into the attack surface.
- Its Rust value is not just safety, but clean separation between packet injection, capture backends, and ordinary async relaying.
- This is a surgical bypass tool, not a full proxy ecosystem, so its appeal is precision rather than breadth.
- The code is elegant, but the overall project still reads as experimental because the trick is stronger than the surrounding maturity story.
The most interesting thing about sni-spoofing-rust is not that it speaks TLS. It is that it sends one TLS ClientHello to two different audiences, and only one of them is meant to believe it.
A DPI middlebox sees a forged SNI and makes a judgment early. The real server sees a packet whose sequence number places it outside the receive window, so it drops the payload before TLS ever becomes relevant. The bypass lives in that split.
The packet the server never sees
The tool’s signature move is simple to state and tricky to execute. It fabricates a TLS ClientHello, patches in a chosen SNI, then sends that packet with a sequence number that makes it look valid to an inspector but invalid to the endpoint.
That matters because many censorship systems want to decide fast. They inspect early bytes, infer a hostname, and then allow or block the flow before the application ever finishes negotiating. If the server never accepts the forged packet, but the DPI already acted on it, the attacker has found a crack in the timing model.
Why sequence numbers matter more than TLS branding
In practice, the tool depends on a mismatch in how aggressively different network actors validate state. The DPI wants to understand the flow early. The server enforces TCP correctness strictly. If the fake packet lands in the inspector’s field of view but outside the server’s accepted window, the two systems diverge.
// Conceptual sequence placement
let fake_seq = isn.wrapping_add(1).wrapping_sub(fake_payload.len() as u32);
// The forged ClientHello is visible to inspection,
// but it is positioned so the origin drops it.
That is why the story is more interesting than generic SNI spoofing. The hostname alone is not the exploit. The exploit is making inspection and delivery disagree about the same bytes.
How the Rust code is split across the stack
The repo is organized like a layered attack surface. `main.rs` starts the sniffer and the listener. `handler.rs` coordinates each connection. `relay.rs` carries the actual traffic once the deception has done its job.
The low-level work lives under `src/sniffer/` and `src/packet/`. That split is the key design choice. One side watches and injects. The other side builds packets, computes checksums, and patches TLS bytes by hand.
// High-level shape of the architecture
main
├─ sniffer thread
│ ├─ capture backend
│ └─ forged packet injector
├─ connection handler
│ └─ register conn tuple
└─ relay
└─ bidirectional tokio copy
That architecture keeps the repo small without making it simplistic. It is still an async proxy at heart, but one with a very sharp packet-level prelude.
Raw sockets, native filters, and no libpcap
The capture layer avoids heavyweight packet-capture abstractions. On Linux it uses `AF_PACKET` and a hand-written BPF filter. On macOS it uses `/dev/bpf*` and the relevant ioctls. The point is to keep packet selection close to the kernel and keep the Rust side focused on the logic that matters.
That choice pays off in two ways. It reduces overhead, and it makes the code feel like infrastructure rather than a wrapper around infrastructure. The backend does the minimum necessary to observe the connection tuple and wake up only when the right traffic appears.
| Project | Mechanism | Scope | Operational burden | Why it matters |
|---|---|---|---|---|
| sni-spoofing-rust | Out-of-window forged ClientHello plus async relay | Narrow and surgical | Low to moderate | Optimized for one bypass trick |
| Xray-core with Reality | Protocol-level camouflage inside a larger proxy ecosystem | Broad | Moderate to high | A full platform with a bigger operational footprint |
| snirect | Transparent proxy that modifies SNI through MITM | Focused but proxy-centric | Moderate | Pragmatic, but requires a different trust model |
The comparison is revealing. This repo is smaller than Xray, and less of a full platform than snirect. That is not a weakness by itself. It just means the design center is a single exploit path, not an all-purpose censorship stack.
Why this is smaller than Xray, but more surgical
Xray’s Reality protocol hides in a broader camouflage system. Snirect works as a transparent proxy and leans on a different trust story. `sni-spoofing-rust` is narrower. It does one thing with a high degree of precision: manipulate what the middlebox believes about a packet that the endpoint will never accept.
That narrowness is attractive if you care about footprint and control. It is less attractive if you want a turnkey ecosystem. The repo is not trying to be the ecosystem. It is trying to make one network lie cleanly.
Xray’s Reality protocol hides the SNI by mimicking a major site’s TLS handshake.
That quote is the useful baseline. This repo does not try to outdo Reality on disguise. It uses a different lever entirely, one that depends on sequence math and the assumptions middleboxes make when they inspect TCP streams.
What the code hints at, and what it still lacks
The code structure is clean enough to suggest a thoughtful author. The separation of capture, packet crafting, and relay is good engineering. So is the use of Rust traits and async channels to keep the pieces loosely coupled.
But the project still feels young. There is no deep testing story visible here, and the design reads as a sharp prototype more than a hardened production system. That is not a criticism of the idea. It is a reminder that network subversion is easy to imagine and hard to operationalize safely.
- The backend abstraction is the strongest sign that the repo could grow into more platforms without rewriting the core trick.
- The packet module is the riskiest part, because a small checksum or sequence mistake would break the illusion.
- The relay layer is the least surprising part, which is exactly how it should be in a tool like this.
- The project’s real value is conceptual: it shows how much leverage sits between packet inspection and packet acceptance.





