基于 TLS 1.3 的域名前置

介绍

基于 TLS 1.3 的域名前置
来自 Cisco、BlueCoat、FireEye 等知名制造商的现代企业内容过滤系统与其功能更强大的同类产品 - DPI 系统有很多共同点,DPI 系统正在国家层面积极实施。 两者工作的本质都是检查传入和传出的互联网流量,并根据黑/白名单做出禁止互联网连接的决定。 由于它们在工作的基础上都依赖相似的原则,因此规避它们的方法也有很多共同点。

域前端技术是允许您非常有效地绕过 DPI 和公司系统的技术之一。 其本质是,我们访问一个被阻止的资源,隐藏在另一个具有良好声誉的公共域后面,该域显然不会被任何系统阻止,例如google.com。

关于这项技术已经写了很多文章,并且给出了很多例子。 然而,最近流行的 DNS-over-HTTPS 和加密 SNI 技术,以及新版本的 TLS 1.3 协议,使得考虑域前置的另一种选择成为可能。

了解技术

首先,让我们定义一些基本概念,以便每个人都了解谁是谁以及为什么需要这一切。 我们提到了eSNI机制,其操作将进一步讨论。 eSNI(加密服务器名称指示)机制是 SNI 的安全版本,仅适用于 TLS 1.3 协议。 主要思想是加密有关请求发送到哪个域的信息等。

现在我们来看看eSNI机制在实际中是如何运作的。

假设我们有一个被现代 DPI 解决方案阻止的互联网资源(例如,著名的 torrent 跟踪器 rutracker.nl)。 当我们尝试访问 torrent 跟踪器的网站时,我们会看到提供商的标准存根,表明该资源被阻止:

基于 TLS 1.3 的域名前置

在 RKN 网站上,该域名实际上列在停止列表中:

基于 TLS 1.3 的域名前置

当你查询whois时,你可以看到域名本身“隐藏”在云提供商Cloudflare后面。

基于 TLS 1.3 的域名前置

但与 RKN 的“专家”不同,Beeline 的技术更精明的员工(或者是受到我们著名监管机构的惨痛经验的教训)并没有愚蠢地通过 IP 地址禁止该网站,而是将域名添加到停止列表中。 如果您查看同一 IP 地址后面隐藏的其他域,访问其中一个域并查看访问未被阻止,则可以轻松验证这一点:

基于 TLS 1.3 的域名前置

这是怎么发生的? 由于所有通信都是通过 https 协议进行的,而且我们还没有注意到 Beeline 的 https 证书被替换,所以提供商的 DPI 如何知道我的浏览器位于哪个域? 是他有千里眼还是我被跟踪了?

让我们尝试通过wireshark查看流量来回答这个问题

基于 TLS 1.3 的域名前置

屏幕截图显示,首先浏览器通过 DNS 获取服务器的 IP 地址,然后与目标服务器进行标准 TCP 握手,然后浏览器尝试与服务器建立 SSL 连接。 为此,它发送 SSL 客户端 Hello 数据包,其中包含明文形式的源域名称。 cloudflare 前端服务器需要此字段才能正确路由连接。 这就是提供商 DPI 捕获我们并中断我们连接的地方。 同时,我们没有收到来自提供商的任何存根,并且我们看到标准浏览器错误,就好像该网站已被禁用或根本无法工作:

基于 TLS 1.3 的域名前置

现在让我们在浏览器中启用eSNI机制,如说明中所写 火狐 :
为此,我们打开 Firefox 配置页面 about:config中 并激活以下设置:

network.trr.mode = 2;
network.trr.uri = https://mozilla.cloudflare-dns.com/dns-query
network.security.esni.enabled = true

之后,我们将检查 cloudflare 网站上的设置是否正常工作。 链接 让我们再次尝试一下我们的 torrent 跟踪器的技巧。

基于 TLS 1.3 的域名前置

瞧。 我们最喜欢的跟踪器在没有任何 VPN 或代理服务器的情况下打开。 现在让我们看一下wireshark 中的流量转储,看看发生了什么。

基于 TLS 1.3 的域名前置

这次,ssl 客户端 hello 包没有显式包含目标域,而是包中出现了一个新字段 - crypto_server_name - 这是包含 rutracker.nl 值的位置,只有 cloudflare 前端服务器可以解密该字段场地。 如果是这样,那么提供商 DPI 别无选择,只能洗手并允许此类流量。 没有其他加密选项。

因此,我们研究了该技术在浏览器中的工作原理。 现在让我们尝试将其应用到更具体、更有趣的事情上。 首先,我们将教同一个curl使用eSNI来与TLS 1.3配合使用,同时我们将了解基于eSNI的域前端本身是如何工作的。

使用 eSNI 进行域前置

由于curl使用标准openssl库通过https协议进行连接,因此首先我们需要在那里提供eSNI支持。 openssl master 分支中还没有 eSNI 支持,因此我们需要下载一个特殊的 openssl 分支,编译并安装它。

我们从 GitHub 克隆存储库并照常编译:

$ git clone https://github.com/sftcd/openssl
$ cd openssl
$ ./config

$ make
$ cd esnistuff
$ make

接下来,我们使用curl克隆存储库并使用我们编译的openssl库配置其编译:

$ cd $HOME/code
$ git clone https://github.com/niallor/curl.git curl-esni
$ cd curl-esni

$ export LD_LIBRARY_PATH=/opt/openssl
$ ./buildconf
$ LDFLAGS="-L/opt/openssl" ./configure --with-ssl=/opt/openssl --enable-esni --enable-debug

在这里,正确指定 openssl 所在的所有目录(在我们的示例中为 /opt/openssl/)并确保配置过程顺利进行非常重要。

如果配置成功,我们会看到这样一行:

警告:esni ESNI 已启用,但标记为“实验”。 谨慎使用!

$ make

成功构建包后,我们将使用 openssl 中的特殊 bash 文件来配置和运行curl。 为了方便起见,我们用curl将其复制到目录中:

cp /opt/openssl/esnistuff/curl-esni 

并向 cloudflare 服务器发出测试 https 请求,同时在 Wireshark 中记录 DNS 和 TLS 数据包。

$ ESNI_COVER="www.hello-rkn.ru" ./curl-esni https://cloudflare.com/

在服务器响应中,除了来自openssl和curl的大量调试信息之外,我们还将收到来自cloudflare的带有代码301的HTTP响应。

HTTP/1.1 301 Moved Permanently
< Date: Sun, 03 Nov 2019 13:12:55 GMT
< Transfer-Encoding: chunked
< Connection: keep-alive
< Cache-Control: max-age=3600
< Expires: Sun, 03 Nov 2019 14:12:55 GMT
< Location: https://www.cloudflare.com/

这表明我们的请求已成功传递到目标服务器、被听到并被处理。

现在让我们看一下wireshark中的流量转储,即提供商 DPI 在这种情况下看到了什么。

基于 TLS 1.3 的域名前置

可以看到,curl 首先向 DNS 服务器寻求 cloudflare 服务器的公共 eSNI 密钥——对 _esni.cloudflare.com(包号 13)的 TXT DNS 请求。 然后,使用 openssl 库,curl 向 cloudflare 服务器发送 TLS 1.3 请求,其中 SNI 字段使用上一步中获得的公钥(数据包 #22)进行加密。 但是,除了 eSNI 字段之外,SSL-hello 数据包还包含一个通常的字段 - open SNI,我们可以按任何顺序指定该字段(在本例中 - www.hello-rkn.ru).

Cloudflare 服务器处理时不会以任何方式考虑此开放 SNI 字段,并且仅充当提供商 DPI 的掩码。 cloudflare 服务器收到我们的 ssl-hello 数据包,解密 eSNI,从那里提取原始 SNI 并处理它,就像什么都没发生一样(它完全按照开发 eSNI 时计划的那样进行)。

在这种情况下,从 DPI 的角度来看,唯一可以捕获的是对 _esni.cloudflare.com 的主 DNS 请求。 但我们开放 DNS 请求只是为了展示该机制如何从内部工作。

为了最终摆脱 DPI 的困扰,我们使用了已经提到的 DNS-over-HTTPS 机制。 一点解释 - DOH 是一种协议,允许您通过 HTTPS 发送 DNS 请求来防止中间人攻击。

让我们再次执行请求,但这次我们将通过 https 协议而不是 DNS 接收公共 eSNI 密钥:

ESNI_COVER="www.hello-rkn.ru" DOH_URL=https://mozilla.cloudflare-dns.com/dns-query ./curl-esni https://cloudflare.com/

请求流量转储如下图所示:

基于 TLS 1.3 的域名前置

可以看到,curl首先通过DoH协议访问mozilla.cloudflare-dns.com服务器(https连接服务器104.16.249.249),从中获取用于SNI加密的公钥值,然后到达目的地服务器,隐藏在域后面 www.hello-rkn.ru.

除了上面的 DoH 解析器 mozilla.cloudflare-dns.com 之外,我们还可以使用其他流行的 DoH 服务,例如来自著名的邪恶公司的服务。
让我们运行以下查询:

ESNI_COVER="www.kremlin.ru" DOH_URL=https://dns.google/dns-query ./curl-esni https://rutracker.nl/

我们得到了答案:

< HTTP/1.1 301 Moved Permanently
< Date: Sun, 03 Nov 2019 14:10:22 GMT
< Content-Type: text/html
< Transfer-Encoding: chunked
< Connection: keep-alive
< Set-Cookie: __cfduid=da0144d982437e77b0b37af7d00438b1a1572790222; expires=Mon, 02-Nov-20 14:10:22 GMT; path=/; domain=.rutracker.nl; HttpOnly; Secure
< Location: https://rutracker.nl/forum/index.php
< CF-Cache-Status: DYNAMIC
< Expect-CT: max-age=604800, report-uri="https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct"
< Server: cloudflare
< CF-RAY: 52feee696f42d891-CPH

基于 TLS 1.3 的域名前置

在这种情况下,我们转向被阻止的rutracker.nl服务器,使用DoH解析器dns.google(这里没有拼写错误,现在著名的公司有自己的一级域名)并用另一个域名覆盖我们自己,这是严格的禁止所有 DPI 封锁,违者处死。 根据收到的回复,您可以了解到我们的请求已成功处理。

作为对提供商的 DPI 响应开放 SNI(我们将其作为掩护进行传输)的额外检查,我们可以以其他一些禁止资源(例如另一个“良好”的 torrent 跟踪器)为幌子向 rutracker.nl 发出请求:

$ ESNI_COVER="rutor.info" DOH_URL=https://dns.google/dns-query ./curl-esni https://rutracker.nl/

我们不会收到服务器的响应,因为...... 我们的请求将被 DPI 系统阻止。

第一部分的简短结论

因此,我们能够使用 openssl 和curl 演示 eSNI 的功能,并测试基于 eSNI 的域前置操作。 以同样的方式,我们可以调整我们最喜欢的使用 openssl 库的工具,以“在其他域的幌子下”工作。 有关此内容的更多详细信息,请参阅我们的下一篇文章。

来源: habr.com

添加评论