Android Binder Fuzzing 的一些思路
1. binder 简介
Android安全模型的一个关键部分是每一个应用程序都被赋予一个唯一的 Linux 用户 ID 和组 ID,运行在自己的进程和 Dalvik 虚拟机里。
在应用程序安装的过程中,Android系统设备上创建一个专门的目录(文件夹),用于存储此应用程序的数据,并且仅允许应用程序利用
Linux 用户 ID 和组 ID 的相应访问权限对这些数据进行访问。此外,此应用程序的 Dalvik 虚拟机使用应用程序的用户 ID 运行在自己的进程中。
这些关键的机制在操作系统层面上强制数据安全,因为应用程序之间不共享内存、访问权限及磁盘存储。应用程序只能在它们自己的 Dalvik 虚拟机范围内访问内存和数据。
1 | $ ps |
Android app 是由 Activity、Service、Broadcast 和 Content Provider 四大组件构成,而这些组件可能运行在同一进程中,也可能运行在不同的
进程中,而像 PowerManagerService等重要服务都运行在核心进程 system_server
里,所以 Android 系统必须实现一个靠谱的进程间通信机制 (IPC)。
Android系统基于 Linux 开发,Linux 中有很多进程间通信的方法,如 Signal、Pipe、Socket、Share Memeory、Semaphore, 但是 Android 系统并没有使用
这些进程间通信的方法,而是基于 OpenBinder 自己开发了一套进程通信的方法,Binder 是 Android 系统 IPC 通信的机制。
1 | $ adb shell ps | grep system_server |
2. Binder 架构
Binder 使用 CS (Client/Server) 模型,提供服务的进程是 Server 进程,访问服务的是 Client 进程。从代码实现的角度看,Binder架构采用的是分层架构设计,
大致上可以分为 Java 层, Java IPC 层,Native IPC 层, Linux 内核层。
从组件的视角来看,Binder 包含了 Client、Server、ServiceManger 和 binder 驱动, ServiceManager 用于管理系统中的各种服务,见下图。
图中虚线的箭头为跨进程的进程间通信,必须使用 Android IPC binder 机制。
3. 为什么要 fuzz binder
把 Binder 作为一个目标的原因比较明显的,因为在 Android 的安全模型中,Binder 是一个重要的安全边界。在一个低权限
的 app 里面构造的数据,会在高权限的进程里面使用,如果发生问题,就是一个明显的权限提升漏洞。另外数据在处理的过程中,
有 flatten 和 unflatten 两个步骤,这些步骤就像我们平时说的编码和解码一样非常容易出问题。
存在一些非常经典的漏洞, 例如 CVE-2014-7911 该漏洞允许恶意应用从普通应用权限提权到system用户执行命令。
4. 实现
在每个层面都可以实现相关代码进行 Fuzz, 下面分析在每个层面的具体实现。
4.1 直接调用 ioctl
实现 Binder fuzzer 的方法有好几种,最直接的想法当然就是直接调用 ioctl 系统调用。
其中 fd 可以通过打开 /dev/binder 设备文件获得,难点在 binder_write_read
数据结构的构造。
1 | int fd = open("/dev/binder", O_RDWR); |
4.2 Native 层
在 Native 层,利用 IBinder 可以将问题简化, 看上面的结构图,通过阅读 Android 源码, 可以看出我们可以利用 IBinder
的 transcat 来调用相应的Binder 接口函数,参考:
https://android.googlesource.com/platform/frameworks/native/+/master/cmds/service/service.cpp
调用 IBinder 的 transact 需要自己填充 parcel 数据, 可以从下面的示意理解大致的含义:
code 为 Binder 调用接口的功能号, parcel 中需要指定调用那个接口。
1 | String16 get_interface_name(sp<IBinder> service) |
4.3 Java 应用层
到了 Java 应用层,我们可以获得的信息就丰富了,可以获得详细的信息。
1) 获取所有运行的services
1 | public String[] getServices() { |
2) 获得对应服务的IBinder 对象
1 | public IBinder getIBinder(String service) { |
3) 利用反射获取对应接口的所有code
1 | public HashMap<String,Integer> getBinderCode(String interfaceDescriptor) { |
4) 利用反射获取对应接口所有调用的参数类型
binder call 的参数类型
1 | public HashMap<String, List<String>> |
5)Binder 调用
1 | public static IBinder getIBinder(String service) { |
上面的几个步骤已经全部java 代码实现,可行。
5. 实现方法的分析于比较
ioctl 的方法过于底层,需要实现的代码很多,而 java 应用层的代码由于权限原因,经常会遇到没有权限的情况。
所以使用 Native 层的方法是合适的,在Root 的Android 机器上运行代码,可以解决权限问题。而 Java 应用层的
代码利用反射可以获取到每个接口的详细信息,根据获取到的信息指导 Native 层 fuzz 程序的后续变异,应该是比较理想的方法。
6. Fuzz 数据的生成
可以考虑移植 radasma 到 Android 平台
1 | $ git clone https://github.com/anestisb/radamsa-android.git |
6.1 更新 radamsa-android
github 上移植的radamsa 已经比较古老 (0.4), 可以直接将最新版(0.6a)的radamsa 移植
到 Android上。方法比较简单,在原始的 radamsa 中有一个 radamsa.c 将这个文件替换
radamsa-android/jni 目录下的 radamsa.c
然后在文件中添加下面代码, 重新编译即可:
1 |
7. Fuzz 出来的一些漏洞
1) htc m8 零权限打开闪光灯
1 | IBinder serviceBinder = getIBinder("media.camera"); |
2) Android 6.0.1 MOB3OM 手势密码清除漏洞
1 |
|
在Android 6.0.1 MOB3OM 之前的一些版本中, 未在setLockPattern 中做权限检查,
导致apk 不需要任何权限就可以将手势密码清除.
这个问题已经修复 author Jim Miller jaggies@google.com Wed Apr 13 16:35:36 2016 -0700
但是考虑到Android 的碎片化问题, 估计在一些手机中将存在这个问题.