导语:HTTP/2 (原名HTTP/2.0)即超文本传输协议 2.0,是下一代HTTP协议。是由互联网工程任务组(IETF)的Hypertext Transfer Protocol Bis (httpbis)工作小组进行开发。
H2.TE通过标头名称注入
在等待PulseSecure发布补丁的同时,Atlassian尝试了几个修复程序。第一个不允许在标头值中使用换行符,但未能过滤标头名称。这很容易被利用,因为服务器允许在标头名称中使用冒号,这在HTTP/1.1中是不可能实现的。
H2.TE 通过请求行注入
最初的修复程序也没有过滤伪标头,导致请求行注入漏洞。利用这些很简单,只要可视化注入发生的位置,并确保得到的HTTP/1.1请求有一个有效的请求行:
修复程序的最后一个漏洞是一个典型的错误,即阻止' r\n',而不是阻止' n'本身,后者几乎总是被利用。
Desync-Powered 请求隧道
接下来,让我们来看看一些不那么明显但仍然危险的东西。在这项研究中,我注意到由于缺乏一定的漏洞知识而忽略了不同步漏洞。接下来,我将探讨漏洞发生原理,以及缓解措施。
每当前端接收到请求时,它必须决定是通过现有连接将其路由到后端,还是建立到后端的新连接。前端采用的连接重用策略对你能够成功启动哪些攻击有很大影响。
大多数前端很乐意通过任何连接发送任何请求,从而实现我们已经看到的跨用户攻击。然而,你会发现你的前缀只会影响来自你自己 IP 的请求。发生这种情况是因为前端为每个客户端 IP 使用与后端的单独连接。这有点麻烦,但你通常可以通过缓存攻击间接攻击其他用户来解决该问题。
其他一些前端在来自客户端的连接和到后端的连接之间强制执行一对一的关系。这是一个更严格的限制,但是常规的缓存攻击和内部标头泄漏技术仍然适用。
当前端选择从不重用与后端的连接时,攻击就会变得非常具有挑战性。此时发送直接影响后续请求的请求是不可能的:
这就只剩下一个利用原语:请求隧道。这种原语也可以由H2C走私等替代方法产生,但本节将重点讨论不同步驱动的隧道。
隧道确认
检测请求隧道很容易,通常的超时技术工作正常。第一个真正的挑战是确认漏洞,你可以通过发送一系列请求并查看早期请求是否影响后期请求来确认常规请求走私漏洞。不幸的是,这种技术总是无法确认请求隧道,因此很容易将漏洞误认为是误报。
我们需要一种新的确认技术,即简单地走私一个完整的请求,然后看看你是否得到两个响应:
不幸的是,此处显示的响应实际上并未告诉我们此服务器易受攻击!连接多个响应正是 HTTP/1.1 keep-alive 的工作原理,所以我们不知道前端是否认为它正在向我们发送一个响应(并且容易受到攻击)还是两个响应(而且是安全的)。幸运的是,HTTP/2很好地解决了这个问题。如果你在HTTP/2响应正文中看到HTTP/1标头,你就会发现自己是一个不同步:
隧道视野(Tunnel vision)
隧道视野效应:一个人若身处隧道,他看到的就只是前后非常狭窄的视野。 由于第二个问题,这种方法并不总是有效。前端服务器通常使用后端响应上的 Content-Length 来决定从套接字读取多少字节。这意味着即使你可以向后端发出两个请求,并从中触发两个响应,前端也只将第一个不太有趣的响应传递给你。
在以下示例中,由于突出显示了 Content-Length,以橙色显示的403响应从未传递给用户:
Bitbucket(一家源代码托管网站,采用Mercurial和Git作为分布式版本控制系统,同时提供商业计划和免费账户)很容易挖掘到漏洞,经过4个多月的反复努力,我完全靠运气找到了一个解决方案。终端返回的响应太大,导致Burp Repeater稍微滞后,所以我决定将我的方法从POST切换到HEAD来缩短它。这实际上是要求服务器返回响应标头,但省略了响应正文:
果然,这导致后端只服务响应标头,包括未传播正文的Content-Length标头!这使得前端过度读取并提供对第二个走私请求的部分响应:
因此,如果你怀疑存在盲请求隧道漏洞,请尝试 HEAD,看看会发生什么。由于套接字读取的时间敏感特性,它可能需要几次尝试,并且你会发现读取快速提供的走私响应更容易。这意味着走私无效请求更适合检测:
走私无效请求也会让后端关闭连接,避免意外响应队列攻击的可能性。请注意,如果目标仅易受隧道攻击,则响应队列攻击是不可能的,因此你无需担心。有时,当 HEAD 失败时,其他方法(如 OPTIONS、POST 或 GET)会起作用。我已经将这种技术添加到HTTP Request Smuggler作为检测方法。
隧道利用:猜测内部标头
请求隧道使你可以使用前端完全未处理的请求到达后端,最明显的利用路径是利用它绕过诸如路径限制之类的前端安全规则。但是,你经常会发现没有任何相关规则可以绕过。幸运的是,还有第二种选择。
前端服务器通常会注入用于关键功能的内部标头,例如指定用户登录的身份。由于前端检测并重写它们,直接利用这些标头的尝试通常会失败。你可以使用请求隧道绕过此重写并成功走私内部标头。
不过有一个问题,攻击者通常看不到内部标头,并且很难利用你不知道名称的标头。为了帮助解决这个问题,我刚刚发布了一个对Param Miner的更新,它增加了通过请求隧道猜测内部标头名称的支持。只要服务器的内部标头在Param Miner的wordlist中,并且在服务器的响应中引起可见的差异,Param Miner就应该检测到它。
隧道利用:泄漏内部标头
不存在于 Param Miner 的静态词表中或在网站流量中泄露的自定义内部标头可能会逃避检测。常规请求走私可用于使服务器将其内部标头泄露给攻击者,但这种方法不适用于请求隧道。
幸运的是,如果你可以通过 HTTP/2 在标头中注入换行符,那么还有另一种发现内部标头的方法。经典的不同步攻击依赖于让两个服务器对请求正文的结束位置产生分歧,但是使用换行符,我们可以让服务器对正文的开始位置产生分歧!
为了获得bitbucket使用的内部标头,我发出了以下请求:
在降级之后,它看起来是这样的:
你能看到我做了什么吗?前端和后端都认为我发送了一个请求,但他们对正文从哪里开始感到困惑。前端认为's=cow'是标头文件的一部分,所以在此之后插入内部标头。这意味着后端最终将内部标头视为我发送到 Wordpress 搜索功能的 's' POST 参数的一部分,并将它们反射回来:
在 bitbucket.org 上访问不同的路径会导致我的请求被路由到不同的后端,并泄漏不同的标头:
由于我们只触发来自后端的单个响应,因此即使请求隧道漏洞是随机的,该技术也能正常工作。
隧道利用:缓存攻击
最后,如果星星是对齐的,你也许可以使用隧道来进行更强大的各种 Web 缓存攻击。此时你需要一个场景,通过 H2.X desync 获得请求隧道,HEAD 技术有效,并且存在缓存。这将允许你使用 HEAD 通过混合和匹配任意标头和正文创建的有害响应来攻击缓存。
经过一番挖掘,我发现获取 /wp-admin 会触发一个重定向,该重定向反映了 Location 标头内的用户输入,而不对其进行编码。就其本身而言,这是完全无害的 - Location 标头不需要 HTML 编码。然而,通过将它与来自 /blog/404 的响应标头配对,我可以欺骗浏览器来呈现它,并执行任意的JavaScript:
通过使用这种技术,我在一个明显无用的漏洞上挖掘了6个月后,获得了对bitbucket.org上每个页面的持久控制权。
本文翻译自:https://portswigger.net/research/http2如若转载,请注明原文地址