net4people / bbs

Forum for discussing Internet censorship circumvention
3.19k stars 75 forks source link

TLS record fragmentation, DPYProxy #308

Open wkrp opened 7 months ago

wkrp commented 7 months ago

Circumventing the GFW with TLS Record Fragmentation Niklas Niere (@JonSnowWhite) https://github.com/UPB-SysSec/DPYProxy

This blog post explores an interesting idea: fragmenting TLS messages (especially Client Hello) over multiple TLS records. This is different from TCP segmentation, which has been studied in the past by, e.g., Winter & Lindskog 2012 (Section 5.2, brdgrd), Khattak et al. 2013, and Bock et al. 2021 (Section 4.1), and is implemented in tools including GoodbyeDPI. Rather, this research takes advantage of the fact that TLS messages (e.g. Client Hello) are carried in TLS records, and that one message may be fragmented over multiple records:

The TLS Record Protocol is a layered protocol. At each layer, messages may include fields for length, description, and content. The Record Protocol takes messages to be transmitted, fragments the data into manageable blocks, optionally compresses the data, applies a MAC, encrypts, and transmits the result.

The post introduces a tool, DPYProxy, that fragments TLS messages over multiple records (by parsing the user's TLS records and re-wrapping them in multiple shorter records). They test the tool in China and find that when a Client Hello message is fragmented at the TLS record layer, the SNI is not interpreted by the firewall and the connection doesn't get blocked. (Surprisingly, TCP-layer segmentation also worked in their tests—as long as the Client Hello message is broken before the SNI extension.)

HectorHW commented 7 months ago

Verified to work (for now at least) against Roskomnadzor blocking https websites in Russia. Nice finding!

JonSnowWhite commented 7 months ago

@HectorHW Thanks for the info!

ghost commented 7 months ago

I believe both TCP Segmentation and TLS Hello Fragmentation are implemented in Xray-core. Although, some CDNs like Fastly are not compatible with these methods, they work fine with majority of websites in Iran using different ISPs. Also, to my understanding @rprx and Chinese community insist that these methods do not work in China.

klzgrad commented 7 months ago

It is less likely for realtime systems to handle TCP or TLS reassembly due to its computation or memory cost at scale, in realtime, but this is not a fact that can be relied on in general. During a previous blockage of Cloudflare in https://github.com/net4people/bbs/issues/295 I tested various ways of TCP fragmentation of TLS Hello and was able to trigger TCP resets in all variations. I believe in general it is more likely for offline and deeper traffic analysis to perform traffic reassembly as realtime decisions are not required.

RPRX commented 7 months ago

@5e2t 针对河南 GFW 和广州国际出口 GFW 测试过 tlshello 分片:https://github.com/XTLS/Xray-core/issues/2426#issuecomment-1719685919

我的观点是这些分片 可能可用,但 GFW 并不需要重组它们,目前来说,一个报文只含一部分而不是完整的 client hello 即为异常

@5e2t tested tlshello fragmentation against Henan GFW and Guangzhou international outbound GFW: https://github.com/XTLS/Xray-core/issues/2426#issuecomment-1719685919

My view is that these fragments may be usable, but the GFW doesn't need to reconstruct them, and for now, it's an anomaly for a message to contain only a portion of a client hello rather than the full one

JonSnowWhite commented 7 months ago

Should Record Fragmentation not work standalone, it can hopefully be combined with TCP fragmentation. That might be something to try out for circumvention tools like XRay. Especially when the TCP and TLS Records are of different size/in different order.

5e2t commented 7 months ago

@5e2t 针对河南 GFW 和广州国际出口 GFW 测试过 tlshello 分片:XTLS/Xray-core#2426 (comment)

我的观点是这些分片 可能可用,但 GFW 并不需要重组它们,目前来说,一个报文只含一部分而不是完整的 client hello 即为异常

@5e2t tested tlshello fragmentation against Henan GFW and Guangzhou international outbound GFW: XTLS/Xray-core#2426 (comment)

My view is that these fragments may be usable, but the GFW doesn't need to reconstruct them, and for now, it's an anomaly for a message to contain only a portion of a client hello rather than the full one

河南GFW能阻断servername位于第一个clienthello分片的TLS握手,对于VLESS-TCP不开TLS的翻墙流量则未触发阻断,说明并不是用hex string的方式识别特定域名的,而是识别出来clienthello握手包了。

Henan GFW can block servername located in the first clienthello fragment of the TLS handshake. As for VLESS-TCP, it does not open TLS flows that trigger blocking, that shows it does not identify a specific domain name hex string, but the clienthello handshake packet.

5e2t commented 7 months ago

理论上要是用hex string来识别的话(iptables可以做到),则VLESS-TCP不开TLS的翻墙流量,以及server name写在第二个clienthello分片的流量都能被识别,但是直接靠16进制数对应的字符来识别域名肯定会造成流量的误伤吧。GFW应该肯定不会维护我跟目标IP的连接状态,也就是说gfw不管我有没有给对方发过syn,也不管我在这条连接上跟对方通信了多久,只看流量是不是clienthello,或者HTTP请求。那也就是说这种情况下用hex string来识别,必然造成流量的误伤(虽然我不知道概率是多少)...........

Theoretically, if you use hex string to identify the traffic (iptables can do it), then the fact that VLESS-TCP does not open TLS circumvention traffic, as well as that the server name written in the second clienthello fragment of the flow can also be recognized, however it directly relies on the hexadecimal number of the corresponding characters to identify the domain name, will certainly cause traffic to be mistakenly injured, right? GFW should definitely not maintain my connection status with the target IP, that is to say, GFW does not care whether I have sent syn to the other party or not, and does not care how long I have been communicating with the other party on this connection, it only looks at the traffic to see whether it is a clienthello, or an HTTP request. That means that using a hex string to classify in this case will inevitably cause the traffic to be misdirected (although I don't know what the probability is)...

5e2t commented 7 months ago

@RPRX 之前 河南数据流量网络上的墙 能识别出开了TFO的流量的 clienthello,应该就是因为 数据流量网络上的墙 会看 headerlenth,就是那个一比特代表4字节的字段,4个比特都置1 也就是代表整个报头最大60字节的那个字段

所以说并不是 河南数据流量网络上的墙,特别地对TFO流量做了应对,反倒说明 这个墙是不维护连接状态的。 也就是说,我与某个IP 某个端口建立连接以后,发送 SNI不位于黑名单中的 clienthello握手包,通信一段时间以后,在这条连接上,再发送一个 SNI位于黑名单中的 clienthello握手包,应该是一定会收到阻断的。

所以固定宽带网络上的墙为什么那么傻,连HeaderLenth都不看(兴许是故意不看的.............)

Previously, the wall on Henan's data traffic network was able to recognize the clienthello of the TFO traffic because the wall on the data traffic network would look at the headerlenth, that is, the field where one bit represents 4 bytes, and all 4 bits are set to 1, that is, the field representing the maximum of 60 bytes of the entire header.

So it's not that the wall on Henan's data traffic network specifically responds to TFO traffic, but rather that the wall doesn't maintain connection status. That is to say, after I establish a connection with a certain IP port, I send a clienthello handshake packet with SNI not in the blacklist, and after a period of communication, on this connection, I send another clienthello handshake packet with SNI in the blacklist, and I should be sure to receive a block.

So why are the walls on fixed broadband networks so stupid that they don't even look at HeaderLenth (perhaps on purpose...)

5e2t commented 7 months ago

@RPRX 会看HeaderLenth当然也算不上先进,之前我对计算机网络知识的了解还不够,所以才觉得河南数据流量网络上的墙 先进,现在看来,这一切似乎都是故意的

Looking at HeaderLenth is certainly not considered advanced, earlier I did not have enough knowledge of computer networks, and that's why I considered the wall on the network of Henan data traffic to be advanced, now it all seems to be intentional

5e2t commented 7 months ago

@RPRX 那么问题来了, TCP Timestamps和TCP Fast Open均绕不过 河南数据流量网络GFW的SNI阻断的原因已经知道了,就是无状态+会看HeaderLenth, TCP Timestamps和TCP Fast Open均能绕过河南固定宽带网络GFW的SNI阻断的原因也知道了,是无状态+不看HeaderLenth

那么 TCP Timestamps绕不过北上广出口GFW的SNI阻断,TCP Fast Open可以绕过北上广出口GFW的SNI阻断,是说明北上广出口GFW是无状态的还是有状态的,到底是看HeaderLenth还是不看HeaderLenth................

So here's the question. The reason why TCP Timestamps and TCP Fast Open cannot bypass the Henan data traffic network GFW SNI blocking is known, it is stateless + looks at the HeaderLenth. The reason why TCP Timestamps and TCP Fast Open can bypass the Henan fixed broadband network GFW SNI blocking is also known, is is stateless + does not look at the HeaderLenth

So, given the facts that TCP Timestamps cannot bypass GFW SNI blocking in the north, and TCP Fast Open can bypass GFW SNI blocking in the north, does it mean that the GFW in the north is stateless or stateful? In the end, does it look at the HeaderLenth or does it not look at HeaderLenth?...

5e2t commented 7 months ago

@RPRX 无论是河南固定宽带网络上的GFW还是河南数据流量网络上的GFW,都能识别出servername位于第一个clienthello分片的TLS握手,并且 河南GFW的行为与 tcp的 HeaderLenth高度相关联, 我有很大的信心确认,河南GFW是靠 应用层的前几个或前十几个字节来识别TLS Clienthello的................

Both the GFW on Henan's fixed broadband network and the GFW on Henan's data traffic network are able to recognize TLS handshakes in which servername is located in the first clienthello fragment, and the behavior of Henan's GFW is highly correlated with the tcp HeaderLenth, and I have a great deal of confidence in confirming that Henan's GFW is relying on the application layer's first few or first dozen or so bytes at the application layer to recognize the TLS Clienthello...

5e2t commented 7 months ago

@RPRX 北上广出口GFW识别不出 servername 写在了第一个clienthello分片的TLS握手,更像是”有状态“ 行为...... TCP Timestamps绕不过北上广出口GFW的SNI阻断, 只能说明 北上广出口GFW会看TCP HeaderLenth。 前面已经知道了,无状态+看HeaderLenth可识别TFO的clienthello, TCP FastOpen可以绕过北上广出口GFW的SNI阻断,大概也许说明了 TCP FastOpen的clienthello流量无法被GFW识别为syn,或者说,被识别为Syn了,然后就忽略了这个包的应用层数据了..........

就是说只能说明北上广GFW是有状态+看HeaderLenth的。。。。

Northbound exiting GFW can't recognize TLS handshakes where the servername is written in the first clienthello fragment, more like "stateful" behavior ...... TCP Timestamps can't bypass the SNI blocking of the GFW, which only means that the GFW can look at the TCP HeaderLenth. As you already know, statelessness + looking at HeaderLenth can recognize the clienthello of TFO, TCP FastOpen can bypass the SNI blocking of the northbound exiting GFW, probably perhaps indicating that the clienthello traffic of TCP FastOpen cannot be recognized by the GFW as syn, or rather, is recognized as Syn, but then the packet's application layer data is ignored...

That is, it can only mean that the Northbound GFW is stateful + looks at HeaderLenth...

5e2t commented 7 months ago

@RPRX 北上广出口GFW 可能会维护连接状态,就是说,以每条TCP连接 为单位,只看 每条连接的前几个包,或者干脆是只看客户端发的第一个包............,如果客户端发的第一个包 不触发阻断,那么 此条连接后续所有通信 内容 将不再 被 检测..............

(以上为纯粹的猜测~~~~~)

Northbound exiting GFW may maintain the connection status, that is to say, in terms of each TCP connection, it only looks at the first few packets of each connection, or simply only looks at the first packet sent by the client... If the first packet sent by the client does not trigger a block, then all subsequent traffic on the connection will no longer be detected...

(The above is pure speculation~~~~~)

5e2t commented 7 months ago

@RPRX 我个人认为 TCP Fast Open之所以能绕过 北上广出口GFW的SNI阻断,是因为北上广出口GFW 期望看到syn用以维护连接状态,然后syn包的应用层数据就理所当然的忽略掉了(因为在等着看后面一个包是不是Clienthello..........)。

而河南GFW 由于不维护连接状态,则期望看到的是TLS Clienthello握手包,所以 这就是同样的一个开了TCP Fast Open的TLS clienthello握手包,在 河南GFW和国际出口GFW那里有不同的表现的原因吧~~~

Personally, I think the reason why TCP Fast Open bypasses the SNI blocking of the northbound exit GFW is because the northbound exit GFW expects to see a syn to maintain the connection status, and then the syn packet's application-layer data is rightly ignored (because it's waiting to see if the later packet is Clienthello...).

The Henan GFW expects to see TLS Clienthello handshake packets because it doesn't maintain the connection state, so that's why the same TLS Clienthello handshake packet with TCP Fast Open is treated differently by the Henan GFW and the international exit GFWs, right?~~~

5e2t commented 7 months ago

同样的一个开了TCP Fast Open的TLS clienthello握手包,在北上广出口GFW眼里是 syn, 在河南GFW眼里是TLS clienthello握手包。

The same TLS clienthello handshake packet with TCP Fast Open on is a syn in the eyes of the Northbound exit GFW and a TLS clienthello handshake packet in the eyes of the Henan GFW.

5e2t commented 7 months ago

当然,我其实 最想强调的还是,河南GFW检测TCP流量的方式 , 跟对待UDP流量似的,以每个数据包为单位,对每个包 读取应用层前十几个字节 来确认是不是TLS的clienthello,兴许代码里压根没有检测syn的行为。

北上广出口GFW检测TCP流量的方式 才真正的像对待TCP流量,以每条连接为单位。

与此同时有个细节,众所周知workers.dev域名被北上广出口GFW给SNI阻断了,但是阻断的只是TLS,没有阻断对此域名的HTTP请求 由于河南GFW部署了对HTTP的HOST的检测,恰巧workers.dev域名也在河南GFW的黑名单里,所以河南部署域名黑名单墙以后,连对workers.dev的HTTP连接也被RST阻断。

而我们都知道HTTP是持久连接的,同一条连接上可以进行多次的HTTP GET/POST之类的请求,也正是河南GFW的这种 无状态检测,可以有效阻断明文HTTP连接。。(不过话说回来同一条TCP连接上进行的多次HTTP请求难道还能换HOST的吗.........暂且认为可以吧~~~)

北上广出口GFW对待HTTP流量的历史就源远流长了~具体细节我也不知道。

如果只讨论河南GFW的话,此GFW在读取一个包的前十几个字节之前,也不知道这个包到底是TLS的clienthello握手还是HTTP GET/POST 之类的请求,如果是在一条连接已经通信了很久的情况下,发送了一个新的HTTP GET/POST请求当然是非常合理的,

当然如果一条TLS连接已经通信了很久,又在此连接上发送了一个新的明文clienthello握手包, 看起来河南GFW是可以阻断这个 已经通信了很久的连接 上的 新发送的明文clienthello握手包的..........

(毕竟原来的目的是为了阻断“一个通信了很久的连接” 上 《新的HTTP GET/POST请求》,能阻断“已经通信了很久的连接“ 上的 《新发送的明文clienthello握手包》是 额外的效果.....)

所以河南GFW可能是为了阻断 非TLS的 HTTP请求,才选用了无状态策略,没有别的复杂原因。。

话说回来,检测 非TLS的 HTTP请求 ,也是必须从应用层的第一个字节开始 读吧.........而读之前是不知道这个数据包 属于 非TLS的 HTTP请求,还是TLS的clienthello的,所以可能是 用 更利于 检测HTTP流量的方式读取的, 检测TLS流量是顺便的。


Of course, I actually want to emphasize that the way the Henan GFW detects TCP traffic, as with UDP traffic, is each packet as a unit, for each packet to read the first dozen or so bytes of the application layer to confirm that it is not a TLS clienthello, perhaps the code simply does not detect the syn behavior.

The way GFW detects TCP traffic on the north exit is really like treating TCP traffic in units of connections.

At the same time there is a detail, it is known that the domain name workers.dev is blocked by the north exit of GFW by SNI, but the block is only for TLS, it does not block the domain name of HTTP requests As Henan GFW deployed the detection of HTTP HOST, coincidentally, the domain name of workers.dev was also in Henan GFW's blacklist, so after Henan deployed the domain name blacklist wall, even the HTTP connection of workers.dev was also blocked by RST.

We all know that HTTP is a persistent connection, the same connection can be used for multiple HTTP GET/POST requests, it is also known that Henan GFW's stateless detection, can effectively block plaintext HTTP connections. (But then again, multiple HTTP requests on the same TCP connection can still change their HOST... For the time being, I think it is possible~~~)

The history of GFW's treatment of HTTP traffic in the north of the country has a long history, and I don't know the details.

If we only discuss the Henan GFW, this GFW does not know whether the packet is a TLS clienthello handshake or an HTTP GET/POST request until it reads the first dozen or so bytes of the packet, and if it sends a new HTTP GET/POST request when a connection has already been communicating for a long time, of course it's very reasonable, and it's not a good idea for the GFW to send a new request for the first dozen bytes of the packet.

Of course, if a TLS connection has been communicating for a long time, and a new plaintext clienthello handshake packet is sent on this connection, it looks like Henan GFW is able to block the new plaintext clienthello handshake packet sent on this connection that has been communicating for a long time...

(After all, the original purpose is to block "new HTTP GET/POST request" on a "long-term connection", the ability to block "new clienthello handshake packets" on a "long-term connection" is an added effect...)

So it is possible that GFW chose the stateless policy to block non-TLS HTTP requests, but not for any other complicated reasons.

Having said that, detecting non-TLS HTTP requests, it is also necessary to read the first byte from the application layer, right?... And before reading it is not known whether the packet belongs to a non-TLS HTTP request or a TLS clienthello, so it is probably read in a way that is more conducive to detecting HTTP traffic, and detecting TLS traffic is incidental.

5e2t commented 7 months ago

@RPRX 所以有没有可能是因为 河南GFW的检测算法,不是专门针对检测TLS流量设计的,而是针对检测HTTP流量设计的,所以才造成了,河南GFW可以阻断 servername位于第一个clienthello分片的TLS握手包。

如果他真想好好的检测TLS握手包,兴许就会像国际出口GFW那样,如果不读到一个完整的clienthello包,就不判断为是TLS 的clienthello握手了。

从应用层第一个字节开始,一个字节一个字节的读真的很像 HTTP服务器的行为呀~~~

So is it possible that the Henan GFW's detection algorithm is not specifically designed for detecting TLS traffic, but for detecting HTTP traffic, which is why Henan GFW can block the TLS handshake packet where servername is located in the first clienthello fragment.

If it really wants to properly detect TLS handshake packets, perhaps it will be like the international export GFW, if you do not read a complete clienthello packet, it will not be judged as a TLS clienthello handshake.

Starting from the first byte of the application layer, reading byte by byte is really like the behavior of an HTTP server~~~

5e2t commented 7 months ago

河南GFW有个特点是对于黑名单里的所有域名一律阻断HTTP HOST和TLS SNI,这一点跟国际出口GFW的行为是很不一样的。国际出口GFW对HTTP HOST的阻断跟TLS SNI像是两个系统。

One feature of Henan GFW is to block HTTP HOST and TLS SNI for all domain names in the blacklist, which is very different from the behavior of international exit GFW. The blocking of HTTP HOST and TLS SNI by international exit GFW is like two systems.

5e2t commented 7 months ago

河南GFW阻断HTTP HOST用的RST包跟阻断TLS SNI用的RST包应当是没有任何区别的。

There should be no difference between the RST packet used by Henan GFW to block HTTP HOST and the RST packet used to block TLS SNI.

fortuna commented 7 months ago

FYI, I tested TLS Record fragmentation with DPYProxy last month and was able to demonstrate that it bypasses SNI-based blocking in an network in Russia and one in Iran.

I also realized that leaving a single byte of the first message out of the first record was enough to bypass censorship. That suggests to me that they just give up parsing TLS. Perhaps they require the message length to be the record length -5 in their TLS detector. This may help against blocking based on TLS fingerprint because of that.

See the commands I used for testing at https://github.com/UPB-SysSec/DPYProxy/issues/2#issuecomment-1769539007

This is how the TCP fragmentation looks like on tshark:

Transmission Control Protocol, Src Port: 40334, Dst Port: 443, Seq: 1143, Ack: 100, Len: 23
[21 Reassembled TCP Segments (517 bytes): #28(19), #29(25), #30(25), #31(25), #32(25), #33(25), #34(25), #35(25), #36(25), #37(25), #38(25), #39(25), #40(25), #41(25), #42(25), #43(25), #44(25), #45(25), #46(25), #47(25), #48(23)]
Transport Layer Security
    TLSv1.3 Record Layer: Handshake Protocol: Client Hello
        Content Type: Handshake (22)
        Version: TLS 1.2 (0x0303)
        Length: 512
        Handshake Protocol: Client Hello
            Handshake Type: Client Hello (1)
            Length: 508
            Version: TLS 1.2 (0x0303)

This is how it looks with TLS record fragmentation only:

Transmission Control Protocol, Src Port: 58772, Dst Port: 443, Seq: 1, Ack: 1, Len: 642
Transport Layer Security
    TLSv1 Record Layer: Handshake Protocol: Client Hello (fragment)
        Content Type: Handshake (22)
        Version: TLS 1.0 (0x0301)
        Length: 20
        Handshake Protocol: Client Hello (fragment)
    TLSv1 Record Layer: Handshake Protocol: Multiple Handshake Messages
        Content Type: Handshake (22)
        Version: TLS 1.0 (0x0301)
        Length: 20
        Handshake Protocol: Client Hello (fragment)
    TLSv1 Record Layer: Handshake Protocol: Multiple Handshake Messages
        Content Type: Handshake (22)
        Version: TLS 1.0 (0x0301)
        Length: 20
        Handshake Protocol: Client Hello (fragment)
    TLSv1 Record Layer: Handshake Protocol: Multiple Handshake Messages
        Content Type: Handshake (22)
        Version: TLS 1.0 (0x0301)
        Length: 20
        Handshake Protocol: Client Hello (fragment)
    TLSv1 Record Layer: Handshake Protocol: Multiple Handshake Messages
        Content Type: Handshake (22)
        Version: TLS 1.0 (0x0301)
        Length: 20
        Handshake Protocol: Client Hello (fragment)
    TLSv1 Record Layer: Handshake Protocol: Multiple Handshake Messages
        Content Type: Handshake (22)
        Version: TLS 1.0 (0x0301)
        Length: 20
        Handshake Protocol: Client Hello (fragment)
    TLSv1 Record Layer: Handshake Protocol: Multiple Handshake Messages
        Content Type: Handshake (22)
        Version: TLS 1.0 (0x0301)
        Length: 20
        Handshake Protocol: Client Hello (fragment)
    TLSv1 Record Layer: Handshake Protocol: Multiple Handshake Messages
        Content Type: Handshake (22)
        Version: TLS 1.0 (0x0301)
        Length: 20
        Handshake Protocol: Client Hello (fragment)
    TLSv1 Record Layer: Handshake Protocol: Multiple Handshake Messages
        Content Type: Handshake (22)
        Version: TLS 1.0 (0x0301)
        Length: 20
        Handshake Protocol: Client Hello (fragment)
    TLSv1 Record Layer: Handshake Protocol: Multiple Handshake Messages
        Content Type: Handshake (22)
        Version: TLS 1.0 (0x0301)
        Length: 20
        Handshake Protocol: Client Hello (fragment)
    TLSv1 Record Layer: Handshake Protocol: Multiple Handshake Messages
        Content Type: Handshake (22)
        Version: TLS 1.0 (0x0301)
        Length: 20
        Handshake Protocol: Client Hello (fragment)
    TLSv1 Record Layer: Handshake Protocol: Multiple Handshake Messages
        Content Type: Handshake (22)
        Version: TLS 1.0 (0x0301)
        Length: 20
        Handshake Protocol: Client Hello (fragment)
    TLSv1 Record Layer: Handshake Protocol: Multiple Handshake Messages
        Content Type: Handshake (22)
        Version: TLS 1.0 (0x0301)
        Length: 20
        Handshake Protocol: Client Hello (fragment)
    TLSv1 Record Layer: Handshake Protocol: Multiple Handshake Messages
        Content Type: Handshake (22)
        Version: TLS 1.0 (0x0301)
        Length: 20
        Handshake Protocol: Client Hello (fragment)
    TLSv1 Record Layer: Handshake Protocol: Multiple Handshake Messages
        Content Type: Handshake (22)
        Version: TLS 1.0 (0x0301)
        Length: 20
        Handshake Protocol: Client Hello (fragment)
    TLSv1 Record Layer: Handshake Protocol: Multiple Handshake Messages
        Content Type: Handshake (22)
        Version: TLS 1.0 (0x0301)
        Length: 20
        Handshake Protocol: Client Hello (fragment)
    TLSv1 Record Layer: Handshake Protocol: Multiple Handshake Messages
        Content Type: Handshake (22)
        Version: TLS 1.0 (0x0301)
        Length: 20
        Handshake Protocol: Client Hello (fragment)
    TLSv1 Record Layer: Handshake Protocol: Multiple Handshake Messages
        Content Type: Handshake (22)
        Version: TLS 1.0 (0x0301)
        Length: 20
        Handshake Protocol: Client Hello (fragment)
    TLSv1 Record Layer: Handshake Protocol: Multiple Handshake Messages
        Content Type: Handshake (22)
        Version: TLS 1.0 (0x0301)
        Length: 20
        Handshake Protocol: Client Hello (fragment)
    TLSv1 Record Layer: Handshake Protocol: Multiple Handshake Messages
        Content Type: Handshake (22)
        Version: TLS 1.0 (0x0301)
        Length: 20
        Handshake Protocol: Client Hello (fragment)
    TLSv1 Record Layer: Handshake Protocol: Multiple Handshake Messages
        Content Type: Handshake (22)
        Version: TLS 1.0 (0x0301)
        Length: 20
        Handshake Protocol: Client Hello (fragment)
    TLSv1 Record Layer: Handshake Protocol: Multiple Handshake Messages
        Content Type: Handshake (22)
        Version: TLS 1.0 (0x0301)
        Length: 20
        Handshake Protocol: Client Hello (fragment)
    TLSv1 Record Layer: Handshake Protocol: Multiple Handshake Messages
        Content Type: Handshake (22)
        Version: TLS 1.0 (0x0301)
        Length: 20
        Handshake Protocol: Client Hello (fragment)
    TLSv1 Record Layer: Handshake Protocol: Multiple Handshake Messages
        Content Type: Handshake (22)
        Version: TLS 1.0 (0x0301)
        Length: 20
        Handshake Protocol: Client Hello (fragment)
    TLSv1 Record Layer: Handshake Protocol: Multiple Handshake Messages
        Content Type: Handshake (22)
        Version: TLS 1.0 (0x0301)
        Length: 20
        Handshake Protocol: Client Hello (fragment)
    TLSv1 Record Layer: Handshake Protocol: Client Hello
        Content Type: Handshake (22)
        Version: TLS 1.0 (0x0301)
        Length: 12
        Handshake Protocol: Client Hello (last fragment)
        [26 Reassembled Handshake Fragments (512 bytes): #4(20), #4(20), #4(20), #4(20), #4(20), #4(20), #4(20), #4(20), #4(20), #4(20), #4(20), #4(20), #4(20), #4(20), #4(20), #4(20), #4(20), #4(20), #4(20), #4(20), #4(20), #4(20), #4(20), #4(20),]
            [Frame: 4, payload: 0-19 (20 bytes)]
            [Frame: 4, payload: 20-39 (20 bytes)]
            [Frame: 4, payload: 40-59 (20 bytes)]
            [Frame: 4, payload: 60-79 (20 bytes)]
            [Frame: 4, payload: 80-99 (20 bytes)]
            [Frame: 4, payload: 100-119 (20 bytes)]
            [Frame: 4, payload: 120-139 (20 bytes)]
            [Frame: 4, payload: 140-159 (20 bytes)]
            [Frame: 4, payload: 160-179 (20 bytes)]
            [Frame: 4, payload: 180-199 (20 bytes)]
            [Frame: 4, payload: 200-219 (20 bytes)]
            [Frame: 4, payload: 220-239 (20 bytes)]
            [Frame: 4, payload: 240-259 (20 bytes)]
            [Frame: 4, payload: 260-279 (20 bytes)]
            [Frame: 4, payload: 280-299 (20 bytes)]
            [Frame: 4, payload: 300-319 (20 bytes)]
            [Frame: 4, payload: 320-339 (20 bytes)]
            [Frame: 4, payload: 340-359 (20 bytes)]
            [Frame: 4, payload: 360-379 (20 bytes)]
            [Frame: 4, payload: 380-399 (20 bytes)]
            [Frame: 4, payload: 400-419 (20 bytes)]
            [Frame: 4, payload: 420-439 (20 bytes)]
            [Frame: 4, payload: 440-459 (20 bytes)]
            [Frame: 4, payload: 460-479 (20 bytes)]
            [Frame: 4, payload: 480-499 (20 bytes)]
            [Frame: 4, payload: 500-511 (12 bytes)]
            [Handshake Fragment count: 26]
        Handshake Protocol: Client Hello
            Handshake Type: Client Hello (1)
            Length: 508
            Version: TLS 1.2 (0x0303)

TCP segmentation

In Iran, TCP segmentation alone didn't help.

In Russia, TCP alone could bypass SNI-based censorship, but only on byte 2 (as in [01][2...]), and sometimes on byte 1 (as in [0][1...]). That split is right in the middle of the TLS version, which may confuse the TLS detection and make them give up TLS analysis.