总结近期发现的V2Ray弱点


作者: Anonymous

日期: 2020年6月16日,星期二

English Version: Summary on Recently Discovered V2Ray Weaknesses

近期数个V2Ray的弱点被发现。这些弱点可以被用来识别使用VMess、TLS或HTTP协议的V2Ray客户端和服务器。 以下是我们对这些弱点的总结和理解。

总体上,这些弱点可分为三类:

  • VMess服务器没有正确验证客户端的请求,使得服务器可受到重放攻击。
  • 客户端硬编码了一套罕见的TLS密码套件,导致客户端发送的TLS ClientHello拥有几乎独一无二的指纹。
  • 伪装成HTTP服务器的企图失败。

针对VMess协议的重放攻击

VMess协议的请求构造如下:

16 字节 X 字节 余下部分
认证信息 指令部分 数据部分
  • 认证信息 是一个16字节的HMAC,基于用户 IDUTC 时间辍.
  • 指令部分AES-128-CFB(iv, key)算法加密, 其中ivUTC 时间辍的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对已经在之前的请求中使用过了, 服务器就会立即断开连接。 因此取决于其需要,攻击者可以对这个对抗重放攻击的机制做两件事:

  1. 攻击者可以修改密文中对应数据加密 IV数据加密 Key的部分,从而绕过这个对抗重放攻击的机制。
  2. 攻击者可以故意触发这个对抗重放攻击的机制,然后观察对于同一个数据加密 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次连接。在每次连接中攻击者:

  1. 首先记录下某一合法请求的前16+41字节。
  2. 然后将其中对应数据加密 Key的最后一字节和对应余量 P的部分修改为与原来不同的值,并发送。注意要保证不同连接中修改后的值都不一样。
  3. 最后每隔一秒发送一字节的零(或随机)数据,直到服务器主动断开连接。记发送的零(或随机)数据的字节长度为M

如果16次连接中记录的M没有重复且最大值和最小值的差为15,那么被探测的服务器就很有可能是在使用VMess协议。

对于该攻击的解释如下:

  • 为了绕过服务器基于认证信息的校验,攻击者在大约60秒的时间内重复使用同一个来自合法客户端的认证信息
  • 为了绕过服务器基于数据加密 IV-数据加密 Key对的过滤器, 攻击者修改了每次连接中的数据加密 Key
  • 为了避免修改数据加密 Key错误扩散余量 P,攻击者精心的选择了只修改数据加密 Key的最后一字节。因为这一字节与余量 P同属于一个16字节的密码块。(AES-128-CFB算法的错误扩散是这样的: 改变密文块Ci中的任意1位,将会 1)改变其对应明文块Pi中对应的1位;2)并可能随机的改变其之后所有明文块中的任意位。)
  • 攻击者利用流加密算法的malleability特性, 在16次连接中,通过遍历余量 P所有的密文空间来遍历其所有的明文空间。
  • 在读取16+41字节后, 服务器会期待客户端发送N字节的地址 AP字节的填充以及4字节的校验 F。 因此M的实际值为N+P+4
  • 攻击者因此可以利用M猜出每次连接中余量 P的明文值。 (因为地址类型 T是不变的,所以地址 A占用的长度为固定值。)

触发服务器不同反应的重放攻击

@nametoolong 发现了另外两种重放攻击,可以用来识别即使修复了上述问题的VMess服务器。

这两种攻击都与服务器何时及如何关闭连接有关。 我们在这里仅介绍第一种攻击,并把第二种攻击的原理留给读者作为练习。

@nametoolong描述了第一种攻击载荷和其期待引起服务器反应:

    攻击 1:
    M1为一个合法连接的前54个字节。
    M1修改第48字节,其余字节保持不便,记作M2。
    发送M1,服务器会立即断开连接。
    发送M2,服务器不会立即断开连接。
    再次发送M2,服务器会立即断开连接。

注:被替换的第48字节(从0开始数的)即为的数据加密 Key的最后一个字节。

在这次攻击中, 攻击者故意触发重放防御机制, 并期待着服务器在第一次与更多次见到同一个数据加密 IV-数据加密 Key对时, 会有不同的反应。 具体介绍如下:

  1. 因为已经在之前的合法连接中见过M1中的数据加密 IV-数据加密 Key对, 所以服务器会检测出M1是重放攻击,并立即断开连接。
  2. 当第一次发送M2时,服务器是第一次见到其包含的数据加密 IV-数据加密 Key对, 所以服务器不会认定M2是重放攻击,因此会继续等待攻击者发送更多的字节,而不断开连接。
  3. 当第二次发送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)。

独特的TLS ClientHello指纹

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有着罕见的指纹。

旁注:

伪装成HTTP服务器的企图失败

2020年6月2日,@p4gefau1t 报告 V2Ray没能成功模仿真正的HTTP通讯。 汇报了的问题有两个:

  1. V2Ray客户端和服务端都只会在第一个TCP数据包中加入HTTP头部。这种奇特的TCP连接容易被检测和怀疑。
  2. V2Ray服务器对任何请求错误都笼统的回复一个硬编码的500回复

介于2013年起鹦鹉已死, 与其尝试复活鹦鹉, 不如改用真正的HTTP引擎。 现在的许多翻墙软件已经使用了应用前置application fronting)的概念,这些软件包括但不限于forwardproxynaiveproxytrojan

贡献

文中提到的一切贡献、成果均属于该工作的原作者。

致谢

我们想在此感谢@studentmain@p4gefau1t。 他们帮助我们理解了他们所提出的重放攻击的许多细节,并与我们分享了他们对于下一步工作的建议。 我们还想感谢David Fifield和@studentmain对于这篇总结给出的详细反馈和建议。

联系

这篇报告首发于GFW Report。我们还在net4peoplentc.party同步更新了这篇报告。

下一步,我们将调查GFW是否已经使用了针对V2Ray的主动探测攻击。与此同时,如果您有任何翻墙服务器被封锁,我们都欢迎您或公开的或私下的与我们分享您的配置。因为这会帮助我们快速定位许多问题的根源。我们私下的联系方式可见GFW Report的页脚。


评论区