Skip to content

TLS : tls spoofing#6103

Open
codewithtamim wants to merge 16 commits into
XTLS:mainfrom
codewithtamim:feature/tls-spoofing
Open

TLS : tls spoofing#6103
codewithtamim wants to merge 16 commits into
XTLS:mainfrom
codewithtamim:feature/tls-spoofing

Conversation

@codewithtamim
Copy link
Copy Markdown
Contributor

@codewithtamim codewithtamim commented May 9, 2026

fix #5964

So basically when you make a TLS outbound connection, the code checks if you set a "spoof" field or not. If you did, it genrates a fake TLS ClientHello using the SNI you gave for spoofing, then wraps the connection with tlsspoof.Conn. On the first Write() call (which is what triggers the real TLS ClientHello), it shoves the fake ClientHello in as a raw tcp/ip packet first, then immediatly sends the real one normally.

the fake packet is then intentionaly broken depending on which method you picked (spoof_method), so censors can parse it and think theyre seeing a connection to allowed.site.com, but the real destination rejects the fake packet and only process the real one.

how to configure in outbound tls settings:

{
  "tlsSettings": {
    "serverName": "real.site.com",
    "spoof": "allowed.site.com",
    "spoof_method": "wrong-sequence",
    "spoof_count": 1
  }
}

spoof (string) : the fake sni to inject, has to be a domain, ip adresses are rejected automaticaly
spoof_method (string) : which corruption method to use, see below
spoof_count (int32) : how many Write() calls trigger the injection, 0 or 1 both mean single injection (default)

works with: tcp, websocket, http upgrade, grpc, splithttp, mkcp
runs before the actual tls handshake happends

available methods:
wrong-sequence (default) : offsets the tcp sequence number by -len(payload) so the server see it as out of window
wrong-checksum : bitwise inverts the tcp checksum (^0xFFFF), server drops it
wrong-ack : sets a wrong tcp acknowledgement number, server sends RST
wrong-md5 : appends a bogus tcp md5 signature option, servers withouth md5 support drop it
wrong-timestamp : backdates the tcp timestamp by 1 hour so server rejects it (not supported on mac)

platform support:
linux : fully supported, needs CAP_NET_RAW + CAP_NET_ADMIN or root
mac : full support excpet wrong-timestamp, needs sudo/root
freebsd : full support excpet wrong-timestamp, needs root
windows amd64/x86 : full support, needs admin, windivert kernel driver loaded via scm on first run

not supported: windows arm64, android, ios, openwrt

NEW WAY (final mask)

after maintainer review the pr was rebuilt as a tcpmask under finalmask instead of a tls setting.

so basically after the tcp connection is dialed, the rawpacket wrapper sits on top. on the first Write() call (which is what triggers the real TLS ClientHello), it crafts a fake TCP/IP packet with your payload, corrupts it so the server drops it, injects it via raw socket, then lets the real Write() go through normally.

the big difference is you provide the exact fake bytes yourself as base64. before the code auto generate a fake ClientHello from the spoof SNI. now you generate whatever fake payload you want,, could be a ClientHello, could be HTTP headers, could be anything ,and base64 it, and pass it in.

the other big difference is the TTL. before it was hardcoded to 64. now it defaults to 3 and you can change it. the idea is the fake packet should reach the censor but die before it hits the destination server, exactly what RPRX said in the original issue.

how to configure as a tcpmask:

{
  "finalmask": {
    "tcp": [
      {
        "type": "rawpacket",
        "settings": {
          "payload": "FgQDBxo...base64...",
          "method": "wrong-sequence",
          "ttl": 3,
          "count": 1
        }
      }
    ]
  }
}

payload (string, base64) : the exact fake bytes to inject. you generate this yourself.
method (string) : which corruption method to use, see below
ttl (uint32) : IP TTL of the fake packet. default is 3. low values mean it dies before the CDN.
count (int32) : how many Write() calls trigger the injection, 0 or 1 both mean single injection (default)

works with any TCP based transport (tcp, websocket, http upgrade, grpc, splithttp, mkcp)
runs before the actual tls handshake because it intercepts the first Write() is done.

available methods :
wrong-sequence (default): offsets the tcp sequence number by -len(payload) so the server see it as out of window
wrong-checksum : bitwise inverts the tcp checksum (^0xFFFF), server drops it
wrong-ack : sets a wrong tcp acknowledgement number, server sends RST
wrong-md5 : appends a bogus tcp md5 signature option, servers withouth md5 support drop it
wrong-timestamp : backdates the tcp timestamp by 1 hour so server rejects it (not supported on mac/freebsd)

platform support:
linux : fully supported, needs CAP_NET_RAW + CAP_NET_ADMIN or root
mac : full support excpet wrong-timestamp, needs sudo/root
freebsd : full support excpet wrong-timestamp, needs root
windows amd64/x86 : full support, needs admin, windivert kernel driver loaded via scm on first run

not supported:
windows arm64, android, ios, openwrt

@codewithtamim
Copy link
Copy Markdown
Contributor Author

@Fangliding

@RPRX
Copy link
Copy Markdown
Member

RPRX commented May 9, 2026

应当加到 finalmask

@Fangliding
Copy link
Copy Markdown
Member

将它添加为一个tcpmask(以避免需要修改其他传输)
以及不要使用 tls spoofing 之类的名字 这个机制理论上可以用来发送任何虚假的数据包 可以允许用户输入base64 然后定制他们需要的不同的tls clienthello或者其他fake header

@RPRX
Copy link
Copy Markdown
Member

RPRX commented May 9, 2026

其实直接加给 header-custom 就行

@codewithtamim
Copy link
Copy Markdown
Contributor Author

codewithtamim commented May 9, 2026

其实直接加给 header-custom 就行

ok, I need a few days, loaded with work right now

@codewithtamim codewithtamim marked this pull request as draft May 9, 2026 17:04
@codewithtamim codewithtamim marked this pull request as ready for review May 10, 2026 06:00
@codewithtamim
Copy link
Copy Markdown
Contributor Author

@Fangliding @RPRX new changes are ready

@Fangliding
Copy link
Copy Markdown
Member

image 为什么这里还有改动

@codewithtamim
Copy link
Copy Markdown
Contributor Author

image 为什么这里还有改动

check now

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add TLS spoof support

3 participants