Skip to content

TLS : tls spoofing#6103

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

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

Conversation

@codewithtamim
Copy link
Copy Markdown
Contributor

@codewithtamim codewithtamim commented May 9, 2026

fix #5964

RawPacket aka TLS Spoofing

So first lets talk about the idea,

when you connect to a xray server, the censors can see your TLS ClientHello, read the SNI, and figure out what you are doing and block :)

The RawPacket aka TLS spoofing fixes that by injecting a fake TLS ClientHello before the real one Hits.

The fake one carries like a decoy domain (ex. baidu.com) and then the real one carries your actual destination (ex. www.example.com).

A sensor that captures the first packet sees baidu.com and think that you are just browsing, and everything is okay with that, but in reality your connection goes through :)

It can be configured in the finalmask.tcp okay? and its only for client side.

How everything works

When Xray dials a TCP connection and the first Write() happens (which also triggers the real TLS handshake) , our Raw Packet setup does the following.

1.builds a fake Tls ClientHello with whatever SNI you want or you have option to provide the raw bytes (b64).

2.Then we. wrap it in a raw TCP/ip packet.

3.Then deliberately we corrupt the TCP header using whatever method is selected okay?

  1. Then we shoot it out via a raw socket

  2. Then immediately let the real Write() go through normally

In short : two packets arrive at the server back to back and the fake one gets dropped by the servers TCP stack because of the corruption and then the real handshake proceeds like nothing happened.

Config Example

{
  "streamSettings": {
    "finalmask": {
      "tcp": [
        {
          "type": "rawpacket",
          "settings": {
            "sni": "baidu.com",
            "payload": "",
            "method": "wrong-sequence",
            "ttl": 3,
            "count": 1
          }
        }
      ]
    }
  }
}

Available Fields

sni : The domain to put in the fake ClientHello. If you set this, it will auto generate a valid TLS ClientHello for you automatically. If you set payload and sni same time , sni wins here.

"sni": "baidu.com"

payload : If you don't want an auto gnrated ClientHello, then just encode your raw bytes that you want into base64 and put them here. I have added an option to do it from command line also. Do like this

 xray tls fake-hello google.com

It will out the thing and then just copy paste.

Note : If sni and payload are empty or not set , the raw packet does nothing and the connection goes through normally.

method : It defines how we corrupt the fake packet so the server drops it.

wrong-sequence : It duplicates the sequence number (looks like a retransmission so server drops it)

So we grab the kernels snd_nxt ( the next sequence number the kernel expects to send), then subtract the length of the fake payload and then use that as the sequence number of the injected packet. The results then is a duplicate of the. last segment, so the receivers TCP stack sees it as a retransmit at best, or like out of window at worst and then drops it.

wrong-checksum: It flips all the bits in the TCP checksum (so server sees a bad checksum and drops it)

We build the packet with a correct checksum and then flip every bit(^0xFFFF). The receiver then validates the checksum and its wrong, so the packet goes in trash.

wrong-ack: It sets ACK number way outside the receive window (so server sends dup ACK then drops it)

It sets the ACK number to rcv_nxt - window/2 and that's way outside of the receivers window, and the kernel responds with a dup ACK containing the correct sequence number, then drops it.

wrong-md5 : This adds a fake TCP MD5 signature option and server without MD5 drops it

Here we append a TCP option with kind=19 and length=18 to the TCP header. The kind 19 is the MD5 signature option (RFC 2385), if the receiver doesn't use TCP MD5 (which is basically everything except some BGP routers), it sees an unknown option and drops the packet.

wrong-timestamp : Backdates the tcp timestamp by 1 hour (paws detects regression and then drops)

Just backdate the TCP timestamp option value by 3600000 MS (1 HR), The receivers paws mechanism checks if the timestamp went backwards, If it did the packet is then dropped.

Note : wrong-timestamp method only works on Linux, it doesn't work on MacOS and Free BSD because kernel doesn't expose the per connection timestamp offset, so we can't really tell what to value undercut.

ttl : it defines the IP time to live for the fake packet. Default is 3 but the idea is to keep it lower so the packet dies before reaching the server or can but still reaches the probe, you can change it if needed.

"ttl": 3

count : Defines how many writes should trigger an injection. Default is set to 1 means that only the first write gets fake packet treatment, after that the raw socket closes itself and all the subsequent writes goes directly to the TCP connection with zero overhead.

"count": 1

Platform Support

  1. Linux : FULL
  2. macOS : Full( no wrong-timestamp method)
  3. FreeBSD : Full (no wrong-timestamp method)
  4. Windows : Full
  5. Android/iOS : not possible with root , the way we did for other platforms, but I have a workaround, will work on it maybe in future.

How to run it

Linux :

# Option A run as root
sudo ./xray run -c client.json

# Option B grant just the capabilities you need
sudo setcap cap_net_raw,cap_net_admin+ep /path/to/xray-bin

Macos/FreeBSD :

sudo ./xray-bin run -c client.json

Windows : Run a terminal with Admin rights .

If you miss that , you will get the following error.

transport/internet/tcp: mask err > open AF_INET SOCK_RAW: operation not permitted Hint: run as root, or grant capabilities: sudo setcap cap_net_raw,cap_net_admin+ep /path/to/xray

A Sample Client Example

{
  "outbounds": [
    {
      "protocol": "vless",
      "settings": {
        "vnext": [
          {
            "address": "your-server.com",
            "port": 443,
            "users": [
              {
                "id": "550e8400-e29b-41d4-a716-446655440000",
                "flow": "xtls-rprx-vision",
                "encryption": "none"
              }
            ]
          }
        ]
      },
      "streamSettings": {
        "network": "tcp",
        "security": "reality",
        "realitySettings": {
          "fingerprint": "chrome",
          "serverName": "www.microsoft.com",
          "publicKey": "SERVER_PUBLIC_KEY",
          "shortId": "6ba85179e30d4fc2"
        },
        "finalmask": {
          "tcp": [
            {
              "type": "rawpacket",
              "settings": {
                "sni": "baidu.com",
                "method": "wrong-sequence"
              }
            }
          ]
        }
      }
    }
  ]
}

TESTING I DID

I tested the implementation myself on an Ubuntu 24.0.4 and it works. Here is the process I followed.

First ran xray on the server normally with my config,

Example (that's what I used) :

{
  "log": { "loglevel": "warning" },
  "inbounds": [{
    "port": 443,
    "protocol": "vless",
    "settings": {
      "clients": [{
        "id": "550e8400-e29b-41d4-a716-446655440000",
        "flow": "xtls-rprx-vision"
      }],
      "decryption": "none"
    },
    "streamSettings": {
      "network": "tcp",
      "security": "reality",
      "realitySettings": {
        "dest": "www.microsoft.com:443",
        "serverNames": ["www.microsoft.com", "microsoft.com"],
        "privateKey": "ILwC6za0rIYHwdlr6w9spaZFEDoRt8mVWclyOyqo-2g",
        "shortIds": ["6ba85179e30d4fc2"]
      }
    }
  }],
  "outbounds": [{ "protocol": "freedom", "tag": "direct" }]
}

Then run the server

sudo ./xray run -c server.json

Client Example :

{
  "log": { "loglevel": "warning" },
  "inbounds": [{ "port": 1080, "protocol": "socks", "settings": { "auth": "no" } }],
  "outbounds": [{
    "protocol": "vless",
    "settings": {
      "vnext": [{
        "address": "127.0.0.1",
        "port": 443,
        "users": [{
          "id": "550e8400-e29b-41d4-a716-446655440000",
          "flow": "xtls-rprx-vision",
          "encryption": "none"
        }]
      }]
    },
    "streamSettings": {
      "network": "tcp",
      "security": "reality",
      "realitySettings": {
        "fingerprint": "chrome",
        "serverName": "www.microsoft.com",
        "publicKey": "_X_rYvu-na_fZafPGhaGstYOXFKzpa2ZIDDLl7FQyHE",
        "shortId": "6ba85179e30d4fc2"
      },
      "finalmask": {
        "tcp": [{
          "type": "rawpacket",
          "settings": {
            "sni": "baidu.com",
            "method": "wrong-sequence"
          }
        }]
      }
    }
  }]
}

Cases :

  1. Ran as a test user so got the error as there was no capability.
$ setcap -r /tmp/xray
$ su - testuser -c "/tmp/xray run -c /tmp/client.json"
$ curl -x socks5://127.0.0.1:1080 https://www.google.com
HTTP_000

Output

transport/internet/tcp: mask err > open AF_INET SOCK_RAW: operation not permitted
  Hint: run as root, or grant capabilities:
  sudo setcap cap_net_raw,cap_net_admin+ep /path/to/xray

With capabilities everything works. I tested all 5 methods and it worked.

Proof from wire capture, I ran a tcpdump on loopback and saw both packets :

 sudo tcpdump -i lo -s 0 -X -c 10 port 443

Packet 1 (fake one , the one we injected)

13:24:21.119000 IP localhost.40552 > localhost.https: Flags [P.], seq 4294967006:1, ack 1, win 65535, length 291
        0x0000:  4500 014b 0ac7 0000 0306 ade4 7f00 0001  E..K............
        0x0010:  7f00 0001 9e68 01bb 3532 4dad fac7 f736  .....h..52M....6
        0x0020:  5018 ffff 7a51 0000 1603 0101 1e01 0001  P...zQ..........
        0x0030:  1a03 03fa 0ac6 f342 e135 f4c9 4e9d 23a2  .......B.5..N.#.
        0x0040:  46e2 f508 fd16 0272 eb11 a210 0ab9 4edc  F......r......N.
        0x0050:  3bae 3820 ca90 e8cc 1957 041f d8f6 43ab  ;.8......W....C.
        0x0060:  2558 2933 7753 b7d1 8d8e 09ab 278d e12d  %X)3wS......'..-
        0x0070:  9495 35fb 001a c02b c02f c02c c030 cca9  ..5....+./.,.0..
        0x0080:  cca8 c009 c013 c00a c014 1301 1302 1303  ................
        0x0090:  0100 00b7 0000 000e 000c 0000 0962 6169  .............bai
        0x00a0:  6475 2e63 6f6d 000b 0002 0100 ff01 0001  du.com..........
        0x00b0:  0000 1700 0000 1200 0000 0500 0501 0000  ................
        0x00c0:  0000 000a 0008 0006 001d 0017 0018 000d  ................
        0x00d0:  0016 0014 0804 0403 0807 0805 0806 0401  ................
        0x00e0:  0501 0601 0503 0603 0032 001a 0018 0804  .........2......
        0x00f0:  0403 0807 0805 0806 0401 0501 0601 0503  ................
        0x0100:  0603 0201 0203 0010 000e 000c 0268 3208  .............h2.
        0x0110:  6874 7470 2f31 2e31 002b 0005 0403 0403  http/1.1.+......
        0x0120:  0300 3300 2600 2400 1d00 201e 1250 17f7  ..3.&.$......P..
        0x0130:  fc9a e205 ba89 cbfc a9e6 2a05 7130 40d3  ..........*.q0@.
        0x0140:  db08 5a67 1980 cf0e 5a80 53              ..Zg....Z.S

Here look at 0x0090:

... 0000 0962 6169 6475 2e63 6f 6d 62 61 69 64 75 2e 63 6f 6d = baidu.com and that's the decoy SNI in the fake ClientHello

Packet 2 (real one, a normal TCP socket) :

13:24:21.119031 IP localhost.40552 > localhost.https: Flags [P.], seq 1:1824, ack 1, win 512, length 1823
        0x0000:  4500 0753 faa7 4000 4006 3afb 7f00 0001  E..S..@.@.:.....
        0x0010:  7f00 0001 9e68 01bb 3532 4ed0 fac7 f736  .....h..52N....6
        0x0020:  8018 0200 0548 0000 0101 080a 837d 41c1  .....H.......}A.
        0x0030:  837d 41be 1603 0107 1a01 0007 1603 036f  .}A............o
                            (TLS ClientHello... sni extension:)
        0x0230:  066a 6a03 0403 0300 0000 1600 1400 0011  .jj.............
        0x0240:  7777 772e 6d69 6372 6f73 6f66 742e 636f  www.microsoft.co
        0x0250:  6d00 1700 0000 3304 ef04 ed5a 5a00 0100  m.....3....ZZ...
        

Now look at 0x0240 : 7777 772e 6d69 6372 6f73 6f66 742e 636f 6d = www.microsoft.com, that's the real SNI the server processes.

So a probe that only grabs packet 1 sees Baidu.com and moves on. Packet 2 with real SNI goes through the server and the connection works fine.

So that's basically All. Feel free to ask any questions I will try to give ans, Thanks

@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

@codewithtamim
Copy link
Copy Markdown
Contributor Author

@Fangliding any update?

@RPRX
Copy link
Copy Markdown
Member

RPRX commented May 22, 2026

@codewithtamim 下个月合并

@therealtunge
Copy link
Copy Markdown

this should be merged, in iran TLS spoofing is back (it got blocked before, its alive again!)

@codewithtamim
Copy link
Copy Markdown
Contributor Author

this should be merged, in iran TLS spoofing is back (it got blocked before, its alive again!)

can you try building from my branch and try it? and then give some feedback :)

@MrMalekfar
Copy link
Copy Markdown

For everyone in IRAN who are trying to use this from my fork try increasing your ttl to 15+ and try different methods the ones available on your os.

available methods : wrong-sequence (default): offsets the tcp sequence number by -len(payload) so the server sees 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 without md5 support drop it wrong-timestamp : backdates the tcp timestamp by 1 hour so the server rejects it (not supported on mac/freebsd)

in real use cases don't use the same payload generate it every time (ofc)

here is a sample with payload hcaptcha.com

"finalmask": {
  "tcp": [
    {
      "type": "rawpacket",
      "settings": {
        "payload": "FgMBAgABAAH8AwMDiEMGMtRuHWD4Gs6B33kvGPIWfYolMGKbAnbkw1NiXiAigyrtCHtlUcMtXGhw8Dsr+p9U/dqXY9EYRsGRynPBgQBIEwITAxMBEwTALMAwzKnMqMCtwCvAL8CswCPAJ8AKwBTACcATAJ3AnQCcwJwAPQA8ADUALwCfzKrAnwCewJ4AawBnADkAMwD/AQABawAAABEADwAADGhjYXB0Y2hhLmNvbQALAAQDAAECAAoAFgAUAB0AFwAeABkAGAEAAQEBAgEDAQQzdAAAABAADgAMAmgyCGh0dHAvMS4xABYAAAAXAAAAMQAAAA0AIgAgBAMFAwYDCAcICAgJCAoICwgECAUIBgQBBQEGAQMDAwEAKwAFBAMEAwMALQACAQEAMwAmACQAHQAgtuUdDAd4P0VGG2Q5MVSB5CPK1hqN4Ck7pnGPEZBfwDcAFQCvAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==",
        "method": "wrong-checksum",
        "ttl": 12,
        "count": 1
      }
    }
  ]
}

download build : https://github.com/codewithtamim/Xray-core/actions/runs/26385950177

من خیلی تلاش کردم با تغییرات شما در هسته xray و روشهای مختلفی که اینجا گفتید در سیستم عامل ویندوز وصل بشم ولی ننتونستم. کانفیگی که استفاده کردم:

  "outbounds": [
    {
      "tag": "proxy",
      "protocol": "vless",
      "settings": {
        "vnext": [
          {
            "address": "104.19.229.21",
            "port": 443,
            "users": [
              {
                "id": "**********************",
                "email": "t@t.tt",
                "security": "auto",
                "encryption": "none"
              }
            ]
          }
        ]
      },
      "streamSettings": {
        "network": "ws",
        "security": "tls",
        "tlsSettings": {
          "allowInsecure": true,
          "serverName": "*******"
        },
        "wsSettings": {
          "path": "/",
          "host": "************"
        },

"finalmask": {
  "tcp": [
    {
      "type": "rawpacket",
      "settings": {
        "payload": "FgMBAgABAAH8AwMDiEMGMtRuHWD4Gs6B33kvGPIWfYolMGKbAnbkw1NiXiAigyrtCHtlUcMtXGhw8Dsr+p9U/dqXY9EYRsGRynPBgQBIEwITAxMBEwTALMAwzKnMqMCtwCvAL8CswCPAJ8AKwBTACcATAJ3AnQCcwJwAPQA8ADUALwCfzKrAnwCewJ4AawBnADkAMwD/AQABawAAABEADwAADGhjYXB0Y2hhLmNvbQALAAQDAAECAAoAFgAUAB0AFwAeABkAGAEAAQEBAgEDAQQzdAAAABAADgAMAmgyCGh0dHAvMS4xABYAAAAXAAAAMQAAAA0AIgAgBAMFAwYDCAcICAgJCAoICwgECAUIBgQBBQEGAQMDAwEAKwAFBAMEAwMALQACAQEAMwAmACQAHQAgtuUdDAd4P0VGG2Q5MVSB5CPK1hqN4Ck7pnGPEZBfwDcAFQCvAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==",
        "method": "wrong-sequence",
        "ttl": 15,
        "count": 1
      }
    }
  ]
}
      },
      "mux": {
        "enabled": false,
        "concurrency": -1
      }
    },
    {
      "tag": "direct",
      "protocol": "freedom"
    },
    {
      "tag": "block",
      "protocol": "blackhole"
    }
  ], ```

@codewithtamim
Copy link
Copy Markdown
Contributor Author

codewithtamim commented May 28, 2026

For everyone in IRAN who are trying to use this from my fork try increasing your ttl to 15+ and try different methods the ones available on your os.
available methods : wrong-sequence (default): offsets the tcp sequence number by -len(payload) so the server sees 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 without md5 support drop it wrong-timestamp : backdates the tcp timestamp by 1 hour so the server rejects it (not supported on mac/freebsd)
in real use cases don't use the same payload generate it every time (ofc)
here is a sample with payload hcaptcha.com

"finalmask": {
  "tcp": [
    {
      "type": "rawpacket",
      "settings": {
        "payload": "FgMBAgABAAH8AwMDiEMGMtRuHWD4Gs6B33kvGPIWfYolMGKbAnbkw1NiXiAigyrtCHtlUcMtXGhw8Dsr+p9U/dqXY9EYRsGRynPBgQBIEwITAxMBEwTALMAwzKnMqMCtwCvAL8CswCPAJ8AKwBTACcATAJ3AnQCcwJwAPQA8ADUALwCfzKrAnwCewJ4AawBnADkAMwD/AQABawAAABEADwAADGhjYXB0Y2hhLmNvbQALAAQDAAECAAoAFgAUAB0AFwAeABkAGAEAAQEBAgEDAQQzdAAAABAADgAMAmgyCGh0dHAvMS4xABYAAAAXAAAAMQAAAA0AIgAgBAMFAwYDCAcICAgJCAoICwgECAUIBgQBBQEGAQMDAwEAKwAFBAMEAwMALQACAQEAMwAmACQAHQAgtuUdDAd4P0VGG2Q5MVSB5CPK1hqN4Ck7pnGPEZBfwDcAFQCvAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==",
        "method": "wrong-checksum",
        "ttl": 12,
        "count": 1
      }
    }
  ]
}

download build : https://github.com/codewithtamim/Xray-core/actions/runs/26385950177

من خیلی تلاش کردم با تغییرات شما در هسته xray و روشهای مختلفی که اینجا گفتید در سیستم عامل ویندوز وصل بشم ولی ننتونستم. کانفیگی که استفاده کردم:

  "outbounds": [
    {
      "tag": "proxy",
      "protocol": "vless",
      "settings": {
        "vnext": [
          {
            "address": "104.19.229.21",
            "port": 443,
            "users": [
              {
                "id": "**********************",
                "email": "t@t.tt",
                "security": "auto",
                "encryption": "none"
              }
            ]
          }
        ]
      },
      "streamSettings": {
        "network": "ws",
        "security": "tls",
        "tlsSettings": {
          "allowInsecure": true,
          "serverName": "*******"
        },
        "wsSettings": {
          "path": "/",
          "host": "************"
        },

"finalmask": {
  "tcp": [
    {
      "type": "rawpacket",
      "settings": {
        "payload": "FgMBAgABAAH8AwMDiEMGMtRuHWD4Gs6B33kvGPIWfYolMGKbAnbkw1NiXiAigyrtCHtlUcMtXGhw8Dsr+p9U/dqXY9EYRsGRynPBgQBIEwITAxMBEwTALMAwzKnMqMCtwCvAL8CswCPAJ8AKwBTACcATAJ3AnQCcwJwAPQA8ADUALwCfzKrAnwCewJ4AawBnADkAMwD/AQABawAAABEADwAADGhjYXB0Y2hhLmNvbQALAAQDAAECAAoAFgAUAB0AFwAeABkAGAEAAQEBAgEDAQQzdAAAABAADgAMAmgyCGh0dHAvMS4xABYAAAAXAAAAMQAAAA0AIgAgBAMFAwYDCAcICAgJCAoICwgECAUIBgQBBQEGAQMDAwEAKwAFBAMEAwMALQACAQEAMwAmACQAHQAgtuUdDAd4P0VGG2Q5MVSB5CPK1hqN4Ck7pnGPEZBfwDcAFQCvAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==",
        "method": "wrong-sequence",
        "ttl": 15,
        "count": 1
      }
    }
  ]
}
      },
      "mux": {
        "enabled": false,
        "concurrency": -1
      }
    },
    {
      "tag": "direct",
      "protocol": "freedom"
    },
    {
      "tag": "block",
      "protocol": "blackhole"
    }
  ], ```

Try now. https://github.com/codewithtamim/Xray-core/actions/runs/26607198630

also made a small change, now you can specify sni and it will auto generate the payload, or you can do just by running

xray tls fake-hello domain.com
new config structure

"finalmask": {
  "tcp": [{
    "type": "rawpacket",
    "settings": {
      "payload": "...",
      "sni": "hcaptcha.com",
      "method": "wrong-checksum",
      "ttl": 12,
      "count": 1
    }
  }]
}

YOU NEED TO RUN IT AS SUDO

@MrMalekfar
Copy link
Copy Markdown

Try now. https://github.com/codewithtamim/Xray-core/actions/runs/26607198630

also made a small change, now you can specify sni and it will auto generate the payload, or you can do just by running

xray tls fake-hello domain.com new config structure

"finalmask": {
  "tcp": [{
    "type": "rawpacket",
    "settings": {
      "payload": "...",
      "sni": "hcaptcha.com",
      "method": "wrong-checksum",
      "ttl": 12,
      "count": 1
    }
  }]
}

YOU NEED TO RUN IT AS SUDO

Thanks for your efforts! I tried running this new version with sudo, but unfortunately, it's still not working for me. For reference, I’m using the same configuration (with slight necessary changes to fit this method and with different ttl and method values as you suggested), which works perfectly with Patterniha's method.

@codewithtamim
Copy link
Copy Markdown
Contributor Author

Try now. https://github.com/codewithtamim/Xray-core/actions/runs/26607198630

also made a small change, now you can specify sni and it will auto generate the payload, or you can do just by running

xray tls fake-hello domain.com new config structure

"finalmask": {
  "tcp": [{
    "type": "rawpacket",
    "settings": {
      "payload": "...",
      "sni": "hcaptcha.com",
      "method": "wrong-checksum",
      "ttl": 12,
      "count": 1
    }
  }]
}

YOU NEED TO RUN IT AS SUDO

Thanks for your efforts! I tried running this new version with sudo, but unfortunately, it's still not working for me. For reference, I’m using the same configuration (with slight necessary changes to fit this method and with different ttl and method values as you suggested), which works perfectly with Patterniha's method.

It shows any error? or like the internet is blocked something? do you have some logs?

@RPRX
Copy link
Copy Markdown
Member

RPRX commented May 31, 2026

@codewithtamim 我看了下这是直接加了个 rawpacket 的 finalmask,那可以直接合并毕竟不影响其它东西

需要 rebase 一下,更新下 PR 正文的 example,另外这个东西能给 UDP 也用上吗

@codewithtamim
Copy link
Copy Markdown
Contributor Author

@codewithtamim 我看了下这是直接加了个 rawpacket 的 finalmask,那可以直接合并毕竟不影响其它东西

需要 rebase 一下,更新下 PR 正文的 example,另外这个东西能给 UDP 也用上吗

I got some report from IRAN it's not working for them. So need a bit more time for it.

@codewithtamim
Copy link
Copy Markdown
Contributor Author

fix #5964

RawPacket aka TLS Spoofing

So first lets talk about the idea,

when you connect to a xray server, the censors can see your TLS ClientHello, read the SNI, and figure out what you are doing and block :)

The RawPacket aka TLS spoofing fixes that by injecting a fake TLS ClientHello before the real one Hits.

The fake one carries like a decoy domain (ex. baidu.com) and then the real one carries your actual destination (ex. www.example.com).

A sensor that captures the first packet sees baidu.com and think that you are just browsing, and everything is okay with that, but in reality your connection goes through :)

It can be configured in the finalmask.tcp okay? and its only for client side.

How everything works

When Xray dials a TCP connection and the first Write() happens (which also triggers the real TLS handshake) , our Raw Packet setup does the following.

1.builds a fake Tls ClientHello with whatever SNI you want or you have option to provide the raw bytes (b64).

2.Then we. wrap it in a raw TCP/ip packet.

3.Then deliberately we corrupt the TCP header using whatever method is selected okay?

  1. Then we shoot it out via a raw socket
  2. Then immediately let the real Write() go through normally

In short : two packets arrive at the server back to back and the fake one gets dropped by the servers TCP stack because of the corruption and then the real handshake proceeds like nothing happened.

Config Example

{
  "streamSettings": {
    "finalmask": {
      "tcp": [
        {
          "type": "rawpacket",
          "settings": {
            "sni": "baidu.com",
            "payload": "",
            "method": "wrong-sequence",
            "ttl": 3,
            "count": 1
          }
        }
      ]
    }
  }
}

Available Fields

sni : The domain to put in the fake ClientHello. If you set this, it will auto generate a valid TLS ClientHello for you automatically. If you set payload and sni same time , sni wins here.

"sni": "baidu.com"

payload : If you don't want an auto gnrated ClientHello, then just encode your raw bytes that you want into base64 and put them here. I have added an option to do it from command line also. Do like this

 xray tls fake-hello google.com

It will out the thing and then just copy paste.

Note : If sni and payload are empty or not set , the raw packet does nothing and the connection goes through normally.

method : It defines how we corrupt the fake packet so the server drops it.

wrong-sequence : It duplicates the sequence number (looks like a retransmission so server drops it)

So we grab the kernels snd_nxt ( the next sequence number the kernel expects to send), then subtract the length of the fake payload and then use that as the sequence number of the injected packet. The results then is a duplicate of the. last segment, so the receivers TCP stack sees it as a retransmit at best, or like out of window at worst and then drops it.

wrong-checksum: It flips all the bits in the TCP checksum (so server sees a bad checksum and drops it)

We build the packet with a correct checksum and then flip every bit(^0xFFFF). The receiver then validates the checksum and its wrong, so the packet goes in trash.

wrong-ack: It sets ACK number way outside the receive window (so server sends dup ACK then drops it)

It sets the ACK number to rcv_nxt - window/2 and that's way outside of the receivers window, and the kernel responds with a dup ACK containing the correct sequence number, then drops it.

wrong-md5 : This adds a fake TCP MD5 signature option and server without MD5 drops it

Here we append a TCP option with kind=19 and length=18 to the TCP header. The kind 19 is the MD5 signature option (RFC 2385), if the receiver doesn't use TCP MD5 (which is basically everything except some BGP routers), it sees an unknown option and drops the packet.

wrong-timestamp : Backdates the tcp timestamp by 1 hour (paws detects regression and then drops)

Just backdate the TCP timestamp option value by 3600000 MS (1 HR), The receivers paws mechanism checks if the timestamp went backwards, If it did the packet is then dropped.

Note : wrong-timestamp method only works on Linux, it doesn't work on MacOS and Free BSD because kernel doesn't expose the per connection timestamp offset, so we can't really tell what to value undercut.

ttl : it defines the IP time to live for the fake packet. Default is 3 but the idea is to keep it lower so the packet dies before reaching the server or can but still reaches the probe, you can change it if needed.

"ttl": 3

count : Defines how many writes should trigger an injection. Default is set to 1 means that only the first write gets fake packet treatment, after that the raw socket closes itself and all the subsequent writes goes directly to the TCP connection with zero overhead.

"count": 1

Platform Support

  1. Linux : FULL
  2. macOS : Full( no wrong-timestamp method)
  3. FreeBSD : Full (no wrong-timestamp method)
  4. Windows : Full
  5. Android/iOS : not possible with root , the way we did for other platforms, but I have a workaround, will work on it maybe in future.

How to run it

Linux :

# Option A run as root
sudo ./xray run -c client.json

# Option B grant just the capabilities you need
sudo setcap cap_net_raw,cap_net_admin+ep /path/to/xray-bin

Macos/FreeBSD :

sudo ./xray-bin run -c client.json

Windows : Run a terminal with Admin rights .

If you miss that , you will get the following error.

transport/internet/tcp: mask err > open AF_INET SOCK_RAW: operation not permitted Hint: run as root, or grant capabilities: sudo setcap cap_net_raw,cap_net_admin+ep /path/to/xray

A Sample Client Example

{
  "outbounds": [
    {
      "protocol": "vless",
      "settings": {
        "vnext": [
          {
            "address": "your-server.com",
            "port": 443,
            "users": [
              {
                "id": "550e8400-e29b-41d4-a716-446655440000",
                "flow": "xtls-rprx-vision",
                "encryption": "none"
              }
            ]
          }
        ]
      },
      "streamSettings": {
        "network": "tcp",
        "security": "reality",
        "realitySettings": {
          "fingerprint": "chrome",
          "serverName": "www.microsoft.com",
          "publicKey": "SERVER_PUBLIC_KEY",
          "shortId": "6ba85179e30d4fc2"
        },
        "finalmask": {
          "tcp": [
            {
              "type": "rawpacket",
              "settings": {
                "sni": "baidu.com",
                "method": "wrong-sequence"
              }
            }
          ]
        }
      }
    }
  ]
}

TESTING I DID

I tested the implementation myself on an Ubuntu 24.0.4 and it works. Here is the process I followed.

First ran xray on the server normally with my config,

Example (that's what I used) :

{
  "log": { "loglevel": "warning" },
  "inbounds": [{
    "port": 443,
    "protocol": "vless",
    "settings": {
      "clients": [{
        "id": "550e8400-e29b-41d4-a716-446655440000",
        "flow": "xtls-rprx-vision"
      }],
      "decryption": "none"
    },
    "streamSettings": {
      "network": "tcp",
      "security": "reality",
      "realitySettings": {
        "dest": "www.microsoft.com:443",
        "serverNames": ["www.microsoft.com", "microsoft.com"],
        "privateKey": "ILwC6za0rIYHwdlr6w9spaZFEDoRt8mVWclyOyqo-2g",
        "shortIds": ["6ba85179e30d4fc2"]
      }
    }
  }],
  "outbounds": [{ "protocol": "freedom", "tag": "direct" }]
}

Then run the server

sudo ./xray run -c server.json

Client Example :

{
  "log": { "loglevel": "warning" },
  "inbounds": [{ "port": 1080, "protocol": "socks", "settings": { "auth": "no" } }],
  "outbounds": [{
    "protocol": "vless",
    "settings": {
      "vnext": [{
        "address": "127.0.0.1",
        "port": 443,
        "users": [{
          "id": "550e8400-e29b-41d4-a716-446655440000",
          "flow": "xtls-rprx-vision",
          "encryption": "none"
        }]
      }]
    },
    "streamSettings": {
      "network": "tcp",
      "security": "reality",
      "realitySettings": {
        "fingerprint": "chrome",
        "serverName": "www.microsoft.com",
        "publicKey": "_X_rYvu-na_fZafPGhaGstYOXFKzpa2ZIDDLl7FQyHE",
        "shortId": "6ba85179e30d4fc2"
      },
      "finalmask": {
        "tcp": [{
          "type": "rawpacket",
          "settings": {
            "sni": "baidu.com",
            "method": "wrong-sequence"
          }
        }]
      }
    }
  }]
}

Cases :

  1. Ran as a test user so got the error as there was no capability.
$ setcap -r /tmp/xray
$ su - testuser -c "/tmp/xray run -c /tmp/client.json"
$ curl -x socks5://127.0.0.1:1080 https://www.google.com
HTTP_000

Output

transport/internet/tcp: mask err > open AF_INET SOCK_RAW: operation not permitted
  Hint: run as root, or grant capabilities:
  sudo setcap cap_net_raw,cap_net_admin+ep /path/to/xray

With capabilities everything works. I tested all 5 methods and it worked.

Proof from wire capture, I ran a tcpdump on loopback and saw both packets :

 sudo tcpdump -i lo -s 0 -X -c 10 port 443

Packet 1 (fake one , the one we injected)

13:24:21.119000 IP localhost.40552 > localhost.https: Flags [P.], seq 4294967006:1, ack 1, win 65535, length 291
        0x0000:  4500 014b 0ac7 0000 0306 ade4 7f00 0001  E..K............
        0x0010:  7f00 0001 9e68 01bb 3532 4dad fac7 f736  .....h..52M....6
        0x0020:  5018 ffff 7a51 0000 1603 0101 1e01 0001  P...zQ..........
        0x0030:  1a03 03fa 0ac6 f342 e135 f4c9 4e9d 23a2  .......B.5..N.#.
        0x0040:  46e2 f508 fd16 0272 eb11 a210 0ab9 4edc  F......r......N.
        0x0050:  3bae 3820 ca90 e8cc 1957 041f d8f6 43ab  ;.8......W....C.
        0x0060:  2558 2933 7753 b7d1 8d8e 09ab 278d e12d  %X)3wS......'..-
        0x0070:  9495 35fb 001a c02b c02f c02c c030 cca9  ..5....+./.,.0..
        0x0080:  cca8 c009 c013 c00a c014 1301 1302 1303  ................
        0x0090:  0100 00b7 0000 000e 000c 0000 0962 6169  .............bai
        0x00a0:  6475 2e63 6f6d 000b 0002 0100 ff01 0001  du.com..........
        0x00b0:  0000 1700 0000 1200 0000 0500 0501 0000  ................
        0x00c0:  0000 000a 0008 0006 001d 0017 0018 000d  ................
        0x00d0:  0016 0014 0804 0403 0807 0805 0806 0401  ................
        0x00e0:  0501 0601 0503 0603 0032 001a 0018 0804  .........2......
        0x00f0:  0403 0807 0805 0806 0401 0501 0601 0503  ................
        0x0100:  0603 0201 0203 0010 000e 000c 0268 3208  .............h2.
        0x0110:  6874 7470 2f31 2e31 002b 0005 0403 0403  http/1.1.+......
        0x0120:  0300 3300 2600 2400 1d00 201e 1250 17f7  ..3.&.$......P..
        0x0130:  fc9a e205 ba89 cbfc a9e6 2a05 7130 40d3  ..........*.q0@.
        0x0140:  db08 5a67 1980 cf0e 5a80 53              ..Zg....Z.S

Here look at 0x0090:

... 0000 0962 6169 6475 2e63 6f 6d 62 61 69 64 75 2e 63 6f 6d = baidu.com and that's the decoy SNI in the fake ClientHello

Packet 2 (real one, a normal TCP socket) :

13:24:21.119031 IP localhost.40552 > localhost.https: Flags [P.], seq 1:1824, ack 1, win 512, length 1823
        0x0000:  4500 0753 faa7 4000 4006 3afb 7f00 0001  E..S..@.@.:.....
        0x0010:  7f00 0001 9e68 01bb 3532 4ed0 fac7 f736  .....h..52N....6
        0x0020:  8018 0200 0548 0000 0101 080a 837d 41c1  .....H.......}A.
        0x0030:  837d 41be 1603 0107 1a01 0007 1603 036f  .}A............o
                            (TLS ClientHello... sni extension:)
        0x0230:  066a 6a03 0403 0300 0000 1600 1400 0011  .jj.............
        0x0240:  7777 772e 6d69 6372 6f73 6f66 742e 636f  www.microsoft.co
        0x0250:  6d00 1700 0000 3304 ef04 ed5a 5a00 0100  m.....3....ZZ...
        

Now look at 0x0240 : 7777 772e 6d69 6372 6f73 6f66 742e 636f 6d = www.microsoft.com, that's the real SNI the server processes.

So a probe that only grabs packet 1 sees Baidu.com and moves on. Packet 2 with real SNI goes through the server and the connection works fine.

So that's basically All. Feel free to ask any questions I will try to give ans, Thanks

@Fangliding @RPRX , Added the pr body with clear details and example. Please review and let me know if you need anything else.

And about your question, no it can't be done with udp as the whole mechanism relies on tcp state (seq num, ack windows, tcp options ) but for UDP its connectionless, so there is nothing in meaningful way to corrupt. and no ClientHello equivalent to spoof, so this is TCP only by its design.

@Ahmad-2213
Copy link
Copy Markdown

Ahmad-2213 commented Jun 4, 2026

@codewithtamim
Hello there, Thanks for your efforts , Seems this is detected and no longer works. ( Tested on Iran's MCI Operator which is the most restricted one )
the only error i encountered was due to permissions and resolved it by running as admin.
what i tested:
a simple vless + ws +tls ( also tested with vless + xhttp + stream-one + tls ) connection with all of the stated methods with different settings and none of them could bypass DPI.
cloned fork branch: git clone -b feature/tls-spoofing https://github.com/codewithtamim/Xray-core.git
configuration:

{
  "log": {
    "loglevel": "debug"
  },
  "dns": {
    "hosts": {
      "dns.google": [
        "8.8.8.8",
        "8.8.4.4",
        "2001:4860:4860::8888",
        "2001:4860:4860::8844"
      ],
      "dns.alidns.com": [
        "223.5.5.5",
        "223.6.6.6",
        "2400:3200::1",
        "2400:3200:baba::1"
      ],
      "one.one.one.one": [
        "1.1.1.1",
        "1.0.0.1",
        "2606:4700:4700::1111",
        "2606:4700:4700::1001"
      ],
      "1dot1dot1dot1.cloudflare-dns.com": [
        "1.1.1.1",
        "1.0.0.1",
        "2606:4700:4700::1111",
        "2606:4700:4700::1001"
      ],
      "cloudflare-dns.com": [
        "104.16.249.249",
        "104.16.248.249",
        "2606:4700::6810:f8f9",
        "2606:4700::6810:f9f9"
      ],
      "dns.cloudflare.com": [
        "104.16.132.229",
        "104.16.133.229",
        "2606:4700::6810:84e5",
        "2606:4700::6810:85e5"
      ],
      "dot.pub": [
        "1.12.12.12",
        "120.53.53.53"
      ],
      "doh.pub": [
        "1.12.12.12",
        "120.53.53.53"
      ],
      "dns.quad9.net": [
        "9.9.9.9",
        "149.112.112.112",
        "2620:fe::fe",
        "2620:fe::9"
      ],
      "dns.yandex.net": [
        "77.88.8.8",
        "77.88.8.1",
        "2a02:6b8::feed:0ff",
        "2a02:6b8:0:1::feed:0ff"
      ],
      "dns.sb": [
        "185.222.222.222",
        "2a09::"
      ],
      "dns.umbrella.com": [
        "208.67.220.220",
        "208.67.222.222",
        "2620:119:35::35",
        "2620:119:53::53"
      ],
      "dns.sse.cisco.com": [
        "208.67.220.220",
        "208.67.222.222",
        "2620:119:35::35",
        "2620:119:53::53"
      ],
      "engage.cloudflareclient.com": [
        "162.159.192.1"
      ]
    },
    "servers": [
      {
        "address": "localhost",
        "domains": [
          "ir"
        ],
        "skipFallback": true,
        "tag": "direct-dns-1"
      },
      {
        "address": "223.5.5.5",
        "domains": [
          "full:dns.cloudflare.com"
        ],
        "skipFallback": true
      },
      "https://dns.cloudflare.com/dns-query"
    ],
    "tag": "dns-module"
  },
  "inbounds": [
    {
      "tag": "socks",
      "port": 10808,
      "listen": "127.0.0.1",
      "protocol": "mixed",
      "sniffing": {
        "enabled": true,
        "destOverride": [
          "http",
          "tls"
        ],
        "routeOnly": false
      },
      "settings": {
        "auth": "noauth",
        "udp": true,
        "allowTransparent": false
      }
    }
  ],
  "outbounds": [
    {
      "tag": "proxy",
      "protocol": "vless",
      "settings": {
        "vnext": [
          {
            "address": "104.19.229.21",
            "port": 443,
            "users": [
              {
                "id": "ID",
                "email": "t@t.tt",
                "security": "auto",
                "encryption": "none"
              }
            ]
          }
        ]
      },
      "streamSettings": {
         "finalmask": {
      "tcp": [
        {
          "type": "rawpacket",
          "settings": {
          
            "payload": "FgMBASMBAAEfAwNQSRTVQcrdgTJ18wpMBzpJUiYKbqQEA0AWaYcRXY0WSSCLT7JFRB8wEyr4ONd2fq1MV0ywyi0i/ggJFGKHsj3aXAAawCvAL8AswDDMqcyowAnAE8AKwBQTARMCEwMBAAC8AAAAEwARAAAOY2xvdWRmbGFyZS5jb20ACwACAQD/AQABAAAXAAAAEgAAAAUABQEAAAAAAAoACAAGAB0AFwAYAA0AFgAUCAQEAwgHCAUIBgQBBQEGAQUDBgMAMgAaABgIBAQDCAcIBQgGBAEFAQYBBQMGAwIBAgMAEAAOAAwCaDIIaHR0cC8xLjEAKwAFBAMEAwMAMwAmACQAHQAgjWOKaOkAdprI+VdRkwt3twGgGJx1ZXngBoNbn11rc3E=",  // xray tls fake-hello cloudflare.com
            "method": "wrong-sequence", // other methods are tested as well
            "ttl": 11,
            "count": 1
          }
        }
      ]
    },
        "network": "ws",
        "security": "tls",
        "tlsSettings": {
          "allowInsecure": false,
          "serverName": "example.com",
          
          "fingerprint": "chrome"
        },
         "wsSettings": {
          "path": "/path",
          "host": "example.com"
        }
      }
    },
    {
      "tag": "direct",
      "protocol": "freedom"
    },
    {
      "tag": "block",
      "protocol": "blackhole"
    }
  ],
  "routing": {
    "domainStrategy": "IPIfNonMatch",
    "rules": [
      {
        "type": "field",
        "inboundTag": [
          "api"
        ],
        "outboundTag": "api"
      },
      {
        "type": "field",
        "outboundTag": "direct",
        "domain": [
          "ir"
        ]
      },
      {
        "type": "field",
        "inboundTag": [
          "direct-dns-1"
        ],
        "outboundTag": "direct"
      },
      {
        "type": "field",
        "inboundTag": [
          "dns-module"
        ],
        "outboundTag": "proxy"
      }
    ]
  }
}

here are some logs :

Xray 26.6.1 (Xray, Penetrates Everything.) cbede9a-dirty (go1.26.0 windows/amd64)
A unified platform for anti-censorship.
2026/06/04 16:49:50.525374 [Info] infra/conf/serial: Reading config: &{Name:config.json Format:json}
2026/06/04 16:49:50.527105 [Warning] common/errors: The feature WebSocket transport (with ALPN http/1.1, etc.) is deprecated, not recommended for using and might be removed. Please migrate to XHTTP H2 & H3 as soon as possible.
2026/06/04 16:49:50.527105 [Debug] app/log: Logger started
2026/06/04 16:49:50.529836 [Info] app/dns: DNS: created localhost client
2026/06/04 16:49:50.529836 [Info] app/dns: DNS: created UDP client initialized for 223.5.5.5:53
2026/06/04 16:49:50.529836 [Info] app/dns: DNS: created DOH client for https://dns.cloudflare.com/dns-query, with h2c false
2026/06/04 16:49:50.532962 [Debug] app/proxyman/inbound: creating stream worker on 127.0.0.1:10808
2026/06/04 16:49:50.533982 [Info] transport/internet/tcp: listening TCP on 127.0.0.1:10808
2026/06/04 16:49:50.533982 [Warning] core: Xray 26.6.1 started
2026/06/04 16:50:09.124770 [Debug] [2620548628] proxy/socks: Not Socks request, try to parse as HTTP request
2026/06/04 16:50:09.124770 [Info] [2620548628] proxy/http: request to Method [CONNECT] Host [www.youtube.com:443] with URL [//www.youtube.com:443]
2026/06/04 16:50:09.125806 [Info] [2620548628] app/dispatcher: sniffed domain: www.youtube.com
2026/06/04 16:50:09.125815 [Info] [2620548628] app/dispatcher: default route for tcp:www.youtube.com:443
2026/06/04 16:50:09.125815 [Info] [2620548628] transport/internet/websocket: creating connection to tcp:104.19.229.21:443
2026/06/04 16:50:09.125815 [Debug] [2620548628] transport/internet: dialing to tcp:104.19.229.21:443
2026/06/04 16:50:09.125815 from 127.0.0.1:21620 accepted //www.youtube.com:443 [socks >> proxy]
2026/06/04 16:50:09.324241 [Error] [2620548628] transport/internet/websocket: failed to dial to 104.19.229.21:443 > read tcp 10.244.46.253:21621->104.19.229.21:443: wsarecv: An existing connection was forcibly closed by the remote host.
2026/06/04 16:50:09.324803 [Info] [2620548628] transport/internet/websocket: creating connection to tcp:104.19.229.21:443
2026/06/04 16:50:09.324803 [Debug] [2620548628] transport/internet: dialing to tcp:104.19.229.21:443
2026/06/04 16:50:09.492776 [Error] [2620548628] transport/internet/websocket: failed to dial to 104.19.229.21:443 > read tcp 10.244.46.253:21622->104.19.229.21:443: wsarecv: An existing connection was forcibly closed by the remote host.
2026/06/04 16:50:09.704340 [Info] [2620548628] transport/internet/websocket: creating connection to tcp:104.19.229.21:443
2026/06/04 16:50:09.704555 [Debug] [2620548628] transport/internet: dialing to tcp:104.19.229.21:443
2026/06/04 16:50:09.905114 [Error] [2620548628] transport/internet/websocket: failed to dial to 104.19.229.21:443 > read tcp 10.244.46.253:21623->104.19.229.21:443: wsarecv: An existing connection was forcibly closed by the remote host.
2026/06/04 16:50:10.305286 [Info] [2620548628] transport/internet/websocket: creating connection to tcp:104.19.229.21:443
2026/06/04 16:50:10.305410 [Debug] [2620548628] transport/internet: dialing to tcp:104.19.229.21:443
2026/06/04 16:50:10.490815 [Error] [2620548628] transport/internet/websocket: failed to dial to 104.19.229.21:443 > read tcp 10.244.46.253:21625->104.19.229.21:443: wsarecv: An existing connection was forcibly closed by the remote host.
2026/06/04 16:50:11.091239 [Info] [2620548628] transport/internet/websocket: creating connection to tcp:104.19.229.21:443
2026/06/04 16:50:11.091456 [Debug] [2620548628] transport/internet: dialing to tcp:104.19.229.21:443
2026/06/04 16:50:11.302958 [Error] [2620548628] transport/internet/websocket: failed to dial to 104.19.229.21:443 > read tcp 10.244.46.253:21627->104.19.229.21:443: wsarecv: An existing connection was forcibly closed by the remote host.
2026/06/04 16:50:12.103545 [Info] [2620548628] app/proxyman/outbound: app/proxyman/outbound: failed to process outbound traffic > proxy/vless/outbound: failed to find an available destination > common/retry: [transport/internet/websocket: failed to dial WebSocket > transport/internet/websocket: failed to dial to (wss://104.19.229.21/path):  > read tcp 10.244.46.253:21621->104.19.229.21:443: wsarecv: An existing connection was forcibly closed by the remote host. transport/internet/websocket: failed to dial WebSocket > transport/internet/websocket: failed to dial to (wss://104.19.229.21/path):  > read tcp 10.244.46.253:21622->104.19.229.21:443: wsarecv: An existing connection was forcibly closed by the remote host. transport/internet/websocket: failed to dial WebSocket > transport/internet/websocket: failed to dial to (wss://104.19.229.21/path):  > read tcp 10.244.46.253:21623->104.19.229.21:443: wsarecv: An existing connection was forcibly closed by the remote host. transport/internet/websocket: failed to dial WebSocket > transport/internet/websocket: failed to dial to (wss://104.19.229.21/path):  > read tcp 10.244.46.253:21625->104.19.229.21:443: wsarecv: An existing connection was forcibly closed by the remote host. transport/internet/websocket: failed to dial WebSocket > transport/internet/websocket: failed to dial to (wss://104.19.229.21/path):  > read tcp 10.244.46.253:21627->104.19.229.21:443: wsarecv: An existing connection was forcibly closed by the remote host.] > common/retry: all retry attempts failed
2026/06/04 16:50:12.104520 [Debug] [484192671] proxy/socks: Not Socks request, try to parse as HTTP request
2026/06/04 16:50:12.105264 [Info] [484192671] proxy/http: request to Method [CONNECT] Host [www.youtube.com:443] with URL [//www.youtube.com:443]
2026/06/04 16:50:12.105816 [Info] [484192671] app/dispatcher: sniffed domain: www.youtube.com
2026/06/04 16:50:12.105816 [Info] [484192671] app/dispatcher: default route for tcp:www.youtube.com:443
2026/06/04 16:50:12.106348 [Info] [484192671] transport/internet/websocket: creating connection to tcp:104.19.229.21:443
2026/06/04 16:50:12.106348 [Debug] [484192671] transport/internet: dialing to tcp:104.19.229.21:443
2026/06/04 16:50:12.105111 from 127.0.0.1:21628 accepted //www.youtube.com:443 [socks >> proxy]
2026/06/04 16:50:12.270323 [Error] [484192671] transport/internet/websocket: failed to dial to 104.19.229.21:443 > read tcp 10.244.46.253:21629->104.19.229.21:443: wsarecv: An existing connection was forcibly closed by the remote host.
2026/06/04 16:50:12.270323 [Info] [484192671] transport/internet/websocket: creating connection to tcp:104.19.229.21:443
2026/06/04 16:50:12.271289 [Debug] [484192671] transport/internet: dialing to tcp:104.19.229.21:443
2026/06/04 16:50:12.430042 [Error] [484192671] transport/internet/websocket: failed to dial to 104.19.229.21:443 > read tcp 10.244.46.253:21630->104.19.229.21:443: wsarecv: An existing connection was forcibly closed by the remote host.
2026/06/04 16:50:12.630325 [Info] [484192671] transport/internet/websocket: creating connection to tcp:104.19.229.21:443
2026/06/04 16:50:12.630472 [Debug] [484192671] transport/internet: dialing to tcp:104.19.229.21:443
2026/06/04 16:50:12.823680 [Error] [484192671] transport/internet/websocket: failed to dial to 104.19.229.21:443 > read tcp 10.244.46.253:21632->104.19.229.21:443: wsarecv: An existing connection was forcibly closed by the remote host.
2026/06/04 16:50:13.214460 [Info] [484192671] transport/internet/websocket: creating connection to tcp:104.19.229.21:443
2026/06/04 16:50:13.214685 [Debug] [484192671] transport/internet: dialing to tcp:104.19.229.21:443
2026/06/04 16:50:13.405304 [Error] [484192671] transport/internet/websocket: failed to dial to 104.19.229.21:443 > read tcp 10.244.46.253:21633->104.19.229.21:443: wsarecv: An existing connection was forcibly closed by the remote host.
2026/06/04 16:50:14.005625 [Info] [484192671] transport/internet/websocket: creating connection to tcp:104.19.229.21:443
2026/06/04 16:50:14.005840 [Debug] [484192671] transport/internet: dialing to tcp:104.19.229.21:443
2026/06/04 16:50:14.190617 [Error] [484192671] transport/internet/websocket: failed to dial to 104.19.229.21:443 > read tcp 10.244.46.253:21634->104.19.229.21:443: wsarecv: An existing connection was forcibly closed by the remote host.
2026/06/04 16:50:14.978842 [Info] [484192671] app/proxyman/outbound: app/proxyman/outbound: failed to process outbound traffic > proxy/vless/outbound: failed to find an available destination > common/retry: [transport/internet/websocket: failed to dial WebSocket > transport/internet/websocket: failed to dial to (wss://104.19.229.21/path):  > read tcp 10.244.46.253:21629->104.19.229.21:443: wsarecv: An existing connection was forcibly closed by the remote host. transport/internet/websocket: failed to dial WebSocket > transport/internet/websocket: failed to dial to (wss://104.19.229.21/path):  > read tcp 10.244.46.253:21630->104.19.229.21:443: wsarecv: An existing connection was forcibly closed by the remote host. transport/internet/websocket: failed to dial WebSocket > transport/internet/websocket: failed to dial to (wss://104.19.229.21/path):  > read tcp 10.244.46.253:21632->104.19.229.21:443: wsarecv: An existing connection was forcibly closed by the remote host. transport/internet/websocket: failed to dial WebSocket > transport/internet/websocket: failed to dial to (wss://104.19.229.21/path):  > read tcp 10.244.46.253:21633->104.19.229.21:443: wsarecv: An existing connection was forcibly closed by the remote host. transport/internet/websocket: failed to dial WebSocket > transport/internet/websocket: failed to dial to (wss://104.19.229.21/path):  > read tcp 10.244.46.253:21634->104.19.229.21:443: wsarecv: An existing connection was forcibly closed by the remote host.] > common/retry: all retry attempts failed

@codewithtamim
Copy link
Copy Markdown
Contributor Author

codewithtamim commented Jun 4, 2026

@Ahmad-2213 Can you test with reality? and with a different machine?

Also try with the format I gave for raw packet in example try to use the same sort of configuration and reduce the ttl

@Ahmad-2213
Copy link
Copy Markdown

Ahmad-2213 commented Jun 4, 2026

@Ahmad-2213 Can you test with reality? and with a different machine?

Also try with the format I gave for raw packet in example try to use the same sort of configuration and reduce the ttl

unfortunately i don't have a server that i could configure reality for, however, i can redirect to an Open proxy via freedom + final mask if solves the problem. (e.g. "redirect":"IP:443" , TCP + TLS )

I meassured the TTL of IP before Setting TTL .

updated configuration :

{
  "log": {
    "loglevel": "warning"
  },
  "inbounds": [
    {
      "tag": "socks",
      "port": 10808,
      "listen": "127.0.0.1",
      "protocol": "socks",
      "settings": {
        "auth": "noauth"
      }
    }
  ],
  "outbounds": [
    {
      "tag": "proxy",
      "protocol": "vless",
      "settings": {
        "vnext": [
          {
            "address": "104.19.229.21",
            "port": 443,
            "users": [
              {
                "id": "ID",
                "email": "t@t.tt",
                "security": "auto",
                "encryption": "none"
              }
            ]
          }
        ]
      },
      "streamSettings": {
        "network": "ws",
        "security": "tls",
        "tlsSettings": {
          "serverName": "example.com",
          "fingerprint": "chrome"
        },
        "wsSettings": {
          "path": "/path",
          "host": "example.com"
        },
        "finalmask": {
          "tcp": [
            {
              "type": "rawpacket",
              "settings": {
                "sni": "cloudflare.com",
                "method": "wrong-sequence"
              }
            }
          ]
        }
      }
    }
  ]
}

logs:

Xray 26.6.1 (Xray, Penetrates Everything.) cbede9a-dirty (go1.26.0 windows/amd64)
A unified platform for anti-censorship.
2026/06/04 17:31:40.187134 [Info] infra/conf/serial: Reading config: &{Name:config.json Format:json}
2026/06/04 17:31:40.187673 [Warning] common/errors: The feature WebSocket transport (with ALPN http/1.1, etc.) is deprecated, not recommended for using and might be removed. Please migrate to XHTTP H2 & H3 as soon as possible.
2026/06/04 17:31:40.189222 [Warning] core: Xray 26.6.1 started
2026/06/04 17:31:40.639721 from 127.0.0.1:22786 accepted //fonts.gstatic.com:443 [socks >> proxy]
2026/06/04 17:31:40.654932 from 127.0.0.1:22787 accepted //unpkg.com:443 [socks >> proxy]
2026/06/04 17:31:40.654932 from 127.0.0.1:22785 accepted //iplocation.com:443 [socks >> proxy]
2026/06/04 17:31:40.861233 [Error] [170264619] transport/internet/websocket: failed to dial to 104.19.229.21:443 > read tcp 10.244.46.253:22789->104.19.229.21:443: wsarecv: An existing connection was forcibly closed by the remote host.
2026/06/04 17:31:40.861233 [Error] [2059557928] transport/internet/websocket: failed to dial to 104.19.229.21:443 > read tcp 10.244.46.253:22788->104.19.229.21:443: wsarecv: An existing connection was forcibly closed by the remote host.
2026/06/04 17:31:40.861975 [Error] [1363289195] transport/internet/websocket: failed to dial to 104.19.229.21:443 > read tcp 10.244.46.253:22790->104.19.229.21:443: wsarecv: An existing connection was forcibly closed by the remote host.
2026/06/04 17:31:41.046264 [Error] [1363289195] transport/internet/websocket: failed to dial to 104.19.229.21:443 > read tcp 10.244.46.253:22792->104.19.229.21:443: wsarecv: An existing connection was forcibly closed by the remote host.
2026/06/04 17:31:41.052596 [Error] [2059557928] transport/internet/websocket: failed to dial to 104.19.229.21:443 > read tcp 10.244.46.253:22791->104.19.229.21:443: wsarecv: An existing connection was forcibly closed by the remote host.

@codewithtamim
Copy link
Copy Markdown
Contributor Author

@Ahmad-2213 Can you test with reality? and with a different machine?
Also try with the format I gave for raw packet in example try to use the same sort of configuration and reduce the ttl

unfortunately i don't have a server that i could configure reality for, however, i can redirect to an Open proxy via freedom + final mask if solves the problem. (e.g. "redirect":"IP:443" , TCP + TLS )

I meassured the TTL of IP before Setting TTL .

updated configuration :

{
  "log": {
    "loglevel": "warning"
  },
  "inbounds": [
    {
      "tag": "socks",
      "port": 10808,
      "listen": "127.0.0.1",
      "protocol": "socks",
      "settings": {
        "auth": "noauth"
      }
    }
  ],
  "outbounds": [
    {
      "tag": "proxy",
      "protocol": "vless",
      "settings": {
        "vnext": [
          {
            "address": "104.19.229.21",
            "port": 443,
            "users": [
              {
                "id": "ID",
                "email": "t@t.tt",
                "security": "auto",
                "encryption": "none"
              }
            ]
          }
        ]
      },
      "streamSettings": {
        "network": "ws",
        "security": "tls",
        "tlsSettings": {
          "serverName": "example.com",
          "fingerprint": "chrome"
        },
        "wsSettings": {
          "path": "/path",
          "host": "example.com"
        },
        "finalmask": {
          "tcp": [
            {
              "type": "rawpacket",
              "settings": {
                "sni": "cloudflare.com",
                "method": "wrong-sequence"
              }
            }
          ]
        }
      }
    }
  ]
}

logs:

Xray 26.6.1 (Xray, Penetrates Everything.) cbede9a-dirty (go1.26.0 windows/amd64)
A unified platform for anti-censorship.
2026/06/04 17:31:40.187134 [Info] infra/conf/serial: Reading config: &{Name:config.json Format:json}
2026/06/04 17:31:40.187673 [Warning] common/errors: The feature WebSocket transport (with ALPN http/1.1, etc.) is deprecated, not recommended for using and might be removed. Please migrate to XHTTP H2 & H3 as soon as possible.
2026/06/04 17:31:40.189222 [Warning] core: Xray 26.6.1 started
2026/06/04 17:31:40.639721 from 127.0.0.1:22786 accepted //fonts.gstatic.com:443 [socks >> proxy]
2026/06/04 17:31:40.654932 from 127.0.0.1:22787 accepted //unpkg.com:443 [socks >> proxy]
2026/06/04 17:31:40.654932 from 127.0.0.1:22785 accepted //iplocation.com:443 [socks >> proxy]
2026/06/04 17:31:40.861233 [Error] [170264619] transport/internet/websocket: failed to dial to 104.19.229.21:443 > read tcp 10.244.46.253:22789->104.19.229.21:443: wsarecv: An existing connection was forcibly closed by the remote host.
2026/06/04 17:31:40.861233 [Error] [2059557928] transport/internet/websocket: failed to dial to 104.19.229.21:443 > read tcp 10.244.46.253:22788->104.19.229.21:443: wsarecv: An existing connection was forcibly closed by the remote host.
2026/06/04 17:31:40.861975 [Error] [1363289195] transport/internet/websocket: failed to dial to 104.19.229.21:443 > read tcp 10.244.46.253:22790->104.19.229.21:443: wsarecv: An existing connection was forcibly closed by the remote host.
2026/06/04 17:31:41.046264 [Error] [1363289195] transport/internet/websocket: failed to dial to 104.19.229.21:443 > read tcp 10.244.46.253:22792->104.19.229.21:443: wsarecv: An existing connection was forcibly closed by the remote host.
2026/06/04 17:31:41.052596 [Error] [2059557928] transport/internet/websocket: failed to dial to 104.19.229.21:443 > read tcp 10.244.46.253:22791->104.19.229.21:443: wsarecv: An existing connection was forcibly closed by the remote host.

I have a few, dm me on tg(same username) I will give you multiple variations of the config, just test and give me feedback

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

6 participants