基于TLS 1.3的前端域。 第二部分

引言


在本文的第一部分中我们简要介绍了加密的SNI(eSNI)机制。 他们展示了如何在其基础上逃避现代DPI系统的检测(以Beeline DPI和禁止的ILV rutracker为例),以及探索基于此机制的域前端的新版本。

在本文的第二部分,我们将继续讨论一些更实际的事情,RedTeam将对专家的艰苦工作很有用。 最后,我们的目标不是获得对阻塞资源的访问权限(对于这种常见的事情,我们有一个很好的旧VPN)。 幸运的是,正如他们所说,对于每种口味,颜色和预算,都有很多VPN提供商。

我们将尝试对现代RedTeam工具(例如Cobalt Strike,Empire等)应用领域前沿机制,并为它们提供更多机会模仿和规避现代内容过滤系统。

上一次,我们在OpenSSL库中实现了eSNI机制,并在熟悉的curl实用程序中成功使用了它。 但是,正如他们所说,卷发不会充满。 当然,我想用高级语言实现类似的功能。 但是,不幸的是,对网络广阔性的粗略搜索使我们感到失望,因为仅在GOLANG中完全实现了对eSNI机制的支持。 因此,我们的选择不是很大:要么使用修补的OpenSSL库以纯C或C ++编写代码,要么使用CloudFlare的单独GOLANG分支,然后尝试将工具包移植到那里。 原则上,还有另一个选择,更经典,但也很耗时-实现对python的eSNI支持。 毕竟,Python还使用OpenSSL与https一起工作。 但是我们将这个选项留给其他人开发,我们将对Golang的实现感到满意,特别是因为我们心爱的Cobalt Strike完全能够与由第三方工具构建的通信通道(外部C2通道)一起工作-我们将在本文结尾处进行讨论。

努力尝试...


Go上实现的工具之一是rsockstun 调谐器 ,它是我们在网络内部进行旋转的开发工具,顺便说一句,现在,Microsoft和Symantec工具已将其检测为非常恶意的软件,旨在破坏世界的稳定性...



在这种情况下,使用以前的开发将是很棒的。 但是,这里出现了一个小问题。 事实是rsockstun最初意味着与服务器使用同步SSL通信通道。 这意味着该连接仅建立一次,并且在隧道操作的整个过程中都存在。 而且,如您所知,https协议不是为此操作模式设计的-它在请求-响应模式下工作,在该模式下,每个新的http-request都存在于新的tcp连接的框架中。

此方案的主要缺点是,服务器无法在客户端发送新的http请求之前将数据传输到客户端。 但是,幸运的是,有许多解决此问题的方法-通过http协议流式传输数据(最后,我们设法以某种方式观看我们喜欢的电视节目并听取运行在https上的门户网站的音乐,而视频和音频传输并非如此。而不是流数据)。 WebSockets技术是用于模拟基于http协议的完整tcp连接的操作的技术之一,其主要实质是在客户端与Web服务器之间建立完整的网络连接。

令我们幸运的是,此技术默认包含在所有CloudFlare收费计划中,并且与eSNI结合使用时效果很好。 这正是我们将要用来教导我们的隧道使用域前端并对现代DPI隐藏的内容。

关于WebSockets的一些知识


首先,我们将简单扼要地介绍一下Web套接字,以便每个人都知道我们将使用什么。

Web套接字技术使您可以通过网络套接字从http连接临时切换到标准流数据,而不会破坏已建立的tcp连接。 当客户端想要切换到Web套接字时,他在http请求中设置了几个http标头。 两个必需的标头是Connection:UpgradeUpgrade:websocket 。 它还可以强制使用websocket协议版本( Sec-Websockset-Version:13 )和类似base64 Web套接字标识符的内容( Sec-WebSocket-Key:DAGDJSiREI3 + KjDfwxm1FA == )。 服务器以http代码101交换协议作为响应,并设置Connection,UpgradeSec-WebSocket-Accept标头。 切换过程如下图所示:



之后,可以认为WebSocket连接已完成。 现在,客户端和服务器的所有数据都将不提供http,而是提供WebSocket标头(它们以字节0x82开头)。 现在,服务器不需要等待客户端的请求即可传输数据,因为 tcp连接未断开。

Web套接字套接字中有几个库。 其中最流行的是Gorilla WebSocket和标准WebSocket 。 我们将使用后者,因为 正如他们所说,它更简单,更小且工作更快。

在rsockstun客户端代码中,我们需要用相应的WebSocket调用替换net.dial或tls.dial调用:





我们希望使客户端成为隧道的通用部分,并能够通过直接ssl连接和WebSockset协议来工作。 为此,我们将创建一个单独的函数func connectForWsSocks(地址字符串,代理字符串)错误{...} ,类似于connectForSocks() ,如果客户端启动时指定的服务器地址以开头,我们将使用它与Web套接字配合使用。 ws:或wss :(对于Secure WebSocket)。

对于隧道的服务器端,我们还将为Web套接字提供一个单独的功能。 将在其中创建http类的实例,并设置http连接处理程序(wsHandler函数):



我们将把所有用于处理连接的逻辑(使用密码授权客户端,安装并结束yamux会话)放入WebSocket连接处理程序中:



我们编译项目,启动服务器部分:

./rsockstun –listen ws:127.0.0.1:8080 –pass P@ssw0rd 

然后客户端部分:

 ./rsockstun -connect ws:127.0.0.1:8080 –pass P@ssw0rd 

然后我们检查本地主机上的工作:





我们传递到领域前沿


我们似乎已经整理了网络套接字。 现在,让我们直接转到eSNI和domain-fronts。 如前所述,要与DoH和eSNI一起使用,我们需要从CloudFlare中获取golang的特殊分支。 我们需要一个支持eSNI的分支机构(pwu / esni)。

我们将其克隆到本地,或者下载并扩展相应的zip:

 git clone -b pwu/esni https://github.com/cloudflare/tls-tris.git 

然后,我们需要复制GOROOT目录,替换克隆分支中的相应文件,并将其设置为主目录。 为了免除开发人员的这种麻烦,CloudFlare的人员准备了一个特殊的脚本_dev /go.sh。 只是运行它。 该脚本以及makefile会自己完成所有操作。 好玩-您可以在makefile中查看详细信息。

编写完脚本后,在编译项目时,我们需要将脚本准备的本地目录指定为GOROOT。 在我们的例子中,它看起来像这样:

 GOROOT="/opt/tls-tris/_dev/GOROOT/linux_amd64" go build …. 

接下来,我们需要在隧道中实现请求和解析所需域的公共eSNI密钥的功能。 在我们的例子中,这些将是来自CloudFlare前端服务器的公共eSNI密钥。 为此,我们将创建三个函数:

 func makeDoTQuery(dnsName string) ([]byte, error) func parseTXTResponse(buf []byte, wantName string) (string, error) func QueryESNIKeysForHost(hostname string) ([]byte, error) 

函数的名称原则上可以说明一切。 我们将从tls-tris的esni_query.go文件中进行填充。 第一个功能使用DoH协议(HTTP-over-HTTPS)创建一个向CloudFlare DNS服务器发送请求的网络数据包,第二个功能解析查询结果并接收公共域密钥的值,第三个功能是前两个的容器。

接下来,我们将为域请求eSNI密钥功能引入我们为Web套接字connectForWsSocks新建的连接函数中。 在服务器部分起作用的地方,设置TLS参数,并设置伪造的“ cover domain”的名称:



这里应该注意,最初,tls-tris分支不是为使用域前端而设计的。 因此,它不会注意服务器的虚假名称(空的serverName字段作为client-hello包的一部分传输)。 为了解决此问题,我们必须将相应的FakeServerName字段添加到TlsConfig结构中。 我们不能使用结构的标准ServerName字段,因为 它由内部tls机制使用,并且如果与原始机制不同,则tls握手将以错误结束。 TlsConfig结构的描述包含在tls / common.go文件中 -我们必须对其进行修复:





此外,在生成TLS握手时,我们将必须对tls / handshake_client.go文件进行更改,以使用FakeServerName字段:



仅此而已! 您可以编译项目并检查工作。 但是在运行测试之前,必须配置您的CloudFlare帐户。 好吧,您怎么说设置-只需创建一个cloudflare帐户并将您的域绑定到该帐户即可。 默认情况下,CloudFlare中包含与DoH,WebSocket和ESNI相关的所有芯片。 DNS记录更新后-您可以通过运行eSNI密钥请求来检查域:

 dig +short txt _esni.df13tester.info 



如果您看到与您的域类似的东西,那么一切对您都有效,您可以继续进行测试。

例如,在DigitalOcean上启动Ubuntu VPS。 PS在我们的情况下,提供商刚刚发布的VPS IP地址在ILV黑名单中。 因此,如果发生类似的情况,请不要感到惊讶。 我必须使用VPN才能到达VPS。

我们将已编译的rsockstun复制到VPS(顺便说一下,这是golang的另一个魅力-您可以自己编译项目并在任何Linux上运行它,仅观察系统的位容量)并启动服务器部分:



然后客户端部分:



如我们所见,客户端使用Web套接字通过CloudFlare前端服务器成功连接到服务器。 要验证隧道是否像隧道一样工作,可以通过服务器上打开的本地socks5发出curl请求:



现在,让我们看看DPI在通信通道中看到的内容:



首先,隧道调谐器使用DoH机制访问Cloudflare DNS服务器以获取目标域的eSNI密钥(软件包编号1-19),然后访问前端服务器并建立TLS连接,该连接隐藏在域www.google.com下 (此值默认情况下,如果在客户端启动时未设置伪域名)。 要指定您的伪域名,必须使用-fronfDomain参数:





现在再来一件事。 默认情况下,CloudFalre的帐户设置设置为“ Flexible SSL”。 这意味着客户端对Cloudflare前端服务器的https请求将以未加密(http)格式重定向到我们的服务器。 这就是为什么我们以非SSL模式(-listen ws:0.0.0.0)而不是(-listen wss:0.0.0.0)启动隧道的服务器部分的原因。



为了切换到完全加密模式,如果服务器上存在此证书,则必须选择“ 完全 ”或“ 完全(严格)” 。 切换模式后,我们将能够通过https协议接受CloudFlare的连接。 记住要为隧道的服务器端生成一个自签名证书。



恼人的读者会问:“ Windows下的客户帐户如何? 确实,确实,隧道的主要应用是提高与公司机器和服务器的反向连接,通常,它始终是Windows。 我该如何为Windows编译隧道,甚至使用特定的TLS堆栈呢?”现在,我们将展示另一款芯片,该芯片显示golang的便捷性。 我们可以直接从Kali编译Windows,只需添加GOOS = windows参数即可:

 GOARCH=amd64 GOROOT="/opt/tls-tris/_dev/GOROOT/linux_amd64" GOOS=windows go build -ldflags="-s -w" 

或32位选项:

 GOARCH=386 GOROOT="/opt/tls-tris/_dev/GOROOT/linux_amd64" GOOS=windows go build -ldflags="-s -w" 

仅此而已! 不再需要麻烦。 确实有效!



需要使用编译器标志–w和–s来从可执行文件中删除多余的垃圾,从而使其减少几兆字节。 此外,可以使用UPX将其打包以进一步减小尺寸。

而不是结论


在本文中,我们以写在golang上的调谐器为例,清楚地展示了在TLS 1.3协议的一个相当有趣的功能上实现的领域前沿新技术的使用。 同样,您可以修改在golang上编写的现有工具包,以通过CloudFlare服务器工作,例如Merlin ,著名的C2,或者在通过外部C2通道 (在golang上或在标准C ++上实现)与Teamserver配合使用时,强制CobaltStrike Beacon使用eSNI域前端。 OpenSSL的修补版本,我们在本文的最后一部分中进行了讨论。 通常,幻想是没有限制的。

以概念的形式介绍了隧道和CloudFlare的示例,关于这种面向领域的远程应用的遥远前景仍然很难说。 目前,仅在CloudFlare上提供eSNI支持,并且从原则上讲,什么也不能阻止它们禁用这种前端,例如,当SNI和eSNI不匹配时,中断tls连接。 总的来说,未来将会展现。 但就目前而言,在kremlin.ru的掩护下工作的前景似乎颇具吸引力。 对不对

更新的隧道代码以及已编译的可执行exe文件位于github上的单独项目分支中。 最好在GitHub的项目页面上写有关所有可能的隧道问题的问题。

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


All Articles