接上篇《深度剖析站点隔离机制,Part 1》
在上一篇文章中,我们为读者解释了站点隔离以及相关安全机制是如何缓解诸如UXSS和Spectre之类的黑客攻击的。然而,由于渲染器进程中的安全漏洞极为常见,因此,Chromium的威胁模型假设渲染器进程可能会遭到入侵,也就是该进程是不可信任的。为了与这种威胁模型保持一致,Chromium在2019年宣布了对站点隔离机制进行相应的改进,以进一步缓解被入侵的渲染器进程可能造成的危害。在这篇文章中,我们将为读者解释这些改进的细节,以及在此过程中发现的各种安全漏洞。
什么是被入侵的渲染器进程?
攻击者可能会在Chromium的渲染器进程中发现安全漏洞,例如在JavaScript引擎、DOM或图像解析逻辑中,这并不稀奇。有时,这些漏洞可能涉及内存错误(例如,UAF漏洞),导致攻击者的网页在渲染过程中能够执行他们自己的、任意的、本地的代码(例如汇编/C++代码,而不是JavaScript代码)。我们称这样的进程为“被入侵的渲染器”——作者:kasz Anforowicz
这意味着,被入侵的渲染器进程不仅可以读取渲染器进程中的所有内存空间,还可以对其进行写入操作。比如说,允许攻击者伪造渲染器进程的IPC消息给其他进程。这里列出了站点隔离的改进之处。
一个可以实现UXSS的站点隔离绕过漏洞
在寻找绕过站点隔离的方法时,我想起了Bo0oM发现的一个非常有趣的UXSS漏洞。在当时,站点隔离还只是一个实验性的功能,而且出于禁用状态,我想知道是否可以用这个漏洞来绕过站点隔离措施。
于是,我在启用站点隔离机制的情况下对UXSS漏洞进行了测试,发现它在某些方面仍然奏效。当源发生变化后,之前网站的流程仍会被重用。例如,如果试图访问cookie,会导致渲染器进程崩溃,因为站点隔离机制认为该进程不应该为另一个源请求cookie。
这简直就是一个完美的漏洞,完全可以用来绕过站点隔离,因为这个行为类似于一个被入侵的渲染器:我们可以覆盖渲染器进程中的源信息;当然,我们无法藉此绕过进程隔离安全机制。通过这个漏洞,我们可以测试哪个API不会在意伪造的源,并允许我们访问其他源的信息。所以,在进行测试的同时,我也把这个有趣的行为告诉了Masato。很快,他就发现了一个漏洞。原来,我们可以创建一个带有伪造源的Blob URL,并且,只要导航到这个Blob URL,就可以访问目标网站的cookie。
虽然我们挖到了上述漏洞,但我们必须确保这个漏洞仍然存在于稳定版中,因为UXSS的漏洞早已经被修复了。为此,我们进行验证并发现,只要在发送IPC创建Blob URL之前通过WinDbg修改源,就能在稳定版上触发这个漏洞。
在创建Blob URL时,通过在浏览器进程中对源进行相应的验证,就能修复这个问题。
伪造IPC消息
我们可以从上一个漏洞中可以清楚地看到,通过被入侵的渲染器测试站点隔离改进情况的最简单的方法,就是在渲染器进程发送源或URL信息的地方伪造IPC消息。但是,通过阅读代码来寻找这样的地方,并使用Mojo JS来发送伪造的IPC消息,似乎是一项浩大的工程。
于是,我为WinDbg创建了一个名为spoof.js的JavaScript调试器扩展。因为spoof.js能够修改渲染器内存中的源和URL,所以,我只需要进行正常的Web API调用,就能完成IPC的测试工作。这样做还有一个意想不到的好处,就是还可以伪造传统的IPC消息,而非Mojo实现的IPC消息(如果我选择用Mojo JS进行测试,就不可能做到这一点)。
postMessage中安全漏洞
在使用spoof.js进行测试的过程中,我意外发现不仅可以将postMessage发送到一个跨站点的窗口/frame中,还可以通过伪造源来接收来自不同的目标源的消息。
为了修复这个漏洞,只要通过在浏览器进程中对postMessage IPC的源进行相应的验证即可。
利用被入侵的渲染器欺骗地址栏
遗憾的是,我通过spoof.js只在postMessage中找到了一个漏洞。之后,我开始思考是否能够通过其他地方中的漏洞来绕过站点隔离,以确定代码审查与测试的目标。所以,我决定研究一下导航机制。
如果您稍微研究一下Chromium的导航机制,就会发现一个有趣的步骤:渲染器进程在提交导航时会向浏览器进程发送一个IPC消息。这个IPC消息非常耐人寻味,因为渲染器进程可以在导航启动后(即网络进程已经开始下载响应后),知道渲染器进程会将导航提交至哪个源和URL。如果浏览器进程的验证机制不够严谨,就容易出现安全漏洞。
在测试导航的处理过程时,我注意到,如果源是一个不透明的源(opaque origin),我就可以佯称导航已经提交至渲染器进程的任何URL。之所以存在这个漏洞,是因为任何URL都可以是一个不透明的源(使用iframe/CSP沙箱),所以对常规的的源与URL进行检查是没有任何意义的。目前,这个检查已经得到了加强,以确保地址栏欺骗无法实施。
滥用协议处理程序
我的另一个想法是,如果我可以使用registerProtocolHandler API来导航任何协议到一些“坏”的URL(例如Data URL),结果会如何?所以,我检查了它们的实现代码,发现下面的限制是可以绕过/欺骗的:
协议/scheme必须位于allow-list中:
- 这个检查是在渲染器进程中进行的,而浏览器进程只实现了与浏览器处理的协议(例如http:, https:等)相关的deny-list检查。
- 目标URL必须与注册窗口同源。
- 这个检查也是在渲染器进程里面进行的,因此可以绕过。
- 用户必须接受权限提示。
- 权限提示中显示的源是用目的URL计算出来的,而目的URL可以用前面提到的方法进行伪造。
- 跨源的iframe可以调用registerProtocolHandler API。
- 如果传递Data URL,权限提示中不会显示源信息。
有了这些绕过技术,攻击者就可以通过以下步骤绕过站点隔离机制:
- 请求一个协议处理程序的权限,将以下Data URL作为目标URL:data:text/html,点击劫持一个带有自定义协议链接的受害者页面(如tel:、mailto:等)。
- 点击该链接会导航到上面的Data URL,该URL会在受害者的渲染器进程中执行。
通过在浏览器进程中加入适当的检查,就能修复这个漏洞。
Reader模式下的安全漏洞
当Edge开始提供阅读视图时,我们决定考察一下DOM Distiller,其作用是为Chrome中的Reader模式提供支持。我很好奇DOM Distiller是如何实现的,所以,开始着手对其进行了相应的安全测试。
Reader模式在渲染前会对网站的HTML内容进行过滤处理,以获得良好的阅读体验。虽然它们试图删除大部分危险的标签(如script、style等),但许多事件处理程序并没有进行适当地过滤(如< button onclick="alert(1)" >)。而网站的图片和视频则按照相应的设计来进行显示。
这本质上意味着,如果攻击者能够利用图像或视频解析中的内存破坏漏洞,或者能够绕过CSP,攻击者就能够入侵Reader模式进程或在Reader模式下执行脚本。
Reader模式的呈现方式为chrome-distiller:scheme,其中主机名为GUID,url参数指向要处理的页面,例如:
- chrome-distiller://9a898ff4-b0ad-45c6-8da2-bd8a6acce25d/?url=https://news.example
而且,由于可以重用GUID来呈现其他跨站点页面,因此,攻击者可以通过以下步骤利用Reader模式:
- 打开新的窗口进入受害者的网站(Reader模式会缓存页面)。
- 使用相同的GUID将受害者窗口导航到Reader模式。
- 这时,攻击者的窗口和受害者的窗口将处于在同一个过程中,使其可以窃取机密信息。
通过在主机名中加入url参数的哈希值以及改进过滤措施,就可以修复这个漏洞。
由此可以看出,Reader模式的设计非常脆弱,因为同一个进程可以处理来自不同站点的敏感数据,并且这个进程很容易被攻击者所入侵。如果借鉴一下“The Rule of 2”的创意,那么站点隔离的“The Rule of 2”就是:
其他研究人员发现的站点隔离绕过方法
实际上,其他研究人员已经发现了许多很好的站点隔离绕过方法。
小结
在上一篇文章中,我们为读者解释了站点隔离以及相关安全机制是如何缓解诸如UXSS和Spectre之类的黑客攻击的。然而,由于渲染器进程中的安全漏洞非常常见,因此,Chromium的威胁模型假设渲染器进程可能会遭到入侵,也就是该进程是不可信任的。为了与这种威胁模型保持一致,Chromium在2019年宣布了对站点隔离机制进行相应的改进,以进一步缓解被入侵的渲染器进程可能造成的危害。在这篇文章中,我们将为读者解释这些改进的细节,以及在此过程中发现的各种安全漏洞。由于本文篇幅较长,所以分为两部分发表,更多精彩内容,我们将在下篇中加以介绍。
(未完待续)
本文翻译自:https://microsoftedge.github.io/edgevr/posts/deep-dive-into-site-isolation-part-2