隐藏在URL凭证中的攻击载荷 | PortSwigger研究
去年,Johan Carlsson发现可以在URL的凭证部分隐藏攻击载荷。这一点令我特别着迷,因为攻击载荷在Chrome和Firefox的URL中实际上是不可见的。这种特性甚至在同源导航中仍然保持。于是我像咬住骨头的狗一样不肯放弃,试图探索可能的应用...
第一个让我惊讶的是,document.URL并不总是与location匹配:
https://foo:bar@portswigger-labs.net
alert(location);//
https://portswigger-labs.net/
alert(document.URL);//
https://foo:bar@portswigger-labs.net/
我原本以为这两个属性是相同的,因为我从未观察到它们有差异,但事实证明document.URL包含URL的凭证部分,而location则不包含。这意味着你可以在事件中仅使用URL来获取凭证中的攻击载荷:
https://alert(1)@portswigger-labs.net
<img src onerror=alert(URL.slice(8,16))>
在通过模糊测试识别URL凭证部分中哪些字符会被编码后,Shazzer发现Firefox不会对单引号进行URL编码。这在DOM XSS场景中特别有用,如果网站删除了查询字符串和哈希值,就能使如下漏洞在Firefox中可利用:
function getBase(url) {
return url.split(/[?#]/)[0];
}
document.write(`<script>const url='${getBase(document.URL)}';<\/script>`);
要利用此漏洞,你需要在Firefox的凭证部分提供攻击载荷,如下所示:
https://'-alert(1)-'@example.com
这可以通过重定向或用户导航来实现。你甚至可以使用此技术来控制锚链接的用户名或密码属性。这是因为每个锚元素都具有这些属性,它们存储来自URL的凭证。如果是相对链接,它会继承父级凭证,允许你覆盖这些值:
https://clobbered@example.com
<a href=# onclick=alert(username)>test</a>
你可以将此技术与DOM Clobbering结合使用,从而控制具有用户名或密码属性的对象。请注意,你甚至可以提供空的href,仍然能够通过URL控制用户名或密码:
https://user:pass@example.com
<a href id=x>test</a>
<script>
eval(x.username)//user
eval(x.password)//pass
</script>
总之,发现location和document.URL之间的差异,以及document.URL如何保留URL的凭证部分——即使像Chrome和Firefox这样的浏览器在地址栏中隐藏了它——是相当令人惊讶的。Firefox对某些字符(如单引号)的处理方式(不进行URL编码)也可能对DOM XSS有用。
通过凭证隐藏攻击载荷、操作锚元素内的用户名和密码属性,并可能将此与DOM Clobbering结合使用的能力,可以用于更高级的利用。
注意:Safari会丢弃URL凭证。所有显示的示例仅在Chrome和Firefox上有效。此外,Chrome会阻止子资源使用URL凭证。