基于TLS 1.3的前端域

引言



来自思科,BlueCoat和FireEye等著名制造商的现代公司内容过滤系统与功能更强大的同类产品-DPI系统有着很多共同点,而DPI系统已在国家一级得到广泛实施。 两者工作的实质是搜索传入和传出的Internet流量,并根据黑名单/白名单来决定禁止Internet连接。 而且由于他们在工作的基础上都依赖相似的原则,因此规避它们的方法也将有很多共同点。

可以非常有效地绕过DPI和公司系统的技术之一是面向域的技术。 其实质在于,我们进入了一个被阻止的资源,该资源被隐藏在具有良好声誉的另一个公共领域的后面,显然,该资源不会被任何系统(例如google.com)所阻止。

关于该技术的文章很多,并且给出了很多例子。 但是,HTTPS上的DNS和加密的SNI技术以及最近讨论的技术以及TLS 1.3协议的新版本为考虑面向域的另一种变体提供了机会。

我们处理技术


首先,让我们对基本概念做出一些决定,以便每个人都了解谁是谁以及为什么需要所有这些。 我们提到了eSNI机制,其工作将在后面讨论。 eSNI(加密服务器名称指示)机制是SNI的安全版本,仅可用于TLS 1.3协议。 重点是加密,包括有关将请求发送到哪个域的信息。

现在让我们看一下eSNI机制在实践中是如何工作的。

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



在ILV网站上,确实在停止列表中列出了该域:



当您请求Whois时,您会看到域本身被“隐藏”在云提供商Cloudflare的后面。



但是,不同于ILV的“专家”,技术上比较精明的Beeline员工(或由我们著名的监管机构的惨痛经历所教taught)没有愚蠢地通过IP地址禁止该站点,而是在停止列表中输入了该域名。 如果查看其他IP地址后面隐藏的域,然后访问其中一个域并查看访问是否被阻止,这很容易验证:



但是怎么发生的呢? 由于所有通信都是通过https协议进行的,因此DPI提供程序如何确定我的浏览器进入了哪个域,而且我们似乎还没有注意到蜂巢中的https证书已被替换? 他是千里眼还是被我跟随?

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



屏幕快照显示,首先浏览器通过DNS接收服务器的IP地址,然后与目标服务器进行标准的TCP握手,然后浏览器尝试建立与服务器的ssl连接。 为此,它将发送SSL Client Hello数据包,其中包含明文形式的源域名。 cloudflare前端服务器需要此字段,以便正确路由连接。 这是提供商DPI抓住我们的地方,破坏了我们的联系。 同时,我们没有从提供商那里收到任何存根,并且我们看到一个标准的浏览器错误,好像该站点已断开连接或根本不起作用:



现在,按照Firefox的说明中所述,在浏览器中启用eSNI机制:
为此,我们打开有关Firefox的配置页面:config并激活以下设置:

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

之后,我们将使用该链接检查cloudflare网站上设置的正确性,然后再次尝试使用我们的洪流跟踪器进行聚焦。



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



这次,ssl客户端hello程序包未显式包含目标域,而是在程序包中出现了一个新字段“ encrypted_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,但标记为EXPERIMENTAL。 请谨慎使用!

 $ make 

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

 cp /opt/openssl/esnistuff/curl-esni 

并向cloudflare服务器执行测试https请求,同时将DNS和TLS数据包写入Wireshark。

 $ 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在这种情况下看到的内容。



可以看出,第一个curl转向了cloudflare服务器的公共eSNI密钥的DNS服务器-向_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/ 

请求流量转储显示在以下屏幕截图中:



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

除了上面指定的mozilla.cloudflare-dns.com解析器DoH,我们还可以使用其他流行的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 



在这种情况下,我们使用dns.google DoH解析器(没有拼写错误,现在这家著名的公司拥有自己的一级域名)转到被阻止的服务器rutracker.nl,并用另一个域覆盖了我们所有的DPI都严格禁止对其进行阻止担心死刑。 根据答案,您可以了解我们的请求已成功处理。

作为提供者DPI对打开的SNI做出响应的另一项检查,我们将其作为掩护传递给我们-我们可以在其他禁止的资源(例如,另一个“良好”的洪流跟踪器)后面执行对rutracker.nl的请求:

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

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

第一部分的小结


因此,我们能够使用openssl和curl展示eSNI的运行状况,并测试基于eSNI的基于域的前端的运行情况。 以同样的方式,我们可以使用openssl库修改我们喜欢的工具,使其“隐藏”在其他域中。 在我们的下一篇文章中对此有更多的了解。

Source: https://habr.com/ru/post/zh-CN475372/


All Articles