翻译:WisFree
预估稿费:190RMB
投稿方式:发送邮件至linwei#360.cn,或登陆网页版在线投稿
前言
首先,我假设各位同学已经知道什么是XSSI了。如果你们不知道的话,可以先看看下面这段引自《基于标识的XXSI攻击》的简单介绍:
跨站脚本包含(XSSI- Cross Site Script Inclusion)是一种能够允许攻击者绕过原始边界窃取特定类型数据的攻击技术。它利用了这样一个事实,即浏览器不会阻止网页加载图像和文字等资源,这些资源通常托管在其他域和服务器。比如说,攻击者可以在恶意Web页面中利用SCRIPT标签来完成攻击:
<!-- attacker's page loads external data with SCRIPT tag -->
<SCRIPT src="http://target.example.jp/secret"></SCRIPT>
技术分析
由于浏览器不会阻止一个域名中的页面直接引用其他域名的资源,所以我们可以在script标签中引入第三方域名的资源,然后观察其运行情况,但我们现在还无法读取到来自第三方域名script标签中的内容。
需要注意的是,包含script标签的不一定必须是JS文件,文件开头也无需标注text/javascript,而且文件的扩展名也并非一定要是“.js”。
我第一次报告给HackerOne的安全问题从理论上来说是存在的,而相应的攻击技术(CSV with quotations theft)在《基于标识的XXSI攻击》一文中也进行了介绍和描述。整个攻击的核心思想是在JavaScript语句中嵌入CSV文件中的内容,我所报告的漏洞节点地址如下:
https://hackerone.com/settings/bounties.csv
这是HackerOne新增的一项功能,你可以访问Settings > Payments as “Download as CSV”找到这个功能。点击这个链接之后,浏览器会发送一个简单的GET请求并弹出一个下载对话框。CSV文件的内容如下:
report_id,report_title,program_name,total_amount,amount,bonus_amount,currency,awarded_at,status
1234,Sample report,Sample Program,100.0,100.0,0.0,USD,2017-01-01 12:30:00 UTC,confirmed
1234,Sample report,Sample Program,100.0,100.0,0.0,USD,2017-01-01 12:30:00 UTC,confirmed
1234,Sample report,Sample Program,100.0,100.0,0.0,USD,2017-01-01 12:30:00 UTC,confirmed
由于我可以控制其中的report_title,所以我立刻想到了利用XSSI来尝试泄漏该文件的内容。CSV文件的第一行内容是一堆由逗号分隔开的值,而它们都是有效的JavaScript变量名。因此,我现在只需要在我的页面中定义这些变量名,之后再导入进去即可。
我设计的PoC如下:
<!DOCTYPE html>
<html>
<head>
<meta charset='utf-8'/>
<script>
var report_id,report_title,program_name,total_amount,amount,bonus_amount,currency,awarded_at,status;
</script>
</head>
<body>
<script src='https://hackerone.com/settings/bounties.csv'></script>
</body>
</html>
我所做的是纯理论的实现。我使用Burp Suite修改了report_title,然后把它们写成了有效的JavaScript代码,最后用反引号(` `)获取到其中的多行数据。《基于标识的XXSI攻击》的作者已经讨论过如何读取多行字符串数据了,而且还给出了详细的示例。
当我们修改信息并构建有效的JavaScript语句时,我们可能看到的响应内容如下:
其中,Sample这个JavaScript变量中包含有CSV文件中所有report内容(除了最后一个)。
大约在一个小时之后,我又发现了另一个XSSI攻击点,而这一次我设计出的PoC不需要在数据的传输过程中进行修改。包含漏洞的节点地址如下:
https://hackerone.com/reports/12345/export/raw?include_internal_activities=true
没错,这并不是一个CSV文件,但我们仍然有可能将其变成一个有效的JavaScript文件。这是“导出”功能的一个部分,它允许我们查看或下载原始报告内容。点击之后,浏览器便会发送上图所示的GET请求。这是一个XHR请求,并带有一个反CSRF令牌。
我们可以在浏览器中看到GET请求所对应的完整响应信息:
为了跨域泄漏报告(Report)的内容,所有的语句必须是有效的JavaScript语句。所以,我提交了一份报告demo:
第一行是一条标记语句(“Title”后面跟着的是用户提供的标题),标记语句是一种有效的JavaScript语句,后面可以跟我自己的输入参数。为了获取到多行字符串数据,我这里还要用到反引号(` `)。接下来,我会在结尾的反引号中添加一条注释来作为字符串结束的标志。
演示视频
现在,我可以在我的script标签中嵌入上面给出的URL地址,然后远程提取出我所需要的数据了。下面是一个PoC演示视频:
重要部分截图如下:
我目前只知道两种控制JavaScript多行字符串的方法(串联和反引号转义),ECMAScript 6也引入了一种箭头函数(Arrow_Functions),它允许开发人员使用简短的字符来定义函数。下面是一个简单的例子:
除此之外,模版字符串(Template Literals)则是一种更简单的多行字符串处理方式,我在我的PoC中也使用了这项技术。
如果你今天是第一次阅读有关XSSI的文章,那么你可能无法那么快地意识到它所能带来的影响。根据数据内容的不同,XSSI的利用方法也不一样。简单来说,XSSI将允许一名攻击者读取到其他用户所提交的报告内容。比如说,Facebook可以在一个JavaScript文件(user.js)中保存用户的数据,如果它能够响应正常的GET请求,那么当你访问攻击者精心制作的恶意网站/页面时,攻击者将有可能读取到你的个人信息。如果你想了解更多现实生活中的XSSI攻击示例,请参考这里。【传送门1】【传送门2】
缓解方案
开发者可以部署多种措施来抵御XSSI攻击。其中一种方法是向用户提供不可预测的授权令牌,在服务器响应任何请求之前,需要发送回该令牌作为额外的 HTTP参数。脚本应该只能响应POST请求,这可以防止授权令牌作为GET请求中的URL参数被暴露。同时,这种方法还可以防止脚本通过脚本标签被加载。浏览器可能会重新发出GET请求,而这很有可能会导致一个操作会执行一次以上,而重新发出的POST请求则需要用户的同意。
在处理JSON请求时,我们可以在响应中增加非可执行前缀(例如“n”)以确保脚本不可执行。在相同域名运行的脚本可以读取响应内容以及删除前缀,但在其他域名运行的脚本则不能。此外,开发者还应该避免使用JSONP(具有填充功能的JSON)来从不同域名加载机密数据,因为这会允许钓鱼网站收集数据。同时,发送响应表头“X-Content-Type-Options: nosniff”也将帮助保护IE和谷歌Chrome用户免受XSSI攻击。
总的来说,开发人员或网站拥有者可以通过以下几种方式来避免这个问题的出现:
1.使用POST请求;
2.使用秘密令牌(CSRF保护);
3.让URL地址无法预测;
4.限制资源引用;
如果数据是通过ajax请求来获取的话,我们可以:
1.使用类似for(; ;)这样的Parser-Breaking语句;
2.使用自定义HTTP头;