Trojan-killer
Trojan-killer copied to clipboard
简单的测试结果以及实验建议
感谢大佬分享,我稍微在本地测试了一下。
Xray + Example 内用例测试
通过example里面的config,
Firefox: [email protected]:xxxxx -------> Xray [email protected]:11111 ------> Trojan [email protected]:12345 --------> Trojan [email protected]:22222 ------------> Freedom
准确率非常高。
其他Trojan实现测试
此外我分别用手机上的Sagernet trojan以及trojan-go连接trojan服务器,trojan的服务器是我自己的个人项目,用rust写的。在所有trojan的代理连接中,大概10% - 20%的请求能够被识别出来是trojan proxy,但是波动比较大,有时候好几分钟都不会识别出来一个,这可能跟rustls或者我自己的实现有关。我手动uncomment那行print,以下是一小部分的日志(这边手动把代理服务器写死到127.0.0.1:12346)
127.0.0.1:12346 upCount 334 downCount 4284
127.0.0.1:12346 upCount 671 downCount 4216
is Trojan
127.0.0.1:12346 upCount 1442 downCount 3864
127.0.0.1:12346 upCount 5940 downCount 5343
127.0.0.1:12346 upCount 2939 downCount 387
127.0.0.1:12346 upCount 588 downCount 200
127.0.0.1:12346 upCount 412 downCount 4856
127.0.0.1:12346 upCount 690 downCount 225
127.0.0.1:12346 upCount 349 downCount 4290
127.0.0.1:12346 upCount 349 downCount 4290
127.0.0.1:12346 upCount 349 downCount 4312
127.0.0.1:12346 upCount 349 downCount 4291
127.0.0.1:12346 upCount 318 downCount 5230
127.0.0.1:12346 upCount 318 downCount 5229
127.0.0.1:12346 upCount 415 downCount 5206
127.0.0.1:12346 upCount 415 downCount 5206
127.0.0.1:12346 upCount 1437 downCount 1283
127.0.0.1:12346 upCount 364 downCount 3485
127.0.0.1:12346 upCount 4648 downCount 1283
127.0.0.1:12346 upCount 671 downCount 178
is Trojan
127.0.0.1:12346 upCount 671 downCount 178
is Trojan
127.0.0.1:12346 upCount 2359 downCount 852
127.0.0.1:12346 upCount 988 downCount 883
Rust的实现
使用我自己的trojan在rust上面的实现,
Firefox: [email protected]:xxxxx -------> Trojan [email protected]:8080 ------> Trojan [email protected]:12345 --------> Trojan [email protected]:12346 ------------> Freedom
trojan-killer无法检测出任何连接,[重要!]但是这不代表Rust的TLS就一定安全,很有可能只是目前没有合适的upCount和downCount[重要!]。从流量分析中可以看出rust的TLS实现跟其他版本的TLS实现比较不一样
正常网页浏览误报
此外,我也测试了一下直接用浏览器http代理,打开各种网页,文档,youtube等等,确实如readme所说,没有一个请求被误报。
建议
-
虽然正常的网页浏览不会被误报,但是因为trojan的检测是通过上行以及下行流量的大小来判断的,可能会有其他的误报情况。我能想到的一个用例是restful服务的请求,很多简单的crud请求所产生的数据流量可能会导致误报。我觉得可以在实验里面加一组go实现的一个简单的restful微服务来作为对照组。
-
测试一下grpc以及quic是否能通过类似的方法检测
首先,感谢你的测试
此外,我也测试了一下直接用浏览器http代理,打开各种网页,文档,youtube等等,确实如readme所说,没有一个请求被误报。
其实还是有的,我和 @yuhan6665 都看到过,但出现概率非常非常之低,可以忽略不计的程度
我又看了下源代码,感觉这个检测方法总体来说是可行的,只是upCount和downCount根据不同的实现可能要选用不同的值。
我们这边的测试,包括群里很多人的测试结果都是直接刷屏(识别率目测至少有 80%)
我看了你贴上来的数据,发现一个问题,就是很多 upCount 看起来并不包含现代浏览器的 TLS Client Hello
现代浏览器的 TLS Client Hello 基本上就是 517 字节,所以这方面你可以排查下,毕竟是检测 TLS in TLS
有尝试将电脑端浏览器的 HTTP 代理设为 Trojan 吗?
现代浏览器的 TLS Client Hello 基本上就是 517 字节
是的,我刚测了下,在firefox这里Client Hello刚好是517字节。不过不同的TLS实现会导致不少偏差,比如说Rust的rustls使用TLS1.3的情况下,ClientHello的大小会在648 - 654之间浮动
有尝试将电脑端浏览器的 HTTP 代理设为 Trojan 吗?
我感觉我测试环境的设置跟你测试的环境比较不一样,在pc端我是通过这种方法来测试的,
Firefox: [email protected]:xxxxx -------> Trojan [email protected]:8080 ------> Trojan [email protected]:12345 --------> Trojan [email protected]:12346 ------------> Freedom
跑下来什么都识别不了,我还在debug, 也不确定是不是我设置有问题,但是网络是通的,可以正常浏览网页。
============================================== Edit: 我把你Http响应的那段代码去掉了,trojan killer直接接收tcp请求然后转发到127.0.0.1:12346
Edit2: trojan client 和trojan server之间使用的是Rustls实现的TLS1.3
Edit3: 通过wireshark抓包,我发现firefox确实有把大小为517的包发给trojan client,但是trojan client没有发送过大小为517的包到trojan killer
Firefox到trojan client
Frame 163: 583 bytes on wire (4664 bits), 583 bytes captured (4664 bits) on interface lo, id 0
Ethernet II, Src: 00:00:00_00:00:00 (00:00:00:00:00:00), Dst: 00:00:00_00:00:00 (00:00:00:00:00:00)
Internet Protocol Version 4, Src: 127.0.0.1, Dst: 127.0.0.1
Transmission Control Protocol, Src Port: 37442, Dst Port: 8080, Seq: 41, Ack: 13, Len: 517
16 03 01...
看来是client hello了没错
比较规范的做法就是 padding 至 512(517)字节,rustls 应该也是 512(517)吧
我觉得你应该先试试 example 中的 trojan,有兴趣再弄其它的,~~不要本末倒置~~
我试试看,另外通过wireshark抓包,rustls的clienthello是333
Frame 28: 399 bytes on wire (3192 bits), 399 bytes captured (3192 bits) on interface lo, id 0
Ethernet II, Src: 00:00:00_00:00:00 (00:00:00:00:00:00), Dst: 00:00:00_00:00:00 (00:00:00:00:00:00)
Internet Protocol Version 4, Src: 127.0.0.1, Dst: 127.0.0.1
Transmission Control Protocol, Src Port: 38468, Dst Port: 12346, Seq: 1, Ack: 1, Len: 333
Source Port: 38468
Destination Port: 12346
[Stream index: 7]
[Conversation completeness: Incomplete, DATA (15)]
[TCP Segment Len: 333]
Sequence Number: 1 (relative sequence number)
Sequence Number (raw): 458511839
[Next Sequence Number: 334 (relative sequence number)]
Acknowledgment Number: 1 (relative ack number)
Acknowledgment number (raw): 1960627581
1000 .... = Header Length: 32 bytes (8)
Flags: 0x018 (PSH, ACK)
Window: 512
[Calculated window size: 65536]
[Window size scaling factor: 128]
Checksum: 0xff75 [unverified]
[Checksum Status: Unverified]
Urgent Pointer: 0
Options: (12 bytes), No-Operation (NOP), No-Operation (NOP), Timestamps
[Timestamps]
[SEQ/ACK analysis]
TCP payload (333 bytes)
Transport Layer Security
Frame 28: 399 bytes on wire (3192 bits), 399 bytes captured (3192 bits) on interface lo, id 0
Ethernet II, Src: 00:00:00_00:00:00 (00:00:00:00:00:00), Dst: 00:00:00_00:00:00 (00:00:00:00:00:00)
Internet Protocol Version 4, Src: 127.0.0.1, Dst: 127.0.0.1
Transmission Control Protocol, Src Port: 38468, Dst Port: 12346, Seq: 1, Ack: 1, Len: 333
Transport Layer Security
TLSv1.3 Record Layer: Handshake Protocol: Client Hello
Content Type: Handshake (22)
Version: TLS 1.0 (0x0301)
Length: 328
Handshake Protocol: Client Hello
Handshake Type: Client Hello (1)
Length: 324
Version: TLS 1.2 (0x0303)
Random: 81e875007c354b95b72a67924c0d19799d914e232e83a5bbde2ff1ac03baafd7
Session ID Length: 32
Session ID: db341b7a9c886acfcc74327421bc2bcd0be2593605653c17329aee1c55737e3d
Cipher Suites Length: 20
Cipher Suites (10 suites)
Compression Methods Length: 1
Compression Methods (1 method)
Extensions Length: 231
Extension: supported_versions (len=5)
Extension: ec_point_formats (len=2)
Extension: supported_groups (len=8)
Extension: signature_algorithms (len=20)
Extension: extended_master_secret (len=0)
Extension: status_request (len=5)
Extension: server_name (len=16)
Extension: signed_certificate_timestamp (len=0)
Extension: key_share (len=38)
Extension: psk_key_exchange_modes (len=2)
Extension: pre_shared_key (len=91)
[JA3 Fullstring: 771,4866-4865-4867-49196-49195-52393-49200-49199-52392-255,43-11-10-13-23-5-0-18-51-45-41,29-23-24,0]
[JA3: 983a9cd8000c1d87618a64a6f5af2673]
Rustls这种实现可能解释了为什么前段时间没有被大规模封禁
其实关系不大,因为这里的 TLS in TLS 检测暂不关心外层 TLS 指纹,而内层 TLS 基本上就是浏览器的 HTTPS
前段时间没有出现特别的反馈说 rust 实现的 trojan 更抗封
刚测了下,example没有任何问题,非常准。我在想rust的不准是否因为外层使用tls1.3的缘故,我看xray好像是1.2,1.3都支持的
我能看到的问题就是,你最初展示出来的数据中,upCount 大多非浏览器的 TLS Client Hello(现在你知道它固定为 517 了)
外层使用 TLSv1.3 是没问题的,~~反而我没测过外层 TLSv1.2~~,总之这个。。。你自己研究吧
(可能你需要在 issue 开头处添加说明,以避免 “10%-20%” 这个数据误导他人)
不过你这个方法的可行性我觉得没什么问题,主要看误封的比例有多少,我感觉很多restful api可能会恰好符合这个特征,有空可以搭个微服务测一下
就,目前来说,我比较关心你在开头写的 “10%-20%”,我觉得你应当在开头补充一些说明,避免产生误导。
日常使用 trojan 浏览网页,就是我们的测试所针对的场景。(不过,大多数 APP 的 TLS 应该也是 517)
就,目前来说,我比较关心你在开头写的 “10%-20%”,我觉得你应当在开头补充一些说明,避免产生误导。
我已经改了,但是我觉得这其实跟trojan关系不大,trojan + grpc 和 trojan + quic还没有证明能够识别。不知道为什么这个项目叫trojan-killer.
你确定 trojan+grpc 和 trojan+quic 是原版 trojan-gfw 有的东西吗?~~尤其是 trojan+quic 简直离谱。~~
你确定 trojan+grpc 和 trojan+quic 是原版 trojan-gfw 有的东西吗?
我指的是trojan协议本身 https://trojan-gfw.github.io/trojan/protocol.html. Trojan + grpc 感觉比较异端一点,Trojan + quic我觉得没什么问题,自用了很久了。trojan协议本身只说了外层应该用TLS加密,没有限定TLS版本或者是传输层。等你把REALITY做出来,我能给你搞个Trojan + REALITY
还有一件事是,我可以给你说,trojan-killer 理论上对你用 rust 实现的 trojan 是完全有效的,因为 trojan 协议就是这么设计的,你用不同语言实现只是改了外层 TLS 指纹,基本上不会影响内层 TLS in TLS 的流量特征。你对着它没测出什么,大概率是你哪里没设置对,毕竟我也不知道你把代码改成啥样了,你需要再研究研究。
你用不同语言实现只是改了外层 TLS 指纹,基本上不会影响内层 TLS in TLS 的流量特征
这点我完全同意,我在overview里面也说了,
很有可能只是目前没有合适的upCount和downCount
很有可能只是目前没有合适的upCount和downCount
不是,这个推测是错的,它们是通用的,是你哪里没设置对,~~是不是需要我写一下 TLS in TLS 检测的原理~~。
你确定 trojan+grpc 和 trojan+quic 是原版 trojan-gfw 有的东西吗?
我指的是trojan协议本身 https://trojan-gfw.github.io/trojan/protocol.html. Trojan + grpc 感觉比较异端一点,Trojan + quic我觉得没什么问题,自用了很久了。trojan协议本身只说了外层应该用TLS加密,没有限定TLS版本或者是传输层。等你把REALITY做出来,我能给你搞个Trojan + REALITY
我们一般认为,trojan 协议为 trojan-gfw 实现的、trojan-gfw 客户端支持的、流行的那个 trojan 协议。
此外 REALITY 出来几个月了,并且 Xray 一直都支持 Trojan + REALITY 这种配法。。。
不是,这个推测是错的,它们是通用的,是你哪里没设置对
有这个可能,我还在debug中
是不是需要我写一下 TLS in TLS 检测的原理
简单点来说你这个原理就是通过加密过后的tls 握手包的大小来判断的吧,主流浏览器的握手包都是固定的,当然欢迎你发文解释原理
但是我觉得这其实跟trojan关系不大
我大概猜出来了,你是觉得这个主要是和内外两层 TLS 有关,而和 trojan 协议本身关系不大。然而事实不是这样,这个就是 trojan 协议设计上的问题(若加一些 padding 就能规避这种简单的检测),并且其实它与外层 TLS 实现关系不大,所以它叫 trojan-killer。
我大概猜出来了,你是觉得这个主要是和内外两层 TLS 有关,而和 trojan 协议本身关系不大
是的,我的想法是跟Trojan本身的关系不大,而是tls in tls暴露了内部的特征
并且其实它与外层 TLS 关系不大
这个问题主要是因为内层的TLS对吧?
我大概猜出来了,你是觉得这个主要是和内外两层 TLS 有关,而和 trojan 协议本身关系不大
是的,我的想法是跟Trojan本身的关系不大,而是tls in tls暴露了内部的特征
首先是客观存在 TLS in TLS 问题,其次是 trojan 没有处理该问题,才会导致这么容易就能检测。
并且其实它与外层 TLS 关系不大
这个问题主要是因为内层的TLS对吧?
我那句话的意思是,这里主要检测的就是内层 TLS,与你外层 TLS 用什么实现关系不大。
首先是客观存在 TLS in TLS 问题,其次是 trojan 没有处理该问题,才会导致这么容易就能检测。
你这么说我也同意
嗯,这个仓库就是直观地揭露出确实存在的 TLS in TLS 问题
以及让大家看到要检测未处理该问题的 Trojan 协议有多么的容易和精准
通过用wireshark比较分析流量,我发现firefox的ClientHello是这么一个逻辑,如果ClientHello的大小小于517, 那么会被加上padding使得大小刚好为517。但是在部分情况下,ClientHello会带有一个叫做
Extension: pre_shared_key (len=272)
的extension, 由于这个extension非常的长,往往会使得ClientHello的大小变成650左右,这个时候firefox就不会再去加padding, 而且这个clientHello的具体大小会受到sni长度的影响。
经过我的测试,如果Firefox发的clientHello请求的大小刚好为517, 那么CCS后面的数据包的大小会刚好落在 650 -750 字节这个区间内,这个时候你的if statement就会起效判断为trojan. 如果FF发的ClientHello请求的大小为650左右,CCS 后面的数据往往会超过800字节,导致判断失效。这是我说成功率只有10%-20%的原因。
Frame 370: 725 bytes on wire (5800 bits), 725 bytes captured (5800 bits) on interface lo, id 0
Ethernet II, Src: 00:00:00_00:00:00 (00:00:00:00:00:00), Dst: 00:00:00_00:00:00 (00:00:00:00:00:00)
Internet Protocol Version 4, Src: 127.0.0.1, Dst: 127.0.0.1
Transmission Control Protocol, Src Port: 55436, Dst Port: 11111, Seq: 236, Ack: 40, Len: 659
Source Port: 55436
Destination Port: 11111
[Stream index: 10]
[Conversation completeness: Incomplete, DATA (15)]
[TCP Segment Len: 659]
Sequence Number: 236 (relative sequence number)
Sequence Number (raw): 1254675842
[Next Sequence Number: 895 (relative sequence number)]
Acknowledgment Number: 40 (relative ack number)
Acknowledgment number (raw): 3762826686
1000 .... = Header Length: 32 bytes (8)
Flags: 0x018 (PSH, ACK)
Window: 512
[Calculated window size: 65536]
[Window size scaling factor: 128]
Checksum: 0x00bc [unverified]
[Checksum Status: Unverified]
Urgent Pointer: 0
Options: (12 bytes), No-Operation (NOP), No-Operation (NOP), Timestamps
TCP Option - No-Operation (NOP)
TCP Option - No-Operation (NOP)
TCP Option - Timestamps
[Timestamps]
[SEQ/ACK analysis]
TCP payload (659 bytes)
Hypertext Transfer Protocol
Transport Layer Security
TLSv1.3 Record Layer: Handshake Protocol: Client Hello
Content Type: Handshake (22)
Version: TLS 1.0 (0x0301)
Length: 654
Handshake Protocol: Client Hello
Handshake Type: Client Hello (1)
Length: 650
Version: TLS 1.2 (0x0303)
Random: 67a386ba35e4e7927f0cc8924987a4b66cff9d6b8662e5e20204f34b0aabd311
Session ID Length: 32
Session ID: c164eaa4d9bda436b5c1891b64eb8f45b1b04db83ea08567696d60abb5603344
Cipher Suites Length: 34
Cipher Suites (17 suites)
Compression Methods Length: 1
Compression Methods (1 method)
Extensions Length: 543
Extension: server_name (len=35)
Type: server_name (0)
Length: 35
Server Name Indication extension
Server Name list length: 33
Server Name Type: host_name (0)
Server Name length: 30
Server Name: incoming.telemetry.mozilla.org
Extension: extended_master_secret (len=0)
Type: extended_master_secret (23)
Length: 0
Extension: renegotiation_info (len=1)
Type: renegotiation_info (65281)
Length: 1
Renegotiation Info extension
Extension: supported_groups (len=14)
Type: supported_groups (10)
Length: 14
Supported Groups List Length: 12
Supported Groups (6 groups)
Extension: ec_point_formats (len=2)
Type: ec_point_formats (11)
Length: 2
EC point formats Length: 1
Elliptic curves point formats (1)
Extension: application_layer_protocol_negotiation (len=14)
Type: application_layer_protocol_negotiation (16)
Length: 14
ALPN Extension Length: 12
ALPN Protocol
Extension: status_request (len=5)
Type: status_request (5)
Length: 5
Certificate Status Type: OCSP (1)
Responder ID list Length: 0
Request Extensions Length: 0
Extension: delegated_credentials (len=8)
Type: delegated_credentials (34)
Length: 8
Signature Hash Algorithms Length: 6
Signature Hash Algorithms (3 algorithms)
Extension: key_share (len=107)
Type: key_share (51)
Length: 107
Key Share extension
Extension: supported_versions (len=5)
Type: supported_versions (43)
Length: 5
Supported Versions length: 4
Supported Version: TLS 1.3 (0x0304)
Supported Version: TLS 1.2 (0x0303)
Extension: signature_algorithms (len=20)
Type: signature_algorithms (13)
Length: 20
Signature Hash Algorithms Length: 18
Signature Hash Algorithms (9 algorithms)
Extension: psk_key_exchange_modes (len=2)
Type: psk_key_exchange_modes (45)
Length: 2
PSK Key Exchange Modes Length: 1
PSK Key Exchange Mode: PSK with (EC)DHE key establishment (psk_dhe_ke) (1)
Extension: record_size_limit (len=2)
Type: record_size_limit (28)
Length: 2
Record Size Limit: 16385
Extension: pre_shared_key (len=272)
Type: pre_shared_key (41)
Length: 272
Pre-Shared Key extension
Identities Length: 235
PSK Identity (length: 229)
PSK Binders length: 33
PSK Binders
这种情况确实存在,~~下个版本安排~~,但是我看你贴上来的这些 upCount,看起来并不含这种情况,此外你可以用 Chrome 试试
实际测试,准确度很高,正常的HTTPS流量的存在误报情况,但是非常少。实装的话,配合一定时间的持续检测,精确封锁应该是问题不大
嗯,我现在只找到了一个问题。FF下upCount我建议再抓取一段800 - 850字节,用来适配附带pre_shared_key的情况。另外pre_shared_key出现的时候,downCount往往都在230多一点,还没查清楚具体为什么。工作忙,周末有空再接着测。
我觉得你可以先把你的 rust 实现测不出来的问题解决,这个应该比较简单,根据你的描述,可能你并没有把流量发给 trojan-killer
Any update?
Any update?
有的,一直拖着没发出来(懒)。根本原因是我在trojan的header这里没做缓存,导致整个trojan握手的header分了好几次发送,所以tls-in-tls的client hello总是在 第一次ssc之后才发出去,而不是之前,所以总是会检测失败。
导致整个trojan握手的header分了好几次发送
你的意思是 header 的每个字段分别发出去吗,这样的话每次都会增加十几字节,~~而且由于特征过于明显很少有人这么实现~~
如果你有时间的话,可否测试一下 Trojan-killer 对 Xray-core 以外其它代理软件的适用性:
- clash(它把 header 和 body 分开发,会增加十几字节,~~当然对它来说直接检测客户端第二个 AEAD 包长度更精准~~)
- v2fly(应该没有悬念,下面几个也是)
- clash.meta
- sing-box
- trojan-go
- trojan-gfw