image-20200716184642338

学习一下Amy小姐姐在今年BlueHat的演讲,视频可以在油管 上查看。

The complexity of JavaScript and Web APIs has led to an increase in vulnerabilities found in modern web browsers. Many of these vulnerabilities are relatively easy to exploit and lead to full code execution within the browser process. To combat this, browser vendors have worked to secure their platforms through sandboxing. With a sandbox, exploits can only hijack control of the renderer process (the process which displays the webpage) and are not able to interact with the rest of the system to install malware or execute other payloads. An advanced attacker may try to find holes in the sandbox to gain access to the full system, but this requires finding more bugs and spending more engineering efforts.

However, if we forgo the sandbox escape, there is still interesting functionality the renderer process is allowed to access. This talk will examine the functionality available to a compromised renderer process and see how it can be abused to compromise browser users. We will also look at how this could be used on up-to-date browsers though “patch - gapping”. Finally, we will see how there is a changing landscape in recent years and what strides taken towards a more isolated renderer processes.

常规浏览器攻击思路

在很多浏览器利用的场景中,我们的思路基本都是逃逸沙箱进行攻击。这次演讲的内容就是探讨一下,在没有逃逸的情况下如何利用漏洞进行攻击。

一般来说,浏览器攻击分成两个部分,一个是利用渲染层的漏洞,一个是浏览器层的漏洞,或者操作系统等等的漏洞(?),通过第二个洞完成逃逸。但是逃逸的“代价”是比较大的,找到能够逃逸的漏洞并不是容易的事情。

其实,浏览器本身中就有很多让我们感兴趣的信息,例如:

  • 访问银行账户
  • 访问本地或云端的敏感文件
  • 查看 邮件/信息的历史记录
  • 下载功能

这些功能我们都是放心大胆地交给浏览器的。

浏览器的架构如图所示(这个我们之前学过啦),是一个多进程的架构,例如网络的进程、GPU的进程等等。

image-20200716185529630

但是我们比较关心的是其中的渲染进程 Renderer Process。JS和HTML等等就是在这一进程中处理的。一般来说这也是攻击者最先下手的位置。

通过利用渲染层的漏洞,攻击者一般可以拿到一个任意地址读写的能力,即对进程中的任意地址都可以读写。有了任意地址读写之后,通过JIT页或者劫持函数指针进行ROP等等,我们可以达到代码执行的效果。注意目前还是在渲染层中。

那么我们已知追求的代码执行,可以给我们带来什么呢?

  • 调用系统函数,如map页面,调用IPC等等
  • 修改现存的代码,不过需要代码存在可读可写的位置
  • 和操作系统进行交互…..?

当然第三条是不可以的啦。因为有沙箱的存在。

1
Content Process <===> IPC <===> Rest of the system

有了沙箱之后,系统中的剩余部分就得到了保护。就算渲染层被攻击,或者出了崩溃等等问题,剩余的系统不会收到影响。

渲染层攻击SOP

那既然有沙箱,我们的第一反应自然是要逃逸出沙箱。 然而正如我们之前提到了,逃逸沙箱是比较困难的事情:要么需要再找一个洞,或者在其他的服务中找洞,这并不容易。

沙箱的目的就是让我们在沙箱中干不了什么有趣的事。但是事实真的如此吗?

我们可以转向浏览器本身。通过利用浏览器的特性,来攻击用户的数据,干一些坏事。

下面我们一步一步深入。首先看一下这个简单的Web请求:

image-20200716192229366

假设有这样的一个网站,我们发送JSON请求,会把我们的邮件通过JSON返还给我们。流程如下:

  • 我们的浏览器发送一个HTTP请求给Web服务器
  • Web服务器收到请求,获取所有的邮件,写成JSON格式,发送HTTP请求回来
  • 我们的浏览器收到了JSON,浏览器中的JS会把收到的JSON渲染成页面。

OK,现在我们的目标就是读取受害者的邮件。假如我们有一个attacker.com。

image-20200716193616490

可以看到浏览器拦截了这次请求。

image-20200716193935283

关于跨域HTTP请求可以参考这篇文章。这里似乎可以简单理解为,attacker.com的JS脚本应该只可以访问attacker.com同域的资源。这里显然域不同,产生了跨域的请求,被浏览器拦截。想要跨域访问资源(如图片用URL渲染)需要其他的方法。

这里拦截我们的原因就是同源策略。你只被允许访问自己的同源的资源。google.com和attacker.com不应该可以互相交互。

有这样的安全策略,我们想到的就是这样的策略具体在哪里应用,没准有漏网之鱼。如果我们彻底控制渲染层的话,是不是可以绕过这里的检查。

Safari

WebKit使用SecurityOrigin来管理同源策略。其中有一个canAccess函数,用于决定你是否可以访问一些网站的资源。

image-20200716195029573

但是这个检查是在渲染层中完成的。也就说,存放在渲染层的代码段中。如果我们可以通过任意地址读写patch掉这个函数,就可以绕过同源策略的检查了(这里有个疑问,代码段不应该是只读的么…啥时候能可读可写)。其实还有一种更简单的方法:Safari用到了一个变量m_universalAcess, 这个是用来控制是否要开启这个安全检查的(猜测本意是用来调试debug的)。

image-20200716221759250

如果我们能够修改这个变量变为True就可以关闭检查,从而访问任意网站了。

image-20200716221857568

Demo:

image-20200716221937642

Firefox

Firefox中并没有像Safari中m_universalAcess那种变量让攻击变得非常简单,但是也有利用的点:

image-20200716222329341

这里的CORS(访问控制)有点像白名单版本的SOP。Firefox用到了一个函数:CheckRequestApproved,用于检查你的请求是不是符合白名单。同样地我们可以patch这个函数让它一直返回true。

UXSS

好了,通过上面的case study我们知道通过渲染层的漏洞可以进行跨站请求。但是漏洞利用的威力还可以再扩大:可以在访问的那个网站中执行Javascript。这有点像Web安全中的XSS:在页面中插入JS代码。通过我们的方法,并不需要目标网站存在XSS漏洞,就可以达到XSS的效果。

image-20200716223709080

Safari

通过注入iframe可以实现上述攻击。

image-20200716223953851

虽然理论上不能通过iframe访问其他网站,但是我们依旧有绕过的方法。在Safari中有一个函数:IsInsecureScriptAcess,用于判断iframe请求的是不是同站的。不过也是用到了m_universalAcess这个变量。

image-20200716224445148

这个可以用之前提到的方法,在渲染层修改。攻击效果:

image-20200716224604470

image-20200716224640900

但是这个方法并不是对所有网站通用,以Google.com为例,用iframe引用会直接报错:

image-20200716225350022

这是因为Google设置了sameorigin。这使得Google的iframe只能放在Google自己的页面中。所以在自己的网站中放个Google是不可能的。那我们看一下这是在哪里检查的以及如何去绕过:

image-20200716233038285

再一次地,可以看到这个检查是在渲染层进行的,在这个ShouldInterruptLoadXFrameOptions函数中。同样我们可以patch这个函数,让它一直返回false这样就不会打断iframe加载了。

但是,patch code并不是容易的事情。工作量比较大,并且和具体架构有关,架构可能应用了一些安全策略,例如iOS会对指针做检查来避免代码执行。

出了patch代码的方法,还有另一种可行的方法,就是去修改对象。

image-20200717104439845

在Check函数中,做的检查的过程有点类似于字符串比较,比较域名和端口等,通过任意地址读写我们把内存中的attacker.com修改成google.com,就可以绕过检查了。这种方法不需要patch代码或者拿到代码执行。

image-20200717104733689

image-20200717104817863

效果:iframe中加载了google.com。之后我们可以写入一些JS代码,就可以掌控你的Google账号了。整个过程不需要进行沙箱逃逸。

现在,我们可以在iframe中加载其他网站,并且执行任意的JS代码。这个能力要怎么利用呢?有了JS代码的执行能力,我们可以使用很多的API:

  • local storage
  • indexdb
  • Cookies
  • service workers

前三个比较好理解,第四个是重点。

image-20200717105314940

如果我们控制了Service worker,就可以做类似于中间人攻击的事情,对于发送给网站和从网站接受的数据都可以控制。Service worker的持续性很好,可以一直存在。

一个简单的场景:我们控制了一个Service worker,然后受害者登陆网站,我们通过service worker就劫持了受害者的账号。

image-20200717105650185

那么通过UXSS能否去安装一个service worker呢?

开发者为了防止XSS,设置了一些限制:

image-20200717110205680

不幸的是这些检查,在Safari中并不是在渲染层完成的。目前还没有绕过的方法。

image-20200717110412416

image-20200717110513092

但是文件类型检查、目录限制等这些是在渲染层完成的。整体思路:

image-20200717110733097

攻击之后的效果是持久的。就算推出Safari,再次访问目标网站依旧是被攻击的。演示的时候尝试下载一个软件,下载的内容也是被修改了的。如果我们把下载的文件替换成一个二进制后门或者其他恶意软件等等, 就可以进一步攻击。

Firefox

Inject DLL:

image-20200717120138943

可以看到我们只利用渲染层的JS引擎的漏洞,在不碰沙箱的情况下就可以做出很多攻击:

image-20200717120305730

并且攻击的效果是持续的。

为了缩短攻击可能持续的时间,Google把patch gap的时间从33天缩短为15天,这样就不会给攻击者留太多的时间用于攻击。

Chrome的做法

想法就是不要让渲染层来决定太多事情。Chrome在做的就是Site isolation:

image-20200717120802349

也就是说不同源的必须要加载在不同的进程,只能通过IPC通信。但是这个思路依旧有一些限制:

image-20200717121650124

WCTF Mojojojo

image-20200717121859158

如果采用之前的方法,由于site isolation的存在我们不能直接读取flag文件,但是通过multipart/*可以绕过。

总结

image-20200717122207726