Domain Fronting,域前置,引用WikiPedia的说明为:
Domain fronting is a technique that circumvents internet censorship by obfuscating the domain of a HTTPS connection.
在介绍这项技术之前,首先来看看我们是如何和一个网页交互的。
首先是网络请求。
- 用户向浏览器输入一个URL,它包含一个hostname。
- 操作系统通过hostname做一个DNS lookup
- 通过lookup得到的IP地址进行两个机器之间的TCP连接
现在已经建立了网络连接,这一切开始发生在应用层中,并且发送了HTTP请求。
接下来介绍HTTP协议对于Host头的处理方式:
在HTTP1.0中认为每台服务器都绑定一个唯一的IP地址,因此,请求消息中的URL并没有传递主机名(hostname)。但随着虚拟主机技术的发展,在一台物理服务器上可以存在多个虚拟主机(Multi-homed Web Servers),并且它们共享一个IP地址。HTTP1.1的请求消息和响应消息都支持Host头域,且请求消息中如果没有Host头域会报告一个错误(400 Bad Request)。
简单说有这个的好处是:若多个域名对应同一个IP,那么当根据不同的域名访问到同一个IP的时候,服务端不知道该响应哪一种服务,而如果有Host,服务端就知道这个请求是从哪个域名访问来的,就可以提供对应的服务。
下面是一个简单的表示HTTP/1.1 request的例子
GET / HTTP/1.1 Host: test.com
下面用curl来演示,以下请求将与Google英国的服务器建立网络连接,但将通过Host头请求网站google.co.jp。由于英国的Google服务器知道Google有不同的虚拟主机,因此能够正确路由该请求至日本服务器并返回适当的内容。
~ curl -H "host: www.google.co.jp" www.google.co.uk <!doctype html><html itemscope="" itemtype="http://schema.org/WebPage" lang="en-GB"><head><meta content="text/html; charset=UTF-8" http-equiv="Content-Type"><meta content="/images/branding/googleg/1x/googleg_standard_color_128dp.png" itemprop="image"><title>Google</title>
如果我们对bing.com发起网络连接,通过Host头请求网站google.com,猜猜会发生什么?
~ curl -H "Host: google.com" https://bing.com <h2>Our services aren't available right now</h2><p>We're working to restore all services as soon as possible. Please check back soon.</p>0HkzzXgAAAAD6JP/Pad+LS6lg4Vf2D0EBTE9OMDRFREdFMDUxMABFZGdl
可以看到发生错误,因为bing.com根本没有叫做google.com的虚拟主机。
了解了基本概念,让我们看看如何将其用于Domain Fronting。
当站点使用CDN(Content Delivery Network)时,将为该域设置CNAME记录,也叫别名记录,相当于给A记录中的域名起一个别名,使其指向CDN服务器。在CDN Web服务器上设置了vhost,因此它可以响应请求。该设置被赋予一个“原始”服务器,该服务器与传入域配对,因此它知道去哪里检索要提供的实际内容。
那么网站的证书怎么处理?会不会因为不同的hostname和Host头发生一些问题,这其实在开头的基础介绍中就有答案 首先要进行的是网络请求,一旦建立TCP连接,则TLS协商便开始了,下面给一张网上的图
并且都基于在连接时的hostname,别忘了,Host头是HTTP协议的事情,所以Host头在应用层的流量中,直到所有底层网络连接都建立后才能进入。
下面我们还是用curl,使用对bing.com发起网络连接,通过Host头请求网站google.com,并且添加-v参数查看更多细节:
~ curl -v -H "Host: google.com" https://bing.com * Rebuilt URL to: https://bing.com/ * Trying 127.0.0.1... * TCP_NODELAY set * Connected to 127.0.0.1 (127.0.0.1) port 7890 (#0) * Establish HTTP proxy tunnel to bing.com:443 > CONNECT bing.com:443 HTTP/1.1 > Host: bing.com:443 > User-Agent: curl/7.54.0 > Proxy-Connection: Keep-Alive > < HTTP/1.1 200 Connection established < * Proxy replied OK to CONNECT request * ALPN, offering h2 * ALPN, offering http/1.1 * Cipher selection: ALL:!EXPORT:!EXPORT40:!EXPORT56:!aNULL:!LOW:!RC4:@STRENGTH * successfully set certificate verify locations: * CAfile: /etc/ssl/cert.pem CApath: none * TLSv1.2 (OUT), TLS handshake, Client hello (1): * TLSv1.2 (IN), TLS handshake, Server hello (2): * TLSv1.2 (IN), TLS handshake, Certificate (11): * TLSv1.2 (IN), TLS handshake, Server key exchange (12): * TLSv1.2 (IN), TLS handshake, Server finished (14): * TLSv1.2 (OUT), TLS handshake, Client key exchange (16): * TLSv1.2 (OUT), TLS change cipher, Client hello (1): * TLSv1.2 (OUT), TLS handshake, Finished (20): * TLSv1.2 (IN), TLS change cipher, Client hello (1): * TLSv1.2 (IN), TLS handshake, Finished (20): * SSL connection using TLSv1.2 / ECDHE-RSA-AES256-GCM-SHA384 * ALPN, server accepted to use h2 * Server certificate: * subject: CN=www.bing.com * start date: Apr 30 20:48:00 2019 GMT * expire date: Apr 30 20:48:00 2021 GMT * subjectAltName: host "bing.com" matched cert's "bing.com" * issuer: C=US; ST=Washington; L=Redmond; O=Microsoft Corporation; OU=Microsoft IT; CN=Microsoft IT TLS CA 2 * SSL certificate verify ok. * Using HTTP2, server supports multi-use * Connection state changed (HTTP/2 confirmed) * Copying HTTP/2 data in stream buffer to connection buffer after upgrade: len=0 * Using Stream ID: 1 (easy handle 0x7faaa5000400) > GET / HTTP/2 > Host: google.com > User-Agent: curl/7.54.0 > Accept: */* > * Connection state changed (MAX_CONCURRENT_STREAMS updated)! < HTTP/2 400 < x-msedge-ref: 0YFLzXgAAAABVlycK+OLNQJ/RS/y1Gx2RTE9OMDRFREdFMDMwNwBFZGdl < date: Wed, 24 Jun 2020 13:17:19 GMT < * Connection #0 to host 127.0.0.1 left intact <h2>Our services aren't available right now</h2><p>We're working to restore all services as soon as possible. Please check back soon.</p>0YFLzXgAAAABVlycK+OLNQJ/RS/y1Gx2RTE9OMDRFREdFMDMwNwBFZGdl%
并且可以看到关键信息:
* subject: CN=www.bing.com * start date: Apr 30 20:48:00 2019 GMT * expire date: Apr 30 20:48:00 2021 GMT * subjectAltName: host "bing.com" matched cert's "bing.com"
正在使用Bing的证书协商TLS连接,并且直到在HTTP请求中发送了主机标头之前,都没有Google.com的影子。
所以我们可以利用这些特性,在和 CDN 服务器通信时,我们可以选择连接一个具有“高信誉”的hostname,然后在 HTTP 请求包的 Host 头中填入我们要访问的域名,这样就达到了隐匿C2服务器的目的。简言之,利用该技术,可以让受控端以为是在和一个“高信誉”域名通信,但实际上却是在和我们的C2服务器进行通信。
全球著名的俄罗斯俄罗斯政府情报组织 APT29 也曾被披露使用这项技术实现恶意后门程序突破域防火墙的检查。
该技术也可以用于绕过审查过滤器和其他类似的审查。
另外还有一种 Domainless Fronting技术,如果CDN服务器尝试检验SNI字段和Host字段是否匹配,攻击者可以将SNI字段设置为空。这样如果CDN服务器检验的时候忽略了空的SNI字段,就可以成功导向攻击者的服务器。
下面来实现一下 Cobalt Strike 的 Domain Fronting:
首先在国内某云平台申请CDN,具体过程略。
正常情况下,第一步我们要做的是先申请一个域名(这个域名的作用是用来配合 CDN 来隐藏我们的C2服务器),在这个过程中CDN会要求我们在域名的解析配置中设置相应的 CNAME。但由于某云有一个有趣的特点:当 CDN 配置中的源 IP 为自己云的服务器时,加速时会跳过对域名的检验,直接与配置中的域名绑定的源服务器IP进行通信。利用该特性,我们不需要真正去申请 CDN 时所填写的域名中配置解析相应的 CNAME。换言之,只要我们的C2服务器属于该云的服务器,那么我们就无需申请域名,只需要在申请 CDN 时随便填一个没有人绑定过的域名,而且这个域名我们可以填成任何高信誉的域名。
https://www.chabug.org/web/1373.html
然后配置C2 profile:
修改其中的header为CDN绑定的域名
使用开源项目 Malleable-C2-Profiles 中的 amazon.profile
profile链接:https://github.com/rsmudge/Malleable-C2-Profiles/blob/master/normal/amazon.profile
接着启动teamserver
然后打开 Cobalt Strike 的 Web Log,可以查看到对应的请求
然后创建CS的Listener,主机填写CDN时的域名,端口为80。
Listener创建完成后,由于使用了 Domain Fronting 技术,Cobalt Strike 默认生成的 Agent 会崩溃,因此我们需要一个针对 Domain Fronting 技术进行定制化的 Agent Generator:https://github.com/mdsecactivebreach/CACTUSTORCH
接下来使用Agent Generator:
Local Host填入CS服务器公网IP
然后我们在受控机器上执行,发现成功上线
并且使用WireShark抓包发现已经成功伪装成我们的高信誉域名。
另外没有发现与我们真实C2服务器之间的通信,成功实现了Domain Fronting技术
利用 Domain Fronting 技术,在受控机器不会产生任何与真实C2服务器的直接通信,只看到是和一个“高信誉”域名进行通信,伪装效果极佳,利用Domain Fronting,红队可以有效的躲避一些审查,实施红队行动。