个人分析 Shadowsocks(R) TLS 混淆的安全性


⚠️ 注意事项

  1. 本文纯属个人观点,专业性低,仅供参考!
  2. 因为软件是不断更新的,所以本文具有时效性(更新于 2018 年 4 月 11 日)。
  3. 本文中的敏感数据已做无害化处理。


一、前言

Shadowsocks(R) 使用 TLS 混淆之后是否安全?

Shadowsocks 与 ShadowsocksR 哪个更安全?

使用 HTTP 混淆好还是 TLS 混淆好?

……

有关 Shadowsocks(R) 安全性的讨论一直都没有停止过。

正好这两天没什么事,于是找了一个闲置的云主机搭建服务,然后抓下包粗略分析了一下。


二、所谓「安全」的含义

我觉得 Shadowsocks(R) 的安全性主要体现在以下两点:

一是不让防火墙检测到具体访问了什么内容。

二是不让防火墙检测到正在提供 Shadowsocks(R) 服务。

第一点可以完全不用考虑。

因为 Shadowsocks(R) 一般使用工业标准的高强度加密算法(如 256 位的 AES),而这个加密算法已经经历了多年的考验。

所以,只要做到不使用有漏洞的加密算法、不使用弱密码以及使用安全的信道传输密码,完全不用考虑这一点。

而第二点正是目前的痛点。

因为我们完全不清楚防火墙的检测机制。

虽然普通 Shadowsocks(R) 协议的数据包没有明显的特征,但是防火墙有很大可能会随机丢弃没有明显特征的数据包,或者对没有明显特征的数据包进行限速。因为正常的上网的数据包是不太可能没有明显特征的。

所以,我们必须为 Shadowsocks(R) 协议的数据包增加一些特征,来仿真正常上网动作,试图逃避防火墙的检测。

但是,仿真的毕竟还是仿真的,它的特征和正常上网数据包还是有所区别。

所以,到底该怎么仿真,是目前最头疼的地方,也是未来主要的研究方向……


三、TLS 混淆简要原理

我们先来看下 SSL/TLS 标准的握手的流程图。

图片出处:Cloudflare

可以看到,正常的 SSL/TLS 连接是需要服务器下发证书的。

然而我们都知道,Shadowsocks(R) 是不支持配置证书的,那它是怎么仿真 TLS 流量呢?

由于 SSL/TLS 每次连接生成加密密钥的开销实在是有点大,因此 SSL/TLS 定义了一个“会话保存恢复机制”。

在第一次 SSL/TLS 握手成功之后,服务器和客户端都会保存连接的凭据,以便下次握手时使用。

而通过“会话保存恢复机制”创建的连接则不需要再次生成密钥,也就不需要服务器下发证书了。

当使用“会话保存恢复机制”时,客户端通过 Session ID 或者 Session Ticket 就可以快速与服务器取得连接。流程见下图。

图片出处:Cloudflare

图片出处:Cloudflare

Shadowsocks(R) 就是利用了 SSL/TLS 的“会话保存恢复机制”来实现 TLS 流量仿真的。


三、系统环境

3.1 服务器

  • 操作系统:CensOS 7.4.1708
  • 应用软件:shadowsocks-libev 3.1.3simple-obfs 0.0.5ShadowsocksR-Python 3.3.3

3.2 客户端

  • 操作系统:macOS 10.13.4
  • 应用软件:shadowsocks-libev 3.1.3simple-obfs 0.0.5shadowsocksr-libev 2.5.6Wireshark 2.4.2tcpdump 4.9.2

四、Shadowsocks TLS 混淆抓包分析

4.1 抓包过程

首先,在同一服务器上依次使用以下三种配置(不同加密方式)搭建服务:

{
    "server":"0.0.0.0",
    "server_port":443,
    "local_port":1080,
    "password":"********",
    "timeout":600,
    "method":"aes-128-cfb",
    "fast_open":true,
    "plugin": "obfs-server",
    "plugin_opts": "obfs=tls;obfs-host=iosapps.itunes.apple.com"
}
{
    "server":"0.0.0.0",
    "server_port":443,
    "local_port":1080,
    "password":"********",
    "timeout":600,
    "method":"chacha20-ietf-poly1305",
    "fast_open":true,
    "plugin": "obfs-server",
    "plugin_opts": "obfs=tls;obfs-host=iosapps.itunes.apple.com"
}
{
    "server":"0.0.0.0",
    "server_port":443,
    "local_port":1080,
    "password":"********",
    "timeout":600,
    "method":"rc4-md5",
    "fast_open":true,
    "plugin": "obfs-server",
    "plugin_opts": "obfs=tls;obfs-host=iosapps.itunes.apple.com"
}

客户端分别使用与服务器对应的三个配置连接服务器之后,通过代理 SSH 到服务器执行 exit 命令。抓取整个过程的数据包,得到三个 PCAP 文件 ABC

然后使用第二种配置(即 chacha20-ietf-poly1305 加密方式),抓取通过代理上网的数据包一段时间,得到 PCAP 文件 D

最后关闭 Shadowsocks 客户端,抓取本机发往 443 端口的正常数据包一段时间,得到 PCAP 文件 E,用作对照。

附上抓取的 PCAP 文件(敏感信息已做无害化处理):
PCAP 文件 A
PCAP 文件 B
PCAP 文件 C
PCAP 文件 D
PCAP 文件 E

4.2 握手过程分析

由于真正传输数据的数据包都是加密的,所以这里只分析 TLS 握手过程的数据包。

4.2.1 ClientHello

下面两张图是 PCAP 文件 ABC 中客户端发送的 ClientHello 数据包。

图中用红色方框表示不同数据包中字段值完全相同的字段,用蓝色方框表示不同数据包中字段值不相同的字段。

下图是从 PCAP 文件 D 随机抽取 3 个 TLS 会话的 ClientHello 数据包。

图中用蓝色方框表示不同数据包中字段值不相同的字段。

下图是从 PCAP 文件 E 随机抽取正常上网 3 个 TLS 会话的 ClientHello 数据包。

这里就不做标识了,一眼就可以看出区别。

4.2.2 ServerHello

下图是 PCAP 文件 ABC 中服务器发送的 ServerHello 数据包。

图中用红色方框表示不同数据包中字段值完全相同的字段,蓝色方框表示不同数据包中字段值不相同的字段。

下图是从 PCAP 文件 D 随机抽取 3 个 TLS 会话的 ServerHello 数据包。

图中用蓝色方框表示不同数据包中字段值不相同的字段。

下图是从 PCAP 文件 E 随机抽取正常上网 3 个 TLS 会话的 ServerHello 数据包。

这里就不做标识了,一眼就可以看出区别。

4.3 结果分析

如果单纯从握手过程来看,不同加密方式以及不同连接的数据包中具有相同值的字段还是挺多的,唯独 RandomSession ID 以及 Session Ticket TLS 字段是不同的。


五、ShadowsocksR TLS 混淆抓包分析

5.1 抓包方法

在服务器上使用以下配置搭建服务:

{
    "server":"0.0.0.0",
    "server_port":443,
    "local_port":1080,
    "password":"********",
    "method":"none",
    "protocol":"auth_chain_a",
    "protocol_param":"",
    "obfs":"tls1.2_ticket_auth",
    "obfs_param":""
}

注:由于这个配置是 ShadowsocksR 原作者推荐使用的,因此我也就不使用其他配置了。

客户端使用与服务器对应的配置连接服务器之后,抓取通过代理上网的数据包一段时间,得到 PCAP 文件 F

附上抓取的 PCAP 文件(敏感信息已做无害化处理):
PCAP 文件 F

5.2 握手过程分析

由于真正传输数据的数据包都是加密的,所以这里只分析 TLS 握手过程的数据包。

5.2.1 ClientHello

下图是从 PCAP 文件 F 随机抽取 3 个 TLS 会话的 ClientHello 数据包。

图中用蓝色方框表示不同数据包中字段值不相同的字段。

5.2.2 ServerHello

下图是从 PCAP 文件 F 随机抽取 3 个 TLS 会话的 ServerHello 数据包。

图中用蓝色方框表示不同数据包中字段值不相同的字段。

5.3 结果分析

如果单纯从握手过程来看,ShadowsocksR TLS 混淆的不同连接的数据包中具有相同值的字段比 Shadowsocks TLS 混淆还要多。

在 PCAP 文件 F 中,ShadowsocksR TLS 混淆所有连接都使用相同的 Session ID,而 Shadowsocks TLS 混淆每个连接都使用不同的 Session ID

另外,每当 ServerHello 出现 New Session Ticket 的时候,Wireshark 都会提示这个数据包有问题,如下图。


六、Shadowsocks HTTP 混淆抓包分析

这里提一下就好了,不做具体分析。因为我没有深入使用也不想用 HTTP 混淆。

如下图,Shadowsocks HTTP 混淆仿真的是 WebSocket。

我觉得 WebSocket 现在基本上都是承载在 HTTPS 协议之上,承载在 HTTP 上的 WebSocket 已经非常少了。总感觉 Shadowsocks HTTP 混淆特征太过明显。


七、结论 & 使用建议

Shadowsocks(R) 数据流本身就是一种无明显特征的 TCP 数据流。

从上面的分析我们不难看出,使用混淆实际上都是在增加 Shadowsocks(R) 数据流的特征。

如果防火墙可以抓取并详细分析仿真 TLS 握手数据包的每个字段,应该很容易可以判断出服务器是否提供了 Shadowsocks(R) 服务。

相比 Shadowsocks TLS 混淆,我认为ShadowsocksR TLS 混淆数据包的特征更明显。因为 ShadowsocksR TLS 混淆握手包的固定字段比较多,而且看起来还有很多错误。

所以我觉得 Shadowsocks TLS 混淆比 ShadowsocksR TLS 混淆更安全。

以下是我的一些使用建议:

  • 如果不混淆的 Shadowsocks(R) 流不被丢弃/限速,那么请不要使用混淆。
  • 如果不需要端口复用,那么请不要使用混淆。
  • 如果 Shadowsocks(R) 流很不幸被丢弃/限速了,请尝试更换为知名端口(如 80、443)。
  • 如果非要使用混淆,那么请使用 Shadowsocks,而不要使用 ShadowsocksR,并将服务开放在 443 端口上。

八、Shadowsocks TLS 混淆改进建议

鉴于 ShadowsocksR 已经停止更新,这里就不提它了。

综合上面的数据包分析结果,我认为对于 Shadowsocks TLS 混淆有以下几点可以改进的:

  • 不同连接在握手的时候可适当增减 Extension 字段的数目。
  • 不同连接在握手的时候使用不同的 Server Name(貌似已经实现了)。
  • 不同连接在握手的时候的 Cipher Suite 以及 Signature Hash Algorithms 字段值可适当的不同。
  • 在连接结束的时候,服务器可以发送一个 Encrypted Alert 数据包。

最后,祝防火墙早日崩塌。

发表评论

电子邮件地址不会被公开。