Binder驱动程序中的漏洞已于2019年12月10日向Google报告,该漏洞已包含在2020年3月的Android安全公告(CVE-2020-0041)中,Binder的漏洞刚开始学习,通过翻译顺便学习分享一下。
在这篇文章中,将介绍此漏洞,写了两个漏洞利用方法:Chrome的沙箱逃逸利用CVE-2020-0041,从存在漏洞的渲染器攻击Chrome浏览器进行权限提升,此漏洞会危害内核并从常规的untrusted_app升级到root。
该漏洞是由计算驱动程序已验证的有效偏移量时的逻辑漏洞引起的,尤其是当Binder驱动程序正在处理transaction时,它会经过多个偏移量,并在每个此类偏移量处验证并转换Binder对象。
BINDER_TYPE_PTR和BINDER_TYPE_FDA类型的对象可以具有父对象,该父对象必须是已验证的对象之一。为了验证这一点,驱动程序使用以下代码:
[1]和[3]处的num_valid计算不正确,因为与sizeof(binder_size_t)的乘积应改为除法,由于该漏洞,可以提供越界偏移作为PTR或FDA对象的父对象。
有趣的是,此漏洞仅在 transaction的发送路径期间发生,而在清除代码中正确计算了相同的值。
即使越界对象存在偏移,但仍可以使用bind_validate_ptr和/或binder_validate_fixup函数验证父对象,然后使用它们,因此,不可能直接提供完全任意的对象。
我们可以利用以下事实:在偏移量数组之后是 transaction缓冲区中的额外缓冲区或sg_buf,并且当遇到BINDER_TYPE_PTR时,会将这些缓冲区复制到其中(以上代码片段中的[2])。
基于此,如果我们在复制相应的sg_buf数据之前使用越界偏移,则该数据将未初始化并从先前执行的 transaction中获取。但是,如果在复制相应的sg_buf之后使用了越界偏移量,则将从新复制的数据中获取偏移量。
这与Synacktiv确定的方法完全相同,您可以在他们的博客文章和幻灯片中找到描述。
然后,我们的漏洞利用程序执行以下步骤来触发这种情况:
将伪造的BINDER_TYPE_PTR对象添加到 transaction中,偏移量为fake_offset。
在legit_offset处添加了合法的偏移量BINDER_TYPE_PTR对象。
添加了一个BINDER_TYPE_PTR对象,其父对象设置为越界偏移。通过发送初始 transaction,我们将出站偏移量预先初始化为legit_offset值。
驱动程序现在具有经过验证的对象,该对象具有超出范围的父偏移量,这也意味着超出范围的父偏移量将成为隐式信任。
添加第二个BINDER_TYPE_PTR对象,并具有相同的越界父级偏移量。但是,这次我们也向该对象添加了一个缓冲区。然后,位于[2]处的副本会将越界偏移量设置为fake_offset。
由于在步骤3中处理了对象之后,隐式信任了越界偏移,因此驱动程序现在信任伪造的BINDER_TYPE_PTR。
在此阶段,驱动程序尝试使用指向已复制到其中的sg_buf数据的指针来修复父缓冲区。
由binder_fixup_parent完成:
last_fixup_obj_off此处是指在步骤3中验证的对象,并且由于已被验证,因此其父偏移也被隐式信任。因此,binder_validate_fixup调用成功。
但是,在处理后一个BINDER_TYPE_PTR对象时,parent_offset的内容已被修改,现在指向具有完全受控内容的伪对象(上面的代码段中的parent)。
因此,我们可以在[1]提供一个任意的buffer_offset,然后将其用于复制[2]处的sg_buf的地址。
但是请注意,为了使复制成功,我们需要知道b-> user_data的值,更糟糕的是,在Pixel设备当前随附的代码中,如果漏洞,则会触发以下BUG_ON,这将导致内核崩溃:
b-> user_data是绑定程序缓冲区的地址,在接收方的地址空间中将 transaction复制到其中。
如果我们能够将 transaction发送到自己的流程中,则此值就无效了,这是可能的,但是需要使用一些技巧(我们将在以后的文章中讨论这些技巧之一)。此外,对于当前发布的Chrome浏览器,此映射在渲染器和浏览器进程中位于相同的地址。
还要注意,在最后一步中,可以使用BINDER_TYPE_FDA对象代替BINDER_TYPE_PTR对象。在那种情况下,驱动程序将处理 transaction的任意部分作为文件描述符,将它们发送给接收者并替换文件描述符号。
这也可以用于破坏任意双字,例如经过验证的对象偏移量。如果需要,这将允许将完全任意的对象注入 transaction中。
使用我们的内存损坏原语,我们可以覆盖已经验证的Binder transaction的部分。由于这些值对内核以外的模块都是只读的,因此系统的其余部分信任它们,我们可以在两个阶段使用这些值作为攻击目标:
在这篇文章中,我们将集中讨论针对用户空间组件时可以做什么,在后续文章中,我们将讨论针对内核清理代码。
可以从libbinder中的Parcel.cpp中找到负责从Binder transaction中解组数据和对象的代码。
从 transaction中读取对象时,将执行以下代码:
从那里我们了解到,如果我们破坏BINDER_TYPE_BINDER对象的cookie字段,最终将控制sp <IBinder \>指针。
为了了解可以从Chrome沙箱访问的内容,我们可以看一下可以从服务管理器或也可以访问的句柄访问的服务。对于前者,我们可以看一下SELinux策略:
2a2K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6S2L8X3c8J5L8$3W2V1i4K6u0W2k6$3!0G2k6$3I4W2M7$3!0#2M7X3y4W2i4K6u0W2j5$3!0E0i4K6u0r3M7r3I4S2N6r3k6G2M7X3#2Q4x3V1k6K6P5i4y4@1k6h3#2Q4x3V1k6K6k6i4m8G2L8r3W2U0P5g2)9J5c8W2)9J5b7W2)9J5c8Y4u0W2k6Y4y4Q4x3V1k6@1j5h3N6K6i4K6u0r3j5h3&6V1M7X3!0A6k6q4)9J5k6o6p5H3i4K6u0W2x3q4)9J5k6e0m8Q4y4h3k6J5x3K6q4Q4x3V1k6H3M7X3W2$3j5i4c8W2i4K6u0r3K9i4y4G2L8r3q4@1k6h3c8Q4y4h3k6S2M7s2m8Q4x3X3g2@1k6g2)9J5x3K6V1%4
这意味着我们可以向服务管理器询问活动管理器,显示服务,WebView更新服务和Ashmem服务的句柄。我们可以看到,所有这些进程都是64位,而我们处于32位进程中。因此,除非从这些进程中额外泄漏出来,否则很难触发漏洞。
因此,我们转向了常规Chrome渲染器过程可用的Binder句柄,为了识别它们,我们使用了以下C代码,它们从AOSP服务管理器代码中借鉴了实用程序功能:
将此代码注入Android 10系统上的渲染器进程,将提供以下输出:
所有这些句柄都属于64位服务进程,但属于Chrome浏览器的IParentProcess除外。对我们来说幸运的是,此过程还在当前的Chrome版本中以32位模式运行,因此我们可以将其定位为目标。
但是,看一下接口定义会有些令人沮丧:
这些调用对于我们的目的而言不是很有趣,因为没有对象被传递。但是,如果我们更深入地了解如何实现Binder对象,则可以在BBinder类中找到所有对象都源自的问题的解决方案:
因此,在上面的IResultReceiver对象中,如果我们用bug 覆盖了它的cookie字段,它将指向受控数据。为了可靠地执行此操作,漏洞利用程序执行以下步骤:
我们向 transaction中添加了上面显示的BBinder类实际上未解析的其他对象,这不是问题,因为libbinder代码只是忽略了可能添加到 transaction中的其他对象,只要所需对象按预期顺序存在即可。
因此,通过此设置,我们最终得到一个Binder对象,该对象指向Binder映射内部的受控数据。
伪对象被强制转换为IResultReceiver对象,最终导致代码被执行,我们需要确保的第一件事是代码可以在对象上获得引用。
特别是,RefBase对象用于引用计数。该对象的地址是从缓冲区的第一个双字中提取的,接下来,从RefBase实例获得一个指针,并且引用计数增加:
在[1]处取消引用的指针必须指向可写地址,并且其内容不得为特殊值0x10000000以避免在[2]处进行调用。
第一部分是有问题的,因为我们的伪造对象位于一个绑定器映射中,该映射对于userland始终是只读的。在我们的利用中,我们将此指针设置为libc数据段中的临时缓冲区,之所以可以这样做,是因为我们已经假设目标进程映射与我们自己的映射非常相似,因此我们可以简单地获取自己的libc地址。
一旦通过了incStrong调用,代码便会一直到以下间接调用:
myobj的值在这里与假对象的值匹配,因此我们最终从假对象调用函数指针,并将假对象地址作为第一个参数传递。
]因此,通过以下代码,我们可以获得代码执行:
utmp此处是libc中似乎已被使用的缓冲区的地址,它是可写映射的一部分。由于libc在渲染器进程和浏览器进程上的地址是相同的,因此我们可以在我们自己的进程中对其进行解析。同样,我们根据自己的流程来解析所有ROPgadget。
另外,由于绑定程序映射地址在两个进程中也相同,因此我们可以使用它来计算目标进程中我们自己的数据的地址。
由于还会将假对象作为第一个参数传递,因此我们使用ldm r0 !, {r2,r5,ip,sp,lr,pc})gadget将堆栈旋转到R0 ,并从对象的开头启动ROP链。
最终设置如下所示:

但是,由于映射是只读的,因此无法调用使用堆栈的函数。因此,我们的ROP链执行以下步骤:
一旦cacheflush返回,我们就会执行shellcode和正确的读/写堆栈。为了减小ROP链的大小,我们使用一个小的初始shellcode,该代码使用memcpy将下一阶段复制到RWX内存中,然后再次调用cacheflush并最终跳转到它。
现在,我们可以不受限制地执行Shellcode了,我们可以执行漏洞利用程序所需的任何操作,然后修复Chrome进程。
为了实现流程的继续,我们的主要shellcode发送回127.0.0.1:6666并检索一个共享库。共享库存储为/data/data/<package_name>/test.so,并使用__loader_dlopen加载。
该__loader_dlopen符号目前由注入渲染器的代码启发式解决。
加载共享对象后,shellcode将恢复浏览器进程状态。
为此,我们使用较高的堆栈帧之一,该堆栈帧可从堆栈中还原大多数寄存器,们使用由art_quick_invoke_stub存储在libart.so中的寄存器的副本:
渲染器代码解析ArtMethod :: Invoke程序集代码,并找到art_quick_invoke_stub调用的返回地址。然后,shellcode在堆栈中查找以找到相应的堆栈帧,并在返回之前恢复所有寄存器。
但是,仅返回那里会导致Art VM随后崩溃。
为了解决这个问题,我们分析了崩溃的位置。我们观察到的崩溃与垃圾回收相关,并在以下代码内发生:
汇编代码如下:
在[1]处,我们检查Thread对象的偏移量0xDC是否为null,在我们返回的位置,r6指向当前的Thread 对象。
因此,我们的shellcode 从还原的寄存器中获取当前的Thread 值,并在继续操作之前清除此字段。
shellcode的最终恢复部分如下所示:
这样,在加载共享对象后,浏览器进程将继续执行,共享对象因此可以执行任何其他操作,例如启动后台线程或启动反向shell。
以下视频演示了在Pixel 3上攻击Chrome浏览器的过程。在左上角,可以看到目标设备上的root shell,我们将其用于将利用漏洞注入渲染器进程。在左下角,可以通过logcat看到我们漏洞利用的输出。
在右侧,将看到目标设备的显示屏,其中显示了目标设置并启动Chrome,启动Chrome之后,我们使用root shell注入shellcode,立即在屏幕的左上角收到一个反向shell。
该shell程序在浏览器进程的上下文中运行,因此逃逸了沙箱保护。


要测试漏洞利用,先build提供的Android branch,该漏洞利用通过以下参数build:
初步build成功后,应用在sandbox/v8.diff
的 v8
代码,并切换main.diff
到主存储库:
这些修补程序会在indexOf
类型化数组缓冲区的函数中创建一个Hook,当使用3个或更多参数调用该Hook时,会将exploit执行重定向到函数content/renderer/exploit.c
。
应用补丁后,重建Chromium并安装生成的APK。
build并将其安装在手机上之后,需要进行以下设置:
build要加载的库,然后从sandbox/
文件夹运行serve.py
脚本。该脚本将提供payload.so
要由shellcode加载的文件。此外,如果文件请求它们,则它还会提供payload.dex
和 payload.exe
文件,payload.so
用于LPE利用:
在tcp:5555上设置反向Shell侦听:
使用python服务访问sandbox/index.html
以触发漏洞利用。
现在,只需导航到http:// localhost:8080 /即可触发漏洞利用。
该漏洞利用还会在logcat中使用PWN标签生成调试输出。要查看它,只需运行adb logcat | grep PWN,产生类似于以下内容的输出:
可以在Blue Frost Security GitHub中找到完整漏洞利用代码,我们提供此代码作为Chromium 78.0.3904.62的一组漏洞演示。
35dK9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6Y4K9i4c8Z5N6h3u0Q4x3X3g2U0L8$3#2Q4x3V1k6T1L8s2g2W2k6Y4u0G2M7%4c8K6k6h3y4#2M7X3W2@1P5g2)9J5c8V1y4h3c8g2)9J5k6o6t1H3x3U0m8Q4x3X3b7H3x3o6b7I4i4K6u0r3
在下一篇文章中,我们将讨论如何攻击内核执行的处理过程,以便使用此相同的bug实现将特权提升到root的权限。
原文地址:826K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6D9j5h3u0K6i4K6u0W2j5X3I4#2k6h3k6J5L8%4y4@1M7$3g2U0N6i4u0A6N6s2W2Q4x3X3g2V1k6g2)9J5c8X3u0D9L8$3N6Q4x3V1j5J5x3o6t1H3i4K6u0r3x3o6y4Q4x3V1j5K6x3g2)9J5c8X3y4$3k6g2)9J5k6o6t1H3x3U0m8Q4x3X3b7H3x3o6b7I4i4K6u0V1M7r3q4J5N6q4)9J5k6o6q4Q4x3X3c8K6j5h3&6V1j5X3!0^5i4K6u0V1k6i4y4U0j5i4m8W2i4K6u0r3
[培训]科锐逆向工程师培训第53期2025年7月8日开班!
最后于 2020-4-7 22:48
被0xbird编辑
,原因: 格式排版