HTTP请求走私作为一种新兴漏洞,利用面相对来说还是比较单一,仅仅是针对具有反向代理的站点,因此作者选择将请求走私的战场转移到用户的Web浏览器中,将请求走私转移到了单服务网站和内部网络中。
利用跨域请求和服务器存在的缺陷搭配,其效果包括:危害到浏览器连接池、安装后门、蠕虫,与此同时也发掘出了包括客户端,服务器端甚至MITM攻击,同时作者也给出了挖掘到的几个相关漏洞。
本篇多数内容建立在:
– https://portswigger.net/research/http-desync-attacks-request-smuggling-reborn
– https://portswigger.net/research/http2
若有觉得本篇部分内容有过于简陋之处,也许在上述两篇中有详细讲到。
本文大致从以下几点讲述:
– 因HTTP处理异常导致的一系列漏洞,其中还有一处 amazon.com 和 AWS 应用程序负载均衡器中的严重缺陷。
– 客户端异步,即摘要中提到的危害浏览器连接池,主要受害者是CDN、Web VPN。
– 基于延时的异步,可用于服务器端和客户端。
– 结论
HTTP handling anomalies
本节讲述通过4个单独的漏洞导致浏览器驱动的异步攻击,其中部分内容在作者以往的议题中有提到过。
Connection state attacks
围绕一个关键点那就是HTTP连接并不是单一的、独立的实体,而是可以被复用的。在抓包软件没有出现之前几乎都是通过浏览器访问服务器,因此服务器假设每个发送给TLS连接HTTP/1.1请求必须有相同的目标和host头,此时毫无意外都会正常工作。
作者发现了两种类型的缺陷。
顾名思义,服务器仅针对首次接受到的请求做验证,假设如下两次单独分开的请求:
理所当然第二个host不在白名单中,而利用burp使HTTP连接复用发送两个请求时会发现成功绕过了白名单,如下:
也就是说所谓的白名单只不过是针对连接中发送的第一个请求,而随后的请求中可以无视名单限制,当然这一缺陷并不存在于大部分机器中,属于是比较少见的存在。
当服务器决定将第一个请求的host当做标识,随后所有来自同一客户端连接的请求路由都自动转发到同一host,那么这时候就会造成一些例如缓存投毒,密码重置投毒等的攻击。
正常请求下期望服务器解析我们的evil host并拼接到重置密码连接中一并发送给用户,但显然服务器不会接受。
通过首次请求路由走私的方式来让我们的请求成功达到后端服务器中:
Click here to reset your password: https://psres.net/reset?k=secret
The surprise factor
大多数请求走私都是通过修改请求长度,使得前后端服务器对于请求处理的歧义所导致的。
在偶然中作者发现了一处漏洞,其由HTTP/2请求触发,这是由于HTTP/2在帧层中有一个内置的长度字段:
AWS有一个负载均衡器名为ALB,作者发现使用其作为前端的各种网站都会间歇性的400错误,进一步测试发现其添加了Transfer-Encoding: chunked请求头,并且会将HTTP请求降级为HTTP/1.1后转发到后端,但请求内容并没有被修改,即:
这是一个发现请求走私漏洞的完美示例,但只有一点不寻常那就是该请求由于HTTP/2导致不存在CL头。然而,大部分情况下浏览器都会主动发送CL头。
Detecting connection-locked CL.TE
作者通过前面的问题及其解决办法想到了解决一个比较棘手的问题,作者在研究HTTP/2时产生的一个问题——请求走私在connection-locked的HTTP/1.1中的通用检测方式。
指前端为与客户端建立的每个连接创建到后端的新连接的常见行为,这使得直接的跨用户攻击几乎是不可能的,但仍然留下了其他攻击途径。
要识别这一走私漏洞,需要在单次连接中同时发送”攻击者“和”受害者“两次请求,但这很容易产生误报,不利于检测,因为服务器无法将其与HTTP pipelining区分开来。
举个例子,给定一个CL.TE估计的请求包和响应包,如何判断目标是否遭到走私攻击?
同样的在Burp Repeater中也能够见到HTTP pipelining,并且很容易被误会成是请求走私:
作者在试图解决这一问题上花费了大量的时间,最后决定明确解释为何上面的现象无法证实存在漏洞,在最后也找到了相应的解决方案:
在上面的响应包中,通过夹带的404请求带来的响应,可以判断出后端服务器正在使用TE头来解析请求,但是无法判断代理服务器是否使用CL来解析,因此也就无法判断出到底是否存在漏洞。
要证明漏洞的存在首先需要将HTTP pipelining这一特性排除,只需要在请求包中带有0rnrn
如下:
因为前面提到了后端服务器使用TE头来解析,那么此时会分为两种情况:
第一种是服务器给到响应,这说明代理服务器认为本次请求已经完成,则其解析的是TE头:
第二种是持续等待响应直到超时,这说明代理服务器一直在等待请求,则其解析的是CL头:
作者利用这种技术也发现了运行在Barracuda WAF后的ISS服务存在走私,尽管针对漏洞的修复已经更新,但目标并没有安装这一更新。
CL.0 browser-compatible desync
一开始作者觉得某个站点上存在TE.CL走私,但当他将TE头去除后发现这对于前后端的服务器而已没有任何影响,这样就是意味着TE头在当前请求中可以完全去除,但走私攻击依旧生效,其效果类似于:
显然代理服务器接受并解析CL,而后端服务器完全忽视CL,因此,绿色字体的请求完全被忽略掉,后端是将请求正文(从xyz开始)当做第二个请求的开始。忽略CL等效与将其值视为0,因此这是一个CL.0的异步,一个已知但较少被研究到的攻击面。
TE.CL and CL.TE // 传统的请求走私
H2.CL and H2.TE// HTTP/2 降级走私
CL.0 // 上文提到的
H2.0 // 类似CL.0
0.CL and 0.TE // 无法脱离pipelining利用
关于CL.0这一利用方式,它是一种完全符合HTTP规范的请求,这也意味着代理服务器无法防御它,进一步来说甚至可以被服务器触发。
H2.0 on amazon.com
在对CL.0/H2.0的异步进行粗略的扫描后,发现受影响的站点不少,其中包括amazon,在向其发送/b/时会忽略掉CL:
作者利用一个简单的poc来确认这一漏洞,随机的将在线用户的完整请求存储在购物清单中(包括auth token):
随后作者便报告了这一漏洞,但却意识到这一漏洞还可以进一步拓展。可以用amazon上的HEAD技术创建XSS页面,当受害者浏览该页面时执行javascript,同时让受害者重启攻击,将攻击进一步传播。即一种请求异步类型的蠕虫。
传统的异步攻击只是在前端和后端服务器之间,因此若网站脱离了前后端服务器架构的话是无法实现此类攻击的,作者将这类攻击称为服务器端异步。而大多数的服务器端异步只能通过HTTP客户端发出的畸形请求触发,但正如刚刚的awazon,有时候也可以创建一个由浏览器驱动的服务器端走私。
浏览器端造成的走私作者成为客户端异步(CSD),由于异步攻击发生在浏览器和前端服务器之间。这使得单服务器网站也可以被利用,因为它们在HTTP解析方面通常较差。
CSD攻击从受害者访问攻击者的网站开始,然后让他们的浏览器向易受攻击的网站发送两个跨域请求。第一个请求是为了让浏览器的连接产生异步,并使第二个请求触发有害响应,通常使攻击者能够控制受害者的帐户:
Methodology
在尝试检测和利用客户端异步漏洞时,可以重用服务器异步攻击中的许多概念。主要区别在于整个漏洞利用过程发生在受害者的Web浏览器中,该环境比专用的黑客工具更复杂且不受控制。这带来了一些新的挑战,在研究这种技术时给作者带来了很多痛苦。为了节省您的时间,作者吸取了经验教训并开发了以下方法:
Detect
首先是确认CSD的载体,这是漏洞的核心。在HTTP Request Smuggler和Burp Scanner中依旧实现了自动检测,但了解如何手动检测仍然很有意义。
首先,服务器必须忽略CL,通常是由于错误的请求触发服务器异常或者是服务器根本不期望接受POST类型的请求。选取的目标通常是静态文件或者服务器重定向,然后利用超长URL或者特殊格式的URL(如/%2e%2e
)触发错误。
其次,请求必须在Web浏览器跨域中触发。浏览器对于跨域请求的控制十分严格,因此只能控制headers,然后如果需要发送请求体还需要使用POST请求。最终只能控制URL、Referer头、请求体以及部分 Content-Type。
现在完成了攻击请求,需要检查服务器是否忽略CL,第一步,使用超过请求体长度的CL,查看服务器是否回复:
这看起来可行,但一些较为安全的服务器会在没有等待请求体的情况下做出响应而导致误报的产生,而另外一些服务器尽管不会处理CL,但会在响应后即刻关闭连接,因此需要用某些方式筛出这类请求。通过在同一个连接上发现两个请求,并查看第一个请求的请求体对第二个响应造成的影响:
作者在Citrix Web VPN上浪费了大量的时间,后来意识到它只是为每个请求都分出两个http响应。
然后还需要注意一点的是,目标服务器是否支持HTTP/2,CSD是利用HTTP/1.1连接复用,并且浏览器通常在站点有HTTP/2时选择HTTP/2,当然会有例外,即某些代理服务器不支持HTTP/2。
Confirm
现在将操作移步到浏览器中执行(推荐选择chrome)。
首先,选择要从中发起攻击的站点。此站点必须通过 HTTPS 访问,并且位于与目标不同的域上。
接下来,确保未配置代理,然后浏览到攻击站点。打开开发人员工具并切换到“network”选项卡。为了方便以后调试潜在问题,我建议进行以下调整:
切换到开发人员控制台并执行 JavaScript 使用 fetch()。像这样:
fetch('https://example.com/', {
method: 'POST',
body: "GET /hopefully404 HTTP/1.1rnX: Y", // malicious prefix
mode: 'no-cors', // ensure connection ID is visible
credentials: 'include' // poison 'with-cookies' pool
}).then(() => {
location = 'https://example.com/' // use the poisoned connection
})
fetch的mode设置为no-cors可以确保chrome在network中显示Connetion ID,同时还设置了 credentials: ‘include’。因为chrome有两个单独的连接池,一个带cookie另一个不带,最好养成投毒’with-cookies’这一连接池的习惯。
执行此命令时,应该会在network选项卡中看到两个具有相同连接 ID 的请求,第二个请求应触发 404:
若你的请求类似于上图,则你成功的在你的浏览器上发现了客户端异步。
Explore
现在我们已经确认了客户端异步,下一步是找到一个可以用来利用它的小工具。在“网络”选项卡中触发意外的 404 可能会给某些人留下深刻的印象,但不太可能产生任何用户密码或赏金。
此时,我们已经确定我们可以毒害受害者浏览器的连接池,并将任意前缀应用于我们选择的HTTP请求,它提供了三种广泛的攻击途径。
Store
一种选择是根据目标站点上可存储文本数据的功能点来将受害者的cookie、auth或者密码存储在攻击者可以查询到的地方,与服务端的请求走私几乎相同。
Chain&pivot
在正常情况下,许多类别的服务器端攻击只能由直接访问目标网站的攻击者发起,因为它们依赖于浏览器拒绝发送的HTTP请求。这几乎包括所有涉及篡改HTTP标头的攻击 – Web缓存中毒,大多数服务器端请求走私,主机标头攻击,基于用户代理的SQLi以及许多其他攻击。
例如,无法在用户代理标头中使用 log4shell 有效负载使其他人的浏览器发出以下请求:
CSD 漏洞为这些攻击打开了一个网关,这些攻击由于位于受信任的 Intranet 上或隐藏在基于 IP 的限制后面而受到保护。例如,如果 intranet.example.com 容易受到 CSD 的攻击,则可以使用以下请求实现相同的效果,该请求可以在具有 fetch() 的浏览器中触发:
难以想象会有多少IoT设备易受CSD攻击,不过浏览器正致力于缓解对内网的攻击,这是一个好的现象。
您还可以利用会话Cookie等环境权限,在CSRF式攻击中攻击身份验证后攻击表面,这通常是不可能的,因为是不可伪造的header,例如JSON Content-Type。总体而言,CSD漏洞非常适合同时链接客户端和服务器端缺陷,并且可以在适当的情况下实现多步骤透视。
Attack
最后一种选择是使用恶意前缀从服务器引发有害响应,通常目的是在易受攻击的网站上执行任意JavaScript,并劫持用户的会话或密码。
我发现,成功攻击的最简单途径来自通常用于服务器端异步攻击的两种关键技术:通过主机头重定向进行 JavaScript 资源中毒,以及使用 HEAD 方法将响应与有害 HTML 拼接在一起。这两种技术都需要进行调整,以克服与在受害者的浏览器中操作相关的一些新挑战。下面将使用一些案例研究来探索这些障碍并展示如何处理它们。
Akamai – stacked HEAD
第一个案例选择采用基于Akamai构建的网站的漏洞,这里作者选择www.capitalone.ca作为示例站点。
当 Akamai 发出重定向时,它会忽略请求的CL,并将任何消息正文保留在 TCP/TLS 套接字上。capitalone.ca 使用 Akamai 将 /assets 请求重定向到 /assets/,因此我们可以通过向该终端节点发出 POST 请求来触发 CSD:
fetch('https://www.capitalone.ca/assets', {method: 'POST', body: "GET /robots.txt HTTP/1.1rnX: Y", mode: 'no-cors', credentials: 'include'} )
为了构建漏洞利用,我们将使用 HEAD 方法将一组 HTTP headers与Content-Type是text/html 和由标头组成的“正文”组合在一起,而此时参数会传递到Location上:
若是服务器端的异步攻击,那么利用到此为止,但对于CSD而言还需要解决两个问题。
第一个问题是初始的重定向响应。要使注入的JavaScript执行,我们需要受害者的浏览器将响应呈现为HTML,但301重定向将自动跟随浏览器,从而破坏攻击。一个简单的解决方案是指定模式:“cors”,这会故意触发 CORS 错误。这可以防止浏览器遵循重定向,并使我们能够通过调用catch()而不是 then()来恢复攻击序列。在 catch 块内,我们将使用location = ‘https://www.capitalone.ca/‘ 触发浏览器导航。使用 iframe 尽管很值得尝试,但这会使我们面临跨站点攻击缓解措施,如same-site cookies。
第二个复杂因素是所谓的“stacked-response problem”。浏览器有一种机制,如果它们收到的响应数据比预期的多,它们将会丢弃连接。这极大地影响了将多个响应排队的技术的可靠性,例如我们在这里使用的HEAD方法。为了解决这个问题,我们需要延迟对HEAD请求的404响应。幸运的是,在这个目标上,我们可以通过添加一个具有随机值的参数来充当缓存破坏者,触发缓存未命中并造成~500ms的延迟来轻松实现这一目标。最后的exp:
Cisco Web VPN – client-side cache poisoning
下一个目标是思科ASA WebVPN,它的所有端点都忽略CL头,所以我们可以简单的通过POST请求向主页发出异步。此处利用Host-header重定向:
最简单的攻击是使用此重定向毒害socket,将受害者导航到/+CSCOE+/logon.html并希望浏览器尝试导入/+CSCOE+/win.js使用中毒的套接字,随后重定向,并最终从我们的网站导入恶意JS。不幸的是,这是非常不可靠的,因为浏览器可能会使用中毒的套接字进行初始导航。为避免此问题,我们将执行客户端缓存中毒攻击。
首先,我们用重定向毒害socket,然后直接将浏览器导航到 /+CSCOE+/win.js:
请注意,此顶级导航对于绕过缓存分区至关重要 – 尝试使用 fetch() 会毒害错误的缓存。
浏览器将使用中毒的套接字,接收恶意重定向,并将其保存在本地缓存中,用于 https:/已编辑/+CSCOE+/win.js。然后,它将按照重定向并返回我们的网站 https://psres.net/+webvpn+/index.html。我们会将浏览器重定向到登录页面,https://redacted/+CSCOE+/logon.html
当浏览器开始呈现登录页面时,它将尝试导入 /+CSCOE+/win.js并发现它已经将其保存在其缓存中。资源加载将遵循缓存的重定向,并向 https://psres.net/+webvpn+/index.html 发出第二个请求。此时,我们的服务器可以使用一些恶意的JavaScript进行响应,这些恶意的JavaScript将在目标站点的上下文中执行。
要使此攻击起作用,攻击者的网站需要在同一端点上同时提供重定向和恶意JS。作者采取了一种懒惰的方法,并用JS / HTML多语言解决了这个问题 – Chrome似乎并不介意不正确的内容类型:
Verisign – fragmented chunk
在寻找异步时,有时候最好不去探测有效的端点,寻找服务器上不寻常的代码路径。如尝试使用不寻常的路径(/..%2f
),我发现通过POST发出/%2f
可以触发verisign.com上的CSD。
最开始作者尝试使用类似在Akamai上用过的HEAD方法,但改方法依赖于CL响应,同时服务器向没有正文的所有请求发送分块响应。此外还拒绝接受包含CL的HEAD请求。最终结果测试发现服务器会为HEAD请求发出一个基于CL的响应,但前提是需要使用Transfer-Encoding: chunked。
这类操作的服务端异步中是无效的,但由于受害者浏览器在攻击者可控下,可以准确预测到下一个请求的大小。
Pulse Secure VPN
最后一个演示的漏洞将针对Pulse Secure VPN,它忽略了对静态文件的POST请求的CL,如robots.txt,同时类似Cisco Web VPN一般,该目标有对于host头重定向,可以用于劫持插入javascript。但该重定向不可缓存,因此客户端缓存投毒并不是一个合适的选择。
由于我们针对的是资源负载,并且没有毒害客户端缓存的想法,因此攻击的时机至关重要。我们需要受害者的浏览器在目标站点上成功加载页面,随后使用投毒的连接来加载 JavaScript 子资源。
固有的竞争条件使这种攻击不可靠,所以如果我们只有一次尝试,它注定要失败 – 我们需要设计一个我们可以进行多次尝试的环境。为了实现这一点,我将创建一个单独的窗口,并在攻击者页面上保留一个句柄。
在大多数目标页面上,劫持JS导入的失败尝试将导致浏览器缓存真正的JavaScript文件,使该页面免受此类攻击,直到缓存的JS过期。我能够通过定位 /dana-na/meeting/meeting_testjs.cgi 来避免这个问题,该目标从/dana-na/meeting/url_meeting/appletRedirect.js – 它实际上并不存在,因此它返回 404 并且不会保存在浏览器的缓存中。我还用一个冗长的标头填充了注入的请求,以缓解堆叠响应问题。
我们之前看到,在HTTP请求中间暂停并观察服务器的反应可以揭示通过篡改请求的实际内容无法获得的有用信息。事实证明,暂停还可能通过触发误导性的请求超时来实现新的异步漏洞。
除非检测工具的超时时间高于目标服务器,否则此漏洞类是不可见的。作者非常幸运地发现了它,因为他的工具应该有2秒的超时,但由于一个错误,它变成了10秒的超时。同时还碰巧遇到一个运行Varnish的站点,该站点配置了自定义的5秒超时。
Varnish
Varnish 缓存具有一个名为 synth() 的功能,它允许您发出响应而无需将请求转发到后端。下面是用于阻止对文件夹的访问的示例规则:
在进行规则匹配部分请求时,如果 Varnish 在 15 秒内未收到任何数据,它将超时。发生这种情况时,即使它只从socket中读取了一半的请求,它也会使连接保持打开状态以供重用。这意味着,如果客户端跟进 HTTP 请求的后半部分,它将被解释为一个新请求。
要在易受攻击的前端上触发基于暂停的异步,首先发送headers,body,然后等待。最终,您将收到响应,当您最终发送请求正文时,它将被解释为新请求:
apache
在这次发现之后,作者遇到了Turbo Intruder的请求超时,并发现同样的技术也适用于Apache。就像 Varnish 一样,它在服务器自己生成响应而不是让应用程序处理请求的端点上容易受到攻击。发生这种情况的一种方式是服务器级重定向:
如果您发现服务器容易受到基于暂停的异步的影响,则有两种利用方式,具体取决于它是前端还是后端。
Server-side
如果易受攻击的服务器在后端运行,则可以触发服务器端异步。为此,您需要一个将请求流式传输到后端的前端服务器。特别是,它需要沿 HTTP 标头转发,而不缓冲整个请求正文。生成的漏洞利用流如下所示:
这里有一个问题就是前端服务器不会读取超时的响应然后发送给我们,我们只会看到一个完整的响应,因此需要先发送headers,延时,然后在没有任何提示的情况下继续发送其余部分,由于作者没找到具备这种部分延迟的安全测试工具,因此他对Turbo Intruder实现了支持。
-
pauseBefore
-
pauseMarker
-
pauseTime
那么,哪些前端实际上具有这种请求流行为呢?一个众所周知的前端是亚马逊的应用程序负载均衡器(ALB),但还有一个额外的障碍。如果 ALB 收到对部分请求的响应,它将拒绝重用该连接。
幸运的是,此机制中存在固有的竞争条件。您可以通过延迟请求的后半部分来利用 ALB 背后的 Varnish,使其在后端超时的同时到达前端。
Matching timeouts
在利用ALB后面的Apache时,还有一个额外的复杂内容 – 两个服务器的默认超时时间为60秒。这留下了一个极小的时间窗口来发送请求的第二部分。
作者试图通过发送一些被前端服务器规范化的数据来解决这个问题,以便在不影响后端计时器的情况下重置前端的计时器。不幸的是,chunk size padding, chunk extensions或 TCP duplicate/out-of-order packets都没有达到此目标。
最后,为了证明这个概念,作者依靠纯粹的运气,使用Turbo Intruder发起了缓慢但持续的攻击。在66小时后最终成功。
MITM-powered
由于基于暂停的不同步攻击使用合法的 HTTP 请求,因此很自然地想知道它们是否可用于触发CSD。作者探索了让浏览器在发出请求时中途暂停的选项,尽管 Streaming Fetch 听起来很有前途,但它尚未实现,最终没有成功。
但是,有一种方法绝对可以延迟浏览器请求 – 主动 MITM 攻击。TLS 旨在防止数据在传输中被解密或修改,但它通过 TCP 捆绑在一起,没有什么可以阻止攻击者延迟整个数据包。这可以称为盲MITM攻击,因为它不依赖于解密任何流量。
攻击流与常规客户端不同步攻击非常相似。用户访问攻击者控制的页面,该页面向目标应用程序发出一系列跨域请求。第一个 HTTP 请求被故意填充得太大,以至于操作系统将其拆分为多个 TCP 数据包,从而使活动的 MITM 能够延迟最终数据包,从而触发基于暂停的异步。由于填充,攻击者可以简单地根据大小来识别要暂停的数据包。
使用默认配置和单个重定向规则成功地对基于 Apache 的独立网站执行此攻击:
从客户端来看,它看起来像使用 HEAD 的常规客户端异步,除了请求填充:
在执行盲MITM的攻击者系统上,使用tc-NetEm实现了延迟:
本文涵盖的主题和技术具有进一步研究的巨大潜力。一些印象深刻的好东西是:
这份清单很可能也有一些重大遗漏。
作者原文:
https://portswigger.net/research/browser-powered-desync-attacks
原文始发于微信公众号(山石网科安全技术研究院):一种全新的通过浏览器驱动的异步攻击利用方式