fans

前言

本文是一篇论文阅读,大致会按照论文中的排序进行解读。作者介绍了对Android系统服务进行漏洞挖掘的一种方法,
在配备Android9的六款智能手机上上,发现30种独特的崩溃,其中20个已被Google确认。在fuzz中还发现了138个独特的Java异常。

介绍

Android最基本的功能由Android系统服务提供,直到2019.10, Google已经收到了数百个跟系统服务相关的漏洞。系统服务可以由java或者C++实现,C++实现的服务通常会包含比较多的漏洞。
系统服务会先注册到Service Manager中,用户App通过Service Manager拿到对应服务的接口,然后通过一个叫IBinder::transact(code,data,reply,flags)的RPC接口进行访问,code决定了访问哪个函数,
data则是经过序列化的用户数据。因此,我们可以用这种方法测试所有的系统服务,但我们必须知道所有可用接口及其数据格式。
因此,就有了3个挑战:

  1. 多层接口识别: 除了ServiceManager中注册的服务,部分接口可以通过顶层服务进行访问,另外需要将java实现的服务也考虑在内。
  2. 接口模型提取:每个接口有code和input语法。我们需要找到所有接口的输入语法,并能够提取出路径约束。
  3. 语义正确的输入生成:Android系统本身会对输入进行各种校验,要实现满足校验的输入会比较困难。

针对以上几点,作者提出了FANS, 为了解决第一个问题,FANS 先通过 Service Manager获取所有的顶层接口,并利用深层次接口会调用writeStrongBinder这一事实来确认深层次接口(新姿势get)。
对于问题2,Android系统使用使用特定的反序列化方法(如readInt32)来解析输入数据,通过识别这些调用顺序,我们可以推断出有效输入语法。对于问题3,利用2中得到的输入顺序进行数据的构造,另外,依靠接口间的关系也可以对他们的数据进行一定推断(这一段建议看看源码,说的不太好理解)。作者将源码开源在https://github.com/iromise/fans上。

背景

这一节主要是对Android系统服务的介绍,具体的细节大家可以google一下。作者在论文中放了一张简单的概念图。

设计

首先作者先讲了如何选择fuzz方案。

  1. 如果直接去service端进行fuzz,容易误报,因为IPC会进行数据校验,并且有些校验可能是动态变化的。所以使用RPC方式更合理一些
  2. 如何变异输入,作者提到使用遗传的方式进行
  3. 如何生成输入,作者提到传统的基于模版的方式需要大量人工,而有的则依赖于现有的通信数据,都不适合。而输入模型实际上是隐藏在代码中的。

整个系统分成4个部分

interface Collector

这个阶段主要是把各个接口及其内部接口全部提取出来。作者并不是直接扫描c/c++文件,而是通过编译命令查找,把AIDL生成的部分也找齐。

interface model Extractor

这个阶段主要是把输入输出的格式及其语义全部提取出来。
这个是整个论文的核心。数据的提取遵循完整,精确,方便这三个原则。
作者提出2个方案,一个是从服务端代码进行分析,一个是从AST入手。
从AST入手的好处是变量名和类型保持不变,从C++文件中可以得到精确的接口,这样使得整个又比较方便。
code的提取也是从AST入手,code分布在swich-case结构体中,通过分析就能够拿到对应的数据。
输入输出变量提取中,作者讲到了3中变量,分别是顺序变量,条件变量,循环变量。另外作者还讲到了一个Return声明,
如果return返回了错误代码,意味着该路径少有漏洞,则需要调整输入,尽量少走此路径。
变量的类型同样可以通过AST获取。

dependency inferer

这个阶段把调用接口的依赖关系分析出来。依赖有2中,一种是接口依赖,还有就是变量依赖。

接口依赖

接口依赖又可以分为生成依赖和使用依赖,如果通过一个接口可以返回另一个接口,那么这两个接口存在依赖关系
如果一个接口被另一个接口使用,那么这两个接口存在使用关系

变量依赖

依赖分为交易(transaction)内依赖和交易间依赖.在同一个交易中,如果变量依赖于另一个,那么就是交易内依赖,
如果是一个变量有时候依赖于另一个交易的变量,那就是交易间依赖,为了处理此类依赖,作者写了一个算法,具体见论文。

Fuzzer Engine

这个阶段主要是进行模糊测试。
fuzzer manager 用于同步pc和手机上的信息,fuzzer会根据id生成对应服务的参数,然后进行fuzz
fuzz数据遵循3个原则:

  • 约束第一
  • 依赖第二
  • 类型和名称第三

实现

作者没有直接基于AFL进行开发,因为使用AFL会引入别的问题。

Interface Collector

作者先编译AOSP代码,然后根据记录下来的编译命令扫描文件

Interface Model Extractor

作者将编译命令转换成cc1命令,这样就可以便利AST获取一个粗糙的接口模型,然后将输入输出部分进行保留

Dependency Inferer

这块就是根据算法遍历

Fuzzer Engine

这块作者实现了一个简单的fuzz用于在多个手机上运行,感兴趣的可以看一下代码

评估

作者提出了3个问题:

  1. 找到多少个接口? 它们之间是什么关系?
  2. 提取的接口模型是什么样的? 模型完整并且精确吗?
  3. FANS在发现漏洞方面的效率如何?

对于第一点,数据如下图,由于AIDL也用于生成别的代码,所以无法直接比较接口数量。

对于第二点,从服务接口数量上看,一共发现了811个,多级接口有281个。从变量上看,作者又分成了变量模式,类型别名和
接口间的变量依存关系这3块。
在完整和精确度上,目前没有对比的方法,作者随机检查了10个服务,发现数据基本都对,所以这块是满足需求的。

对于第三点,作者用FANS跑了30天,并发现很多的崩溃。作者想对比BinderCracker的效率,但因为版本问题没有进行测试,只从漏洞数量上
进行了对比,个人觉得FAN还是有很多优势的。之后就是几个案例的分析,感兴趣的可以再看看。

相关工作

Android IPC和服务安全性:

  1. 早期的IPC研究主要集中在Intent上[2,11,16]
  2. 龚[7]是第一个关注Binder IPC的人,并通过手动发现漏洞来证明它是不安全的
  3. [12]进一步提出了fuzz java 接口的解决方案,[10]主要定位供应商实现的Java服务。
  4. BinderCracker将测试扩展到本地服务,它监视IPC流量然后尝试了解输入模型并生成新的测试用例,但它取决于流量的多样性,但还是有很多问题。

结构化输入:

  1. Peach[5]基于模版进行模糊测试,DomFuzz[15]利用语法生成目标程序结构
  2. VUzzer [14]运用动态污点分析(DTA)进行捕获有效输入的共同特征
  3. 其他的就不一一列举了。

总结

系统化的进行系统服务的挖掘,这篇论文应该是第一篇,其中的方法和思想都是值得借鉴的。