利用HTTP管道化隐藏恶意请求的技术解析

本文详细介绍了如何利用HTTP管道化技术隐藏恶意HTTP请求,通过在同一TCP连接中发送多个请求实现流量伪装。文章包含技术原理、实际测试案例及对AWS CloudFront和Cloudflare的对比分析,为安全研究人员提供实用参考。

利用HTTP管道化隐藏请求

在本篇文章中,我将讨论使用HTTP管道化来隐藏恶意HTTP请求。这并非域名前置(domain fronting),但使用类似技术达到相同效果:无法执行TLS拦截的观察者只能看到“正常”请求,从而隐藏了“恶意”请求。

背景介绍

在互联网早期,客户端请求的每个对象都使用独立的TCP连接。如果一个页面包含两张图片和一个JavaScript库,则需要建立四个连接:第一个用于页面本身,另外三个用于额外元素。这被认为增加了过多流量,特别是在使用HTTPS时,因为每个元素都需要协商安全连接。为解决此问题,引入了管道化技术。

管道化允许将多个请求分组并通过单个TCP连接发送,消除了为每个页面元素建立和拆除独立连接的需要,从而应提高性能。这一概念在一段时间内运作良好,大多数现代浏览器和Web服务器都内置了支持。然而,到2018年,实现中发现了太多问题,因此该功能在浏览器中被禁用。尽管如此,大多数服务器以及更重要的是大多数CDN仍然支持此功能。

更多信息可参考维基百科页面:HTTP管道化

示例请求

以下是我将发送的管道文件内容:

1
2
3
4
5
GET /pipeline/page1.php HTTP/1.1
Host: vuln-demo.com

GET /pipeline/page2.php HTTP/1.1
Host: vuln-demo.com

如您所见,有两个请求:第一个针对page1.php,第二个针对page2.php。它们将通过同一连接依次发送。这两个请求都可以发送,由服务器处理,然后按顺序返回响应。此概念现已被弃用的原因之一是,如果第一个请求需要很长时间返回,所有后续请求将被阻塞等待,最终可能减慢速度而非按设计加速。

持久连接与管道化的区别

在开始研究之前,我假设通过发送Connection: keep-alive头激活管道化,但我错了。“keep-alive”启用持久连接,这与管道化不同。持久连接在请求之间保持TCP连接开放,但强制执行原始规则,即在发出新请求之前等待任何先前请求返回。管道化移除了该规则,允许客户端发送多个请求而无需等待。您必须具有持久连接才能进行管道化,但具有持久连接并不一定意味着您可以进行管道化。在HTTP 1.0中,必须使用“keep-alive”头激活持久性;在HTTP 1.1中,除非使用Connection: close头请求关闭连接,否则假定持久性。有关持久连接的更多信息,请参见HTTP持久连接

发送请求

使用以下命令发送请求:

1
$ (cat pipe ; sleep 5) | openssl s_client -connect vuln-demo.com:443 -servername vuln-demo.com

响应如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
HTTP/1.1 200 OK
Date: Fri, 08 Mar 2019 20:42:47 GMT
Server: Apache
Strict-Transport-Security: max-age=63072000
Upgrade: h2,h2c
Connection: Upgrade, Keep-Alive
X-Content-Type-Options: nosniff
Cache-Control: max-age=0, no-cache, no-store, must-revalidate
Pragma: no-cache
Expires: Wed, 11 Jan 1984 05:00:00 GMT
X-XSS-Protection: 0;
Access-Control-Allow-Origin: https://vuln-demo.com
Access-Control-Allow-Headers: Origin, X-Requested-With, Content-Type, Access-Control-Allow-Origin
Content-Length: 14
Keep-Alive: timeout=5, max=100
Content-Type: text/html; charset=UTF-8

This is page 1HTTP/1.1 200 OK
Date: Fri, 08 Mar 2019 20:42:47 GMT
Server: Apache
Strict-Transport-Security: max-age=63072000
X-Content-Type-Options: nosniff
Cache-Control: max-age=0, no-cache, no-store, must-revalidate
Pragma: no-cache
Expires: Wed, 11 Jan 1984 05:00:00 GMT
X-XSS-Protection: 0;
Access-Control-Allow-Origin: https://vuln-demo.com
Access-Control-Allow-Headers: Origin, X-Requested-With, Content-Type, Access-Control-Allow-Origin
Content-Length: 14
Content-Type: text/html; charset=UTF-8

This is page 2DONE

可以看到有两个响应:页面1和页面2,表明管道化工作正常。

测试AWS CloudFront

由于我已设置AWS CloudFront,我将重用该设置进行测试。有关设置的完整详细信息,请参阅我上一篇关于CloudFront的文章开头的描述;简而言之,“fronted.digi.ninja”是位于我主站点“digi.ninja”前面的“正常”域名,而“d1sdh26o090vk5.cloudfront.net”是指向站点“frontme.vuln-demo.com”的“恶意”域名。请求如下:

1
2
3
4
5
GET / HTTP/1.1
Host: fronted.digi.ninja

GET / HTTP/1.1
Host: d1sdh26o090vk5.cloudfront.net

结果如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
$ (cat pipe2 ; sleep 5) | openssl s_client -connect fronted.digi.ninja:443 -servername fronted.digi.ninja | \
  grep "<title>"
depth=2 C = US, O = Amazon, CN = Amazon Root CA 1
verify return:1
depth=1 C = US, O = Amazon, OU = Server CA 1B, CN = Amazon
verify return:1
depth=0 CN = fronted.digi.ninja
verify return:1
	<title>DigiNinja - DigiNinja</title>
	<title>Fronted Vuln Demo</title>
DONE

我已对结果进行grep筛选,仅显示页面标题,但如您所见,第一个结果来自我的站点,第二个来自vuln-demo站点,表明CloudFront支持管道化且请求按预期工作。任何观察请求的人都会看到对“正常”域名“fronted.digi.ninja”的DNS查找,以及TLS设置中的SNI字段(也是“fronted.digi.ninja”),但不会看到对“d1sdh26o090vk5.cloudfront.net”的任何请求。

与Cloudflare的对比测试

让我们看看Cloudflare的SNI保护是否有助于防止管道化。以下是将发送的请求:

1
2
3
4
5
GET / HTTP/1.1
Host: www.cloudflare.com

GET /index.php HTTP/1.1
Host: digininja.org.uk

回复如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
(cat pipe3 ; sleep 5) | openssl s_client -connect cloudflare.com:443 -servername www.cloudflare.com | \
  grep "<title>"

depth=2 C = US, O = DigiCert Inc, OU = www.digicert.com, CN = DigiCert High Assurance EV Root CA
verify return:1
depth=1 C = US, O = DigiCert Inc, OU = www.digicert.com, CN = DigiCert ECC Extended Validation Server CA
verify return:1
depth=0 businessCategory = Private Organization, jurisdictionC = US, jurisdictionST = Delaware, serialNumber = 4710875, C = US, ST = California, L = San Francisco, O = "Cloudflare, Inc.", CN = cloudflare.com
verify return:1
    <title>Cloudflare - The Web Performance & Security Company
<head><title>403 Forbidden</title></head>
DONE

由于请求中使用的SNI是“www.cloudflare.com”,连接绑定到该主机,因此对“digininja.org.uk”的请求被拒绝,返回403,表明它们受到保护。

结论

因此,我们有了另一种隐藏HTTP流量的方法,该方法适用于AWS但不适用于Cloudflare。在未来的某个时候,我将尝试所有这些方法与Azure CDN和Google CDN的兼容性。在此之前,您有大量内容可以尝试,所以去实验吧。

相关文章

comments powered by Disqus
使用 Hugo 构建
主题 StackJimmy 设计