声明(阅读前必读!!!):
转载自这篇文章
本人是一个英语渣,英语四级都没过。翻译此文存粹用于英语学习,而且大部分还是用翻译软件翻译的。请谨慎阅读此译文,注意不要被译文雷到。
另外这是一篇2009年的老文章了,里面有部分知识可能已经过时了,不过文章中关于JSON劫持的知识大部分还是十分有用的。在每段原文翻译后面我都会加上自己理解和相关备注,主要是本文开阔了一个思路。
由于文章翻译过来导致有些知识点不好理解,这里可以点击上面的JSON劫持漏洞分析和攻防演练,我会按照自己对JSON劫持的理解对这个漏洞进行讲述。
前阵子我写了篇关于一个微妙的JSON的漏洞(Anatomy of a Subtle JSON Vulnerability)可能导致敏感信息泄露的文章。这种特别的漏洞攻击(exploit)主要通过覆盖JavaScript数组的构造函数,从而劫持暴露的JSON数组数据,现在大多数的浏览器都不支持防御这些漏洞攻击。
PS: exploit的英文本意为“利用”。但是在计算机安全领域中指的是漏洞利用、利用程序或者网站的某个漏洞进行攻击!
这里解释下overriding the JavaScript Array(覆盖javascript数组的构造函数):因为javascript允许我们覆盖或重写其他的方法,包括Array()这种内部的方法,所以恶意攻击者可以很轻松的将js中的Array()方法替换和覆盖成恶意代码,相当于C#、JAVA和C++中的方法重写。原文作者使用的是__defineSetter__方法来实现这个功能的,但是此函数只有部门浏览器支持,所以一般来说还是直接使用JS的方法重写。
一般替换的恶意代码都是进行JSON劫持的代码,将获取到的JSON数据发送给第三方网站服务器上。之所以可以利用这点进行劫持,是因为JSON数组本身就是特殊的Array,网站一旦返回的JSON数组,恶意攻击者就通过重写Array()的构造函数将数据转发他们的服务上,这就是JSON数据劫持。
然而,还有另外一个与exploit相关的漏洞有可能会对更多的浏览器造成影响。最近在和微软的ScottHanselman交流时引起了我对这个问题的注意,并且我在上周的挪威开发者大会上演示了这个漏洞,虽然这个漏洞曾经出现在Twitter(推特)上。
PS:这个与exploit相关漏洞的描述地址:http://hackademix.net/2009/01/13/you-dont-know-what-my-twitter-leaks/
在我深入讨论前,让我首先给你讲解下形成JSON劫持漏洞的条件。
这个JSON劫持漏洞需要你暴露出JSON服务(指JSON服务地址被攻击者知道)并且进行如下操作:
因此,如果你从不发送敏感的JSON格式数据,或者你仅仅发送POST请求以响应获取JSON等等操作,那么你的网站可能不会受到这个特定的漏洞的攻击(虽然有可能是其他人)。
我讨厌使用Visio(Microsoft Office Visio:绘制流程图的软件),但我想我会尽我所能的通过图表来更好的讲解这个过程。在第一个屏幕截图上,我们看到不知情的受害者登陆了这个容易受到攻击的网站,而这个易受攻击的网站向浏览器发送一个身份验证的cookie并且浏览器将它保存起来(浏览器会将这个HTTP的身份认证信息和相关的cookie缓存起来)。
在某些时候,无论是在过去,或者是在将来,总有一些坏人(恶意攻击者)不停的发送着大量的垃圾邮件(这里应该是钓鱼/欺诈邮件),受害者会收到一封内容是推荐一个非常有趣的视频的电子邮件,比如邮件中带有"钢琴上的松鼠"这样内容的一个视频链接。
但是这个链接实际上是指向这些恶意攻击者的网站(就是钓鱼网站),当受害人点击了这个链接,接下来会连续执行两个步骤。首先,受害人的浏览器会向恶意攻击者的网站发送一个GET请求。
这些恶意攻击者的网站(钓鱼网站)会响应并返回一些HTML语句,这些HTML语句包含着带有script标签的JavaScript语句(恶意脚本语句)。当浏览器运行到这些script标签时,它就会执行这段代码并向那些容易受到攻击的网站(这里假设攻击和劫持的网站是http://vulnerable.example.com/Json)发送另一个加载脚本的GET请求(如上图所示,这里会将从网站获取的响应文本放到/替换到script标签中,一般是返回JSON数据),该请求还会将受害人浏览器中的身份验证cookie一同发送过去。
攻击者通过伪装成受害者的浏览器,利用浏览器的授权(就是上面发送过去的身份验证cookie) 发出一个请求,这个请求可以到获取到拥有敏感数据的JSON数组。攻击者通过将请求的JSON数组载入到script中以替换重写成可执行的脚本语句,从而窃取到这些数据(脚本语句会将窃取到的数据发送给攻击者)。
为了能更深入的了解,看看这些演示攻击的实际代码,这样也许对我们理解有所帮助(你可以将代码下载拷贝下来并运行)。另外要注意的是下面的演示无论如何都不是专门针对ASP.NET 或者 ASP.NET MVC,我只是碰巧使用ASP.NET MVC来演示它。假设这个容易受到攻击的网站通过一个Action方法返回敏感的JSON数据。
[Authorize]
public JsonResult AdminBalances() {
var balances = new[] {
new {Id = 1, Balance=3.14},
new {Id = 2, Balance=2.72},
new {Id = 3, Balance=1.62}
};
return Json(balances);
}
假如这是一个HomeController控制器的的方法,你可以通过向“/Home/AdminBalances”这个URL地址发送GET请求,从而获取Action方法返回的JSON数据:
[{"Id":1,"Balance":3.14},{"Id":2,"Balance":2.72},{"Id":3,"Balance":1.62}]
请注意,我还通过Action方法上声明了AuthorizeAttribute(Authorize属性)使得请求这个方法必须通过身份验证,所以一个匿名(指未通过身份验证)的GET请求将无法查看此敏感数据。
事实上,这是一个JSON数组是很重要的。事实证明,包含着一个JSON数组的脚本是一个有效的JavaScript脚本。一个仅包含着JSON对象的脚本不是有一个有效的JavaScript文件。例如,如果您有一个包含以下JSON的JavaScript文件:
{"Id":1, "Balance":3.14}
并且你有一个引用这个脚本文件的script标签:
你将会在HTML页面中得到一个JavaScript错误。然而,通过一个不幸的巧合,如果你有一个script标签,并且这个脚本标签引用文件的内容只有JSON数组,那么这(指的是script标签所引用文件中的JSON数组内容)将被认为是有效的JavaScript和数组被执行。
PS:如果JSON是以对象形式返回,即返回{"Id":1, "Balance":3.14},会提示非法标签错误。因为在JS中"{"和"}"这两个大括号(又称作花括号)在代码中是作为起始和结束的含义。所以为在JS文件中,单独的JSON数据要么以数组形式存在,要么在返回的对象上加小括号({"Id":1, "Balance":3.14}),要么返回JSON数组,否则就是非法标签错误。
现在让我们看看放置在这些攻击者服务器主机上的HTML页面:
<html>
...
<body>
<script type="text/javascript">
Object.prototype.__defineSetter__('Id', function(obj){alert(obj);});
script>
<script src="http://example.com/Home/AdminBalances">script>
body>
html>
这里发生了什么?没错,恶意攻击者改变了对象的原型,正是利用了__defineSetter__这个特殊方法在原型对象调用属性设置方法的时候进行覆盖。
在这个案例中,无论何时所有的对象都会设置一个属性名称为Id的字段(通过__defineSetter__方法),一个通过使用alert函数显示原型属性值的匿名函数将被调用和触发。注意,该脚本(上面使用alert函数显示属性值的脚本)可以很容易地替换成将数据上传给攻击者的脚本,敏感数据就是因此被泄露的。
正如我前面所说的(要实现上面的动作),攻击者需要你访问他的恶意网页,前提你刚刚登陆这个受攻击的页面不久并且页面中的会话仍然保持有效。最常见的钓鱼攻击伎俩就是通过发送一个包含恶意网站链接的邮件(诱骗受害人点击链接)。
如果你运气不好没注意到这个钓鱼链接,当你点击了这个链接时你仍然保持原来网站的登陆状态,那么在进入钓鱼网站后载入script标签的引用时,浏览器将会发送你的身份验证cookie至原来的网站。直到与原来网站链接上为止,你会发送一个包含有效身份验证的请求从而通过响应取得JSON数据,这些数据会获取到你的浏览器中。这可能听起来很熟悉,因为它真的是一个变种的跨站请求伪造(CSRF)攻击,之前我有写过相关的文章。
PS:这里原来的网站指的是受攻击的站点,载入的script标签指的是这段代码: