找回密码
 会员注册
查看: 11|回复: 0

Android本地服务漏洞挖掘技术-中篇

[复制链接]

2万

主题

0

回帖

6万

积分

超级版主

积分
67450
发表于 2024-10-9 13:39:34 | 显示全部楼层 |阅读模式
Android本地服务漏洞挖掘技术 - 中篇 OPPO安珀实验室 OPPO安珀实验室 OPPO安珀实验室 OPPO广东移动通信有限公司 以用户数据安全和隐私保护为核心,开展系列研究工作并向开发者开放技术能力,致力为合作伙伴及用户打造信息安全堡垒,为OPPO终端安全保驾护航 43篇内容 2023年12月15日 16:01 四川 引言在上文《Android本地服务漏洞挖掘技术 - 上篇》中,我们介绍了Android本地服务中Socket服务的漏洞挖掘相关内容。在本篇中,我们会对Binder服务的基础知识、攻击面、挖掘方法进行详细阐述。基础知识按照作用域和所处的架构层次,我们将Binder服务分为Origin Binder服务,AIDL Binder服务,HIDL Binder服务和Vendor Binder服务。其详细定义如下:Origin Binder服务:这并不是一个官方的命名,这类指的是命名空间为/dev/binder,服务入口函数仍为人工编码的onTransact接口的服务;AIDL Binder服务:AIDL全称为Android Interface Definition Language,是一种Android接口定义语言;HIDL Binder服务,HIDL全称为HAL Interface Definition Language,同样是一种Android接口定义语言,不同之处它是用于HAL与其他进程的通信;Vendor Binder服务:命名空间为/dev/vndbinder,主要用于vendor进程与vendor进程的通信。Origin Binder服务在Android 8之前,绝大多数Binder服务都属于此类。此时的Android安全还是一片蓝海,且AOSP的代码质量也并不高,因此众多白帽子发现了大量的安全漏洞。收集信息通常可以通过两种方式定位到服务和入口函数:其一,使用系统自带的service list命令,不过由于这个列表中大量的服务都是纯Java服务,如果是想挖掘内存破坏相关漏洞,需要更多关注C/C++服务。其二,在Android源码中查找onTransact函数,比如可以在cs.android.com中得到如下结果:漏洞挖掘除了权限问题以外,大部分漏洞主要是由于服务端未正确校验客户端传入的Parcel数据,导致的各类内存破坏漏洞,如未初始化内存,越界读写,类型混淆等。挖掘方法同样分为人工审计和模糊测试两种,不过根据个人经验来看,模糊测试并不适用,这是因为:1.Parcel数据除了常规的uint32,uint8等整型以外,还会包含一些特殊类型,如文件句柄,native handle,parcelable对象等,这使得如果采用afl或libfuzzer的变异策略,那么就很难能够触发到深层次的代码路径;2.服务的各个接口直接通常存在上下文关系,即要触发B接口,首先需要调用A接口,这也使得传统模糊测试很难做到;3.某些深层次服务需要通过公开服务的接口获得;4.服务代码的复杂度较低,人工审计从onTranscat函数跟踪数据流转并不会显得低效;未初始化内存我们以CVE-2020-0134为例,代码在栈上声明了state变量,但并没有初始化值,接着调用getOfflineLicenseState函数以为其赋值。可以看到在某些条件下,getOfflineLicenseState函数会提前返回,并不会对state赋值。接着代码调用reply->writeInt32(static_cast(state));,将未初始化的state值返回给客户端,从而导致内存信息泄露。除了这种直观的漏洞模式以外,在Origin Binder服务中,未初始化内存还有以下几种模式:1.Parcel::read(void* outData, size_t len)用于从Parcel数据中读取len大小的数据到outData缓冲区中,如果Parcel数据长度小于len,那么函数会返回错误且不会清理outData缓冲区,那么如果上层代码在调用Parcel::read之前未初始化变量,且又未校验Parcel::read返回值时,即很大概率会触发漏洞;2.某个IBinder对象的成员变量未在构造函数中初始化;从Android 12开始,默认启用零初始化内存特性,即编译器会自动对原生代码中的堆栈变量进行赋0操作,几乎是从根本上消灭了此类型漏洞,不过根据实测,对于某些大型堆分配,由于性能的考虑,编译器似乎不一定会进行零初始化。越界读写我们以CVE-2020-0346为例,在ATTACH_BUFFER这个分支里,代码尝试从parcel数据中反序列化出GraphicBuffer对象。data.read(*buffer.get())实际上会调用到GraphicBuffer::unflatten函数,当buf[0] == 'BHBB'时候,代码会又调用unflattenBufferHubBuffer函数。numIntsInToken的最大值可为0xffffffff,由于该服务进程是32位进程,因此const size_t sizeNeeded = static_cast(3 + numIntsInToken) * sizeof(int)会产生整数溢出,使得sizeNeeded小于size,从而绕过后面的合法性校验,最终在memcpy处发生越界写漏洞。随着代码质量的改善和白帽子持之以恒的挖掘,从Android 11开始,此类漏洞也几乎绝迹了。类型混淆我们以CVE-2021-1027为例,在SET_TRANSACTION_STATE分支中,代码从parcel数据中反序列化构造了类型为ComposerState的数组state,接着作为参数传递到setTransactionState函数。在进入setTransactionState函数之前,我们先看看ComposerState的定义。可以看到,ComposerState为一个结构体,其中包含一个类型为layer_state_t的state成员变量,而layer_state_t中又有一个IBinder类型的成员变量surface。再经过多轮函数调用后,代码会执行到addSurfaceChangesLocked函数,其中关键代码如下:const sp layer(getLayer(state.surface));在getLayer函数中,代码通过static_cast关键字强制将IBinder类型的weakHandle转换为Layer::Handle,根据前文的分析,这个surface成员变量的值是由客户端传入的,由于这里的强制转换并没有校验类型,因此可能导致类型混淆。更多关于此漏洞的信息,大家可以参考谷歌提供的补丁:https://android.googlesource.com/platform/frameworks/native/+/a8c7c54eed57e5a4b56905a4fb00e27e25b1b908。类型混淆漏洞通常更为隐蔽,也会造成更严重的安全风险。总结近几年来,从Pixel手机上的统计来看,Origin Binder服务(C/C++)的数量愈来愈少,而且原有服务的代码变动也很小。这些因素使得近两年来,已经很少有白帽子发现相关漏洞。如果你仍对此领域有兴趣,那么更推荐你去关注业务逻辑安全(如权限校验不当、用户交互绕过、敏感信息泄漏等),或者也可以看看关于智能指针生命周期、强弱类型转换的问题,也许会有新的发现。参考:1.https://source.android.com/docs/security/bulletin/END往期推荐·Android本地服务漏洞挖掘技术 - 上篇·浅谈Android BLE蓝牙安全隐私问题·手把手系列之——syzkaller原理及实现分析·浅谈 Hybrid App·Android传感器安全分析
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 会员注册

本版积分规则

QQ|手机版|心飞设计-版权所有:微度网络信息技术服务中心 ( 鲁ICP备17032091号-12 )|网站地图

GMT+8, 2025-1-3 02:01 , Processed in 0.523831 second(s), 26 queries .

Powered by Discuz! X3.5

© 2001-2025 Discuz! Team.

快速回复 返回顶部 返回列表