基于暂停的异步攻击

看似安全的网站可能包含隐藏的异步漏洞,这些漏洞只有在你在请求中途暂停时才会显露出来。

服务器通常配置有读取超时。如果它们在一定时间内没有收到更多的数据,它们就会将请求视为完成并发出响应,无论它们被告知要期望多少字节。当服务器超时一个请求但保持连接开放以供重用时,就可能会发生基于暂停的异步漏洞。在适当的条件下,这种行为可以为服务器端和客户端异步攻击提供另一种载体。

PortSwigger Research

本节中的材料和实验基于PortSwigger Research的《浏览器驱动的异步攻击:HTTP请求走私的新前沿》。

服务器端基于暂停的异步

可以使用基于暂停的技术来引发类似CL.0的行为,从而允许你能够为看似安全的网站构造服务器端请求走私漏洞。

这取决于以下条件:

  • 前端服务器必须立即将请求的每个字节转发到后端,而不是等接收到完整的请求后再转发。

  • 前端服务器不得(或可以被鼓励不)在后端服务器之前请求超时。

  • 后端服务器在读取超时后必须保持连接开放以供重用。

为了演示这种技术是如何工作的,让我们来看一个例子。以下是一个标准的CL.0请求走私探测:

POST /example HTTP/1.1
Host: vulnerable-website.com
Connection: keep-alive
Content-Type: application/x-www-form-urlencoded
Content-Length: 34

GET /hopefully404 HTTP/1.1
Foo: x

考虑一下,如果我们向一个易受攻击的网站发送标头,但在发送正文之前暂停会发生什么。

  1. 前端将标头转发到后端,然后继续等待Content-Length标头承诺的剩余字节。

  2. 一段时间后,后端超时并发送响应,尽管它只消耗了部分请求。此时,前端可能会也可能不会读取这个响应并将其转发给我们。

  3. 我们最后发送正文,在本例中它包含一个基本的请求走私前缀。

  4. 前端服务器将此视为初始请求的延续,并将其转发到同一连接下的后端。

  5. 后端服务器已经对初始请求做出了响应,因此假设这些字节是另一个请求的开始。

至此,我们已经有效地实现了一个CL.0异步,用一个请求前缀毒化了前端/后端连接。

我们发现,当服务器自己生成响应而不是将请求传递给应用程序时,更容易受到攻击。

PortSwigger Research

我们的研究团队在广泛使用的Apache HTTP服务器上发现了这个漏洞,当它执行从/example/example/的服务器级重定向时,就会出现这种行为。我们已经报告了这个问题,并且它已经在2.4.53版本中得到修复,所以如果你还没有更新,请务必更新。欲了解更多详情,请查看PortSwigger Research的《浏览器驱动的异步攻击:HTTP请求走私的新前沿》。

测试基于暂停的CL.0漏洞

使用Burp Repeater可以测试基于暂停的CL.0漏洞,但只有当前端服务器在后端超时响应生成的那一刻将其转发给你时才能这样做,而这并不总是会发生。我们推荐使用Turbo Intruder扩展,因为它可以让你在请求中间暂停,然后无论你是否收到响应都能继续。

  1. 在Burp Repeater中,创建一个像我们在上面的例子中使用的CL.0请求走私探测,然后将其发送到Turbo Intruder。

    POST /example HTTP/1.1
    Host: vulnerable-website.com
    Connection: keep-alive
    Content-Type: application/x-www-form-urlencoded
    Content-Length: 34
    
    GET /hopefully404 HTTP/1.1
    Foo: x
  2. 在Turbo Intruder的Python编辑器面板中,调整请求引擎配置以设置以下选项:

    concurrentConnections=1
    requestsPerConnection=100
    pipeline=False
  3. 将请求加入queue,向queue()接口添加以下参数:

    • pauseMarker——你希望Turbo Intruder暂停的字符串列表。

    • pauseTime——暂停的持续时间,以毫秒为单位。

    例如,要在标头后暂停60秒,请按如下方式将请求加入queue:

    engine.queue(target.req, pauseMarker=['\r\n\r\n'], pauseTime=60000)
  4. 正常地将一个任意的后续请求加入queue:

    followUp = 'GET / HTTP/1.1\r\nHost: vulnerable-website.com\r\n\r\n'
    engine.queue(followUp)
  5. 确保你将所有响应记录到结果表中:

    def handleResponse(req, interesting):
        table.add(req)

当你首次开始攻击时,不会在表中看到任何结果。然而,经过指定的暂停时间后,应该能看到两个结果。如果第二个请求的响应与你期望的走私前缀匹配(在本例中是404),则强烈表明异步是成功的。

LAB

服务器端基于暂停的请求走私

注意

可以使用pauseBefore参数来指定偏移量,而不是使用pauseMarker基于字符串匹配来指定暂停。例如,你可以通过指定与Content-Length相反的偏移(pauseBefore=-34)来在正文前暂停。

客户端基于暂停的异步

理论上,可能能进行基于暂停的CL.0异步的客户端变种。遗憾的是,我们还没有找到一种可靠的方法使浏览器在请求中间暂停。不过,有一个可能的变通方法——主动MITM攻击。

TLS提供的加密可能会阻止MITM读取传输中的流量,但没有什么能阻止它们延迟从浏览器到web服务器的TCP数据包。只需延迟最后一个数据包,直到web服务器发出响应,你可能就能够异步浏览器的连接。

这种攻击的流程与任何其他的客户端异步攻击类似。用户访问恶意网站,导致他们的浏览器向目标站点发出一系列跨域请求。在这种情况下,需要故意填充第一个请求,以便操作系统将其拆分成多个TCP数据包。由于你控制填充,所以你可以填充请求直到最后一个数据包具有明显不同的大小,这样就可以找出需要延迟的数据包。

有关这种攻击在实践中可能会看起来如何的示例,请参见《浏览器驱动的异步攻击:HTTP请求走私的新前沿》.

如何防止基于暂停的异步漏洞

关于你可以采取的一些高级措施,以防止基于暂停的异步漏洞和其他形式的异步攻击,请参阅如何防止HTTP请求走私漏洞

Last updated