作者: Anonymous
日期: 2020年6月16日,星期二
English Version: Summary on Recently Discovered V2Ray Weaknesses
近期数个V2Ray的弱点被发现。这些弱点可以被用来识别使用VMess、TLS或HTTP协议的V2Ray客户端和服务器。 以下是我们对这些弱点的总结和理解。
总体上,这些弱点可分为三类:
VMess协议的请求构造如下:
16 字节 | X 字节 | 余下部分 |
---|---|---|
认证信息 | 指令部分 | 数据部分 |
认证信息
是一个16字节的HMAC,基于用户 ID
和UTC 时间辍
.指令部分
由AES-128-CFB(iv, key)
算法加密, 其中iv
是UTC 时间辍
的md5哈希值, key
是V2Ray用户自己设置的那个密码。下表为指令部分
解密后的结构:
1 字节 | 16 字节 | 16 字节 | 1 字节 | 1 字节 | 4 位 | 4 位 | 1 字节 | 1 字节 | 2 字节 | 1 字节 | N 字节 | P 字节 | 4 字节 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|
版本号 Ver | 数据加密 IV | 数据加密 Key | 响应认证 V | 选项 Opt | 余量 P | 加密方式 Sec | 保留 | 指令 Cmd | 端口 Port | 地址类型 T | 地址 A | 随机值 | 校验 F |
数据加密 IV
和数据加密 Key
是用来加密/解密数据部分
的,不是用来加密/解密指令部分
的。余量 P
和随机值
是填充机制的一部分。其中余量 P
占4位,用来表示随机值
的长度。长度取值在0到15字节之间。校验 F
是一个MAC。在合法的请求中,它的值应该是除自己以外所有指令部分
的FNV1a哈希值。2020年5月31日,@p4gefau1t报告,由于客户端请求的合法性没有得到恰当的认证,VMess服务器可以被基于重放攻击的主动探测识别。
VMess服务器分两步,分别用认证信息
和校验 F
来鉴定客户端请求的合法性。不幸的是,这两步都可以被攻击者绕过。
第一步,
VMess服务器验证包含在认证信息
中的时间辍
是否已经过期。
保质期最长为120秒,平均值为60秒。其具体的实现细节请见这里还有这里。
这也就是说,
攻击者可以先记录下正常客户端发出的合法的认证信息
,然后只要在60秒左右的时间内,在自己的恶意请求中使用这段合法的认证信息
,就可以通过服务器第一步的验证。
第二步,
由于用来加密指令部分
的aes-cfb
算法本身不提供数据认证,
VMess协议使用了一个MAC-then-Encrypt的机制校验数据的真实性和完整性。
@p4gefau1t指出,
VMess协议掉入了与Shadowsocks OTA模式当年同样的陷阱。(英文版的Shadowsocks OTA模式漏洞请见这里。)
具体来讲,
由于随机值
的长度在每个请求中是变化的,
因此服务器被迫在还没有验证余量 P
的真实性之前就盲目的信任这个值。
然后服务器才能确定校验 F
(MAC)在请求中的位置。
(具体实现细节请见这里)
也就是说,
在未经校验的情况下读取了P+4字节后,
V2Ray才能开始验证请求的真实性和完整性。
如果验证未通过,
V2Ray服务器就会断开连接。
VMess服务器确实有一个对抗重放攻击的机制。
不管请求是否合法,
服务器都会记录下每个请求中使用的数据加密 IV
-数据加密 Key
对;
如果一个请求中的数据加密 IV
-数据加密 Key
对已经在之前的请求中使用过了,
服务器就会立即断开连接。
因此取决于其需要,攻击者可以对这个对抗重放攻击的机制做两件事:
数据加密 IV
或数据加密 Key
的部分,从而绕过这个对抗重放攻击的机制。数据加密 IV
-数据加密 Key
对,服务器在第一次和第n>1次见到它时反应是否不同。利用以上弱点, 各种基于重放攻击的,针对VMess服务器的主动探测被创造出来。 我们现在对这些攻击按类别进行介绍。
基于@p4gefau1t发现的VMess弱点和攻击, @studentmain提出了一种更强的攻击来识别VMess服务器。这种攻击后又被@p4gefau1t再次修改增强。 为了叙述简洁, 我们用一种稍有不同的方式呈现它。
该主动探测的载荷基于对合法客户端发送的密文请求的修改,其构造如下:
16 字节 | 41 字节 | M 字节 |
---|---|---|
认证信息 | 恶意修改的指令部分 | 零 |
恶意修改的指令部分
的结构如下:
1 字节 | 16 字节 | 16 字节 | 1 字节 | 1 字节 | 4 位 | 4 位 | 1 字节 | 1 字节 | 2 字节 | 1 字节 |
---|---|---|---|---|---|---|---|---|---|---|
版本号 Ver | 数据加密 IV | 数据加密 Key | 响应认证 V | 选项 Opt | 余量 P | 加密方式 Sec | 保留 | 指令 Cmd | 端口 Port | 地址类型 T |
攻击者在一次攻击中,共向服务器发起16次连接。在每次连接中攻击者:
数据加密 Key
的最后一字节和对应余量 P
的部分修改为与原来不同的值,并发送。注意要保证不同连接中修改后的值都不一样。如果16次连接中记录的M没有重复且最大值和最小值的差为15,那么被探测的服务器就很有可能是在使用VMess协议。
对于该攻击的解释如下:
认证信息
的校验,攻击者在大约60秒的时间内重复使用同一个来自合法客户端的认证信息
。数据加密 IV
-数据加密 Key
对的过滤器,
攻击者修改了每次连接中的数据加密 Key
。数据加密 Key
后错误扩散到余量 P
,攻击者精心的选择了只修改数据加密 Key
的最后一字节。因为这一字节与余量 P
同属于一个16字节的密码块。(AES-128-CFB
算法的错误扩散是这样的:
改变密文块Ci中的任意1位,将会 1)改变其对应明文块Pi中对应的1位;2)并可能随机的改变其之后所有明文块中的任意位。)余量 P
所有的密文空间来遍历其所有的明文空间。地址 A
、P字节的填充
以及4字节的校验 F
。
因此M的实际值为N+P+4。余量 P
的明文值。 (因为地址类型 T
是不变的,所以地址 A
占用的长度为固定值。)@nametoolong 发现了另外两种重放攻击,可以用来识别即使修复了上述问题的VMess服务器。
这两种攻击都与服务器何时及如何关闭连接有关。 我们在这里仅介绍第一种攻击,并把第二种攻击的原理留给读者作为练习。
@nametoolong描述了第一种攻击载荷和其期待引起服务器反应:
攻击 1:
M1为一个合法连接的前54个字节。
M1修改第48字节,其余字节保持不便,记作M2。
发送M1,服务器会立即断开连接。
发送M2,服务器不会立即断开连接。
再次发送M2,服务器会立即断开连接。
注:被替换的第48字节(从0开始数的)即为的数据加密 Key
的最后一个字节。
在这次攻击中,
攻击者故意触发重放防御机制,
并期待着服务器在第一次与更多次见到同一个数据加密 IV
-数据加密 Key
对时,
会有不同的反应。
具体介绍如下:
数据加密 IV
-数据加密 Key
对,
所以服务器会检测出M1是重放攻击,并立即断开连接。数据加密 IV
-数据加密 Key
对,
所以服务器不会认定M2是重放攻击,因此会继续等待攻击者发送更多的字节,而不断开连接。数据加密 IV
-数据加密 Key
对,
所以服务器会认定这次的M2是重放攻击,并且立即断开连接。V2Ray已经将断开连接时所需的时长和所需接收的字节数随机化, 但由于不是在遇到所有类型的错误时都统一采用了这一机制, 因此给了攻击者可乘之机。
@nametoolong因此建议:
断开连接的机制要保持统一。
但也需要考虑断开连接这一行为本身是否会泄露什么特征。
证据显示,以上提到的攻击以GFW现有的能力来讲是切实可行的。 比如说,GFW被观察到可以记录合法客户端的连接,并在0.4秒到数百小时之间的延迟后,将修改过的(或未修改过的)载荷发送给被怀疑的服务器。
下一步,我们将调查GFW是否已经使用了针对V2Ray的主动探测攻击。与此同时,如果你有任何翻墙服务器被封锁,我们都欢迎你或公开的或私下的与我们分享你的配置。因为这会帮助我们快速定位许多问题的根源。
也许同时基于实效性
和重复性
的请求验证是抵抗GFW重放攻击的好办法。
一方面,
V2Ray在认证信息
中仅使用了基于实效性
的重放攻击防御措施。
但这会导致在一定时间内的任何重放攻击都是有效的。
另一方面,
Shadowsocks-libev使用了一个基于nonce的重放过滤器。
但为了让仅基于nonce的重放过滤器有效检测重放攻击,
服务端被不现实的要求(在主密钥更换之前)必须要一直记住所有合法连接中的nonce,即使是服务端重启之后!
因此,
同时基于实效性
和重复性
的防御机制似乎是抵抗GFW重放攻击的更有效办法。
Frolov et al.发现, 包括obfs4, Shadowsocks Outline, 赛风的OSSH和蓝灯的Lampshade在内的许多翻墙协议或工具都可以被服务器断开连接的方式所识别。 具体而言,不同的主动探测可能导致服务器以不同的TCP包或不同的时长来断开连接。 Frolov et al.给出的建议是让服务器在遇到错误时“永远读取buffer”。 直到攻击者自己主动断开连接。 这样做一方面减少了超时信息的泄漏, 另一方面也使得服务器只会使用FIN/ACK断开连接, 而不用RST和FIN/ACK的混合方式(原理请见Fig. 1)。
2020年5月20日,@p4gefau1t 报告V2Ray客户端发送的TLS ClientHello有着非常独特的指纹。 这样独特的指纹不但给了审查者怀疑和识别V2Ray客户端和服务端的机会, 而且还允许审查者在不造成大量误伤(collateral damage)的情况下精确的封锁V2Ray产生的TLS流量。
@p4gefau1t进一步识别出这些独特的指纹是由一套硬编码的密码套件造成的。
具体而言,
当AllowInsecureCiphers
的值为默认的false
时,
那套硬编码的密码套件就会被使用。
V2Ray开发者@xiaokangwang让V2Ray自v4.23.4
起,使用go-tls库默认的设置来缓解这一弱点
(更多补丁细节请见 #2510,#2512和#2518。
@tomac4t总结了一个表格,
里面使用tlsfingerprint.io来比较不同版本及配置下V2Ray使用的ClientHello的指纹的流行程度。
但结果显示,
即使是已经使用go-tls库默认设置后的指纹,
在实际的流量统计中似乎仍是很少见的。
据我们所知, 在2019年11月, @klzgrad就曾调查过V2Ray v4.21.3 以及其他基于TLS的翻墙工具的ClientHello指纹。 其结果显示, 很多工具在当时被调查的版本中的ClientHello有着罕见的指纹。
旁注:
2020年6月2日,@p4gefau1t 报告 V2Ray没能成功模仿真正的HTTP通讯。 汇报了的问题有两个:
介于2013年起鹦鹉已死,
与其尝试复活鹦鹉,
不如改用真正的HTTP引擎。
现在的许多翻墙软件已经使用了应用前置
(application fronting
)的概念,这些软件包括但不限于forwardproxy,
naiveproxy 和trojan。
文中提到的一切贡献、成果均属于该工作的原作者。
我们想在此感谢@studentmain和@p4gefau1t。 他们帮助我们理解了他们所提出的重放攻击的许多细节,并与我们分享了他们对于下一步工作的建议。 我们还想感谢David Fifield和@studentmain对于这篇总结给出的详细反馈和建议。
这篇报告首发于GFW Report。我们还在net4people和ntc.party同步更新了这篇报告。
下一步,我们将调查GFW是否已经使用了针对V2Ray的主动探测攻击。与此同时,如果您有任何翻墙服务器被封锁,我们都欢迎您或公开的或私下的与我们分享您的配置。因为这会帮助我们快速定位许多问题的根源。我们私下的联系方式可见GFW Report的页脚。