-
-
[翻译]现代Web中的JSON劫持
-
发表于: 2017-7-7 11:36 4658
-
现代Web中的JSON劫持
Benjamin Dumke-von der Ehe发现了一个有趣的跨域窃取数据的方法。他使用JS proxies(JS代理)创建了一个可以窃取未定义的JavaScript变量的handler(处理器)。这个问题似乎在Firefox中被修复了,但是我发现了一个新的攻击Edge的方法。虽然Edge似乎不允许对window.__proto__赋值, 但他们没有考虑Object.setPrototypeOf。使用这种方法,我们可以使用代理化的__proto__来覆盖__proto__的属性。像这样:
如果你的脚本中有包含stealme的跨域脚本,那么即便一个变量未被定义,你也会看到该变量的值弹出。
经过进一步的测试,我发现覆盖__proto __.__ proto__(Edge上的[object EventTargetPrototype])可以实现同样的效果。
好,所以我们可以窃取跨域数据,但除此之外我们还能做什么?所有主流浏览器都支持脚本中的字符集(charset)属性,我发现UTF-16BE字符集尤为有趣。 UTF-16BE是一个多字节字符集,两个字节构成一个字符。例如,如果你的脚本以[”开始,[”将被视为0x5b22而不是0x5b 0x22。而0x5b22恰好是一个有效的JavaScript变量=],你能看出其中奥秘吗?
假设Web服务器的给我们的响应包含了一个数组字面量(array literal),并且我们可以控制其中的一部分。我们可以使用UTF-16BE字符集使该数组字面量变成未定义的JavaScript变量,并使用上述技术进行窃取。唯一需要注意的是,组合生成的字符必须形成有效的JavaScript变量。
比如,我们来看看下面的响应:
为了窃取supersecret,我们需要插入一个NULL字符加两个a,由于某种原因,Edge并不将其视为UTF-16BE,除非它包含这些插入的字符。可能Edge会做某种字符集检测,或者也许它会截断响应,NULL之后的字符在Edge上不是有效的JavaScript变量。我不确定,但在我的测试中,似乎就是需要一个NULL并填充一些字符。参见下面的例子:
所以我们像之前一样代理化__proto__属性,并且使用UTF-16BE脚本,服务器响应中的数组字面量的第二个位置是NULL加两个a。接下来解码UTF-16BE编码的字符串,通过移8比特位可以获取到第一个字节,做按位与运算可以获取到第二个字节。结果是弹出["supersecret",",可以看出Edge似乎截断了NULL之后的响应。这个攻击局限性很大,因为许多字符组合后不会生产有效的JavaScript变量。但窃取少量数据可能还是有用的。
在Chrome上窃取feeds
对于Chrome,情况变得更加糟糕。Chrome对包含外来字符集的脚本更加开明。你不需要为了让Chrome使用该字符集而去控制服务器响应。唯一的要求是,像之前一样,字符组合后的要能构成有效的JavaScript变量。为了利用这个“特性”,我们需要另外一个未定义的变量泄露。乍一看Chrome似乎不允许覆盖 __proto__,然而他们并没有考虑到__proto__的深度。
注意:Chrome 54版本中已将该漏洞修复
在Chrome 53版本上窃取JSON feeds的PoC
我们在__proto__链上深入5层,并用代理覆盖之,接下来发生的事情很有趣,虽然name参数并没有包含我们的未定义变量,但是caller(调用者)包含了!它返回一个带有我们变量名的函数!很明显用了UTF-16BE编码,看起来像这样:
啥?所以我们的变量在caller中泄漏了。你必须调用toString方法才能访问数据,否则Chrome会抛出异常。我尝试通过检查函数的constructor(构造器)来进一步利用这个漏洞,看看它是否返回不同的域(也许是Chrome扩展上下文)。当启用了Adblock Plus时,我使用这种方法看到一些扩展代码,但无法利用,因为这些代码看起来只是插入到当前文档中的代码。
在我的测试中,我也用了XML或HTML跨域数据,甚至使用了text / html Content-Type(内容类型),然后出现了一个非常严重的信息泄露漏洞。 Chrome已经修复了此漏洞。
在Safari上窃取feeds
我们也可以在最新版本的Safari中轻松做同样的事。我们只需要少用一个proto,并在代理中而不是caller中使用“name”。
经过进一步测试,我发现Safari和Edge一样,用__proto__.__proto__即可
在不使用JS proxies的情况下黑掉JSON feeds
我之前提到主流浏览器都支持UTF-16BE字符集,如何在没有JS proxies的情况下黑掉JSON feeds?首先,你需要控制一些数据,并且必须构建feed,以便生成有效的JavaScript变量。获取插入数据入之前的JSON feed的第一部分非常简单,只需输出一个UTF-16BE编码的字符串,该字符串赋给非ASCII变量一个特定的值,然后循环遍历该窗口并检查该值的存在,然后属性名将包含所有插入数据之前的JSON feed。代码如下所示:
然后将该代码编码为UTF-16BE字符串,所以我们实际得到的是代码而不是非ASCII变量。实际上,这就是用NULL填充每个字符。要获取注入字符串之后的字符,只需使用增量运算符,并将编码字符串置于窗口属性之后。然后我们调用setTimeout并再次循环遍历窗口,但这次检查NaN (Not a Number),就能找到我们的编码字符串的变量名。看下面:
我把代码放在try catch里面,因为在检查isNaN时IE的window.external会抛出异常。全部的JSON feed如下:
绕过CSP (Content Security Policy,内容安全策略)
你可能已经注意到,转换为UTF-16BE 的字符串也会将新行转换为非ASCII变量,这样做甚至可能绕过CSP! HTML文档将被视为一个JavaScript变量。我们所要做的就是插入一个带UTF-16BE字符集的脚本,该脚本含有编码过的赋值和payload(有效载荷),后面跟着注释。这将绕过只允许脚本引用同域资源(大多数的策略)的CSP策略。
[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课
赞赏
- [翻译]老木马新玩法:Qbot最新攻击手法探究 19013
- [翻译]Crimson远控木马分析 24660
- [翻译]DLL劫持自动化检测 24395
- [翻译]使用Frida框架hook安卓native方法(适合新手) 26743
- [翻译]蓝军测试中PROXY protocol的应用 8319