HwachaFuzzer阅读笔记

论文A Practical Intent Fuzzing Tool for Robustness of Inter-Component Communication in Android Apps阅读笔记

A Practical Intent Fuzzing Tool for Robustness of Inter-Component Communication in Android Apps

同样也是Intent Fuzzer,
设计了一种Intent Specification Language 用于描述Intent的结构,便于生成测试Intent;
基于传统的最长公共子序列算法进行同源crash数据的辨别,因为在Android中,许多原因相同的crash的log日志基本相同,只会在进程号、线程号等方面有细微差别;
指出了当前的Android Intent Fuzzer所采用的算法策略不能相互通用,认为需要在通用性设计上进行努力。而文章设计的Intent Specification Language恰好可以解决这个问题(如果它被广泛认可的话)

研究者来自韩国,Related Work部分写得比较详细,值得认真阅读。

1 Intro

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//样例代码
public class Note extends Activity {
String title;
String content;
void onCreate(Bundle savedInstanceState) {
Intent intent = getIntent();
String action = intent.getAction();
if(action.equals("android.intent.action.EDIT")){
title = intent.getStringExtra("title");
content = intent.getStringExtra("content");
} else if(action.equals("android.intent.action.INSERT")){
title = "No title";
content = "Type your memo";
}
// Code: display a title and a content
}
}

1.1 Intent Vulnerability

intent的设置与intent-filter的过滤匹配分析:https://blog.csdn.net/iispring/article/details/48481793

作者把Intent Vulnerability大致分为四类:

  1. Miss Action: intent部分字段未定义就发送,接受时未检查导致异常。
    对示例代码,如果action没有定义,那么接受之后在处理时会在action.equals()处抛出异常。
  2. Other Action: intent设置了非预期的action,可能导致进入非预期的执行逻辑。
    对示例代码,如果action设置的action不是INSERT或EDIT,那么会在未提取title和content字段的情况下执行后续的代码,可能造成NullPointerException。(其实这里加个else马上return就好)
  3. Miss Value: 如果Intent的Action设置正确,但是Extra的部分字段缺失,也可能导致NULL。
    对示例代码,如果Intent没有设置”title”和”content”字段的值,那么getExtraString时会返回NULL,在执行后续代码时可能NullPointerException。
  4. Wrong Type: Action设置正确,Extra字段也设置了,但是键值的类型搞错了,那么getExtraString也会返回NULL,导致同样的错误。

介绍了相关研究,文献[2-7],并总结了如下不足

  1. 这些研究设计的intent fuzzer都和自己的intent生成策略绑定得太紧,fuzzer更换生成策略的代价较高。
    Null IntentFuzzer粗暴地把intent所有field置空来测试鲁棒性。(毕竟2009的研究)
    JarJarBinks在此基础上加了随机和半合法的intent生成策略。
    DroidFuzzer专注于MIME(audio和video)格式数据处理,主要是针对于MIME数据的生成和变异,来测试应用解析这些数据时有没有逻辑错误。(虽然局限,但是有效)
    IntentFuzzer,IntentDroid,ICCFuzzer使用了一些静态和动态的分析手段,进一步分析了app是如何来响应intent的。
  2. 现有研究对crash日志的处理不到位。
    一个显然的事实是:同一个failure可能在测试过程中产生了多条崩溃日志,应该采取有效的方法来把这些日志去重,降低人工分析日志的工作量。

1.3 Contribution

于是顺其自然地引出自己的贡献:

  1. 设计了一个规范,Intent Specification Language,来描述intent的结构,这样的话,Fuzzer和Intent Generation就可以在逻辑上分离。遵循这种设计语言的Fuzzer和Intent Generation可以自由组合。(在Android Intent Fuzz这个细分方向,应该是这个组最早提的)
  2. 基于最长公共子序列算法设计了一种failure日志去重方法。
  3. Practical&Effective,开源了代码和数据集以及测试结果,比较solid。

可以参考同时发布的其他几篇论文笔记。

3 Motivation: Intent Vulnerability

Activity: 带有窗口的前台程序
Service: 不带有窗口的控制后台任务的程序
Broadcast Receiver: 响应来自系统的事件
Content Provider: 各种数据、存储的使用接口

Intent的优势:Intent可以将组件(Component)松散地耦合在一起,除了目标组件的信息之外,还可以包含指导目标组件如何处理本intent的信息,以及携带一些数据。程序员在编写组件的代码时,并不需要过度区分intent来自App内部还是外部,都可以按照同样的模式来处理intent。

Intent Vulnerability: 恶意构造intent来引发crash,在Intro中有讲。

文章认为Intent Vulnerability的问题难以在开发的过程中解决,原因如下。(至少它希望读者这么认为)

  1. Intent将各个组件松散地结合在一起,因此它的指向性并不很强。以隐式Intent为例,在运行发送之前,没人会知道它最后会调用哪个组件,因为它的构造是不定的,会影响intent的匹配过程,更不要说随时可能出现的动态注册的intent-filter。
  2. 即使是显式的intent和显式的intent-filter以及对应的组件之间,也不好预先验证Intent的合法性。因为Intent的构造和解析代码可以写成多条语句,不好分析。(其实我倒觉得这里可以通过一些静态分析的方式来确定,可以参考FANS对service调用参数的建模方法)

于是研究并解决这个问题就是非常有意义的啦。

4 Intent Specfication Language

文章提出了一种规范化的语言来描述Intent的具体结构。

以前面提及的示例代码为例,可以编写如下的Intent规范,它描述了.Note可以正确响应的两类Intent。
从结构上看,它是一系列Intent规范的集合,一个单独的Intent规范由一系列的字段(Field)组成,每个字段都表示并指明了正确的Intent的字段值。
其中,Extra Data字段由一系列的键值对组成,键名是Intent正确的键名,键值是Intent正确的键值类型。一系列的键值对通过[]包裹。
单个Intent规范由{}包裹,规范之间由||连接表示并集。(原文为disjuction,析取)

1
2
3
4
5
6
7
{ 	cmp = Activity com.example.android/.Note
act = android.intent.action.EDIT
[ title = String, content = String ]
} ||
{ cmp = Activity com.example.android/.Note
act = android.intent.action.INSERT
}

特别介绍了ground intent specification,可以理解为确定性Intent规范。
因为它的每一个字段都被指定了确定的值,因此也只能生成出一个intent测试用例。
根据后文,应该理解为一个确定了内容的Intent。

1
2
3
4
5
6
{ 	cmp = Activity com.example.android/.Note
act = android.intent.action.EDIT
[ title = String "my title",
content = String "your content" ]
dat = URI http://our.uri.com
}

Intent specification language更为准确的表达如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
INTENTSPEC 	::= { FIELD FIELD ... FIELD } | { FIELD FIELD ... FIELD } || INTENTSPEC
FIELD ::= COMPONENT | ACTION | DATA | EXTRA | CATEGORY | TYPE | FLAG | INTERNAL

COMPONENT ::= cmp = COMPTYPE COMPNAME
COMPTYPE ::= Activity | Service | BroadcastReceiver | ContentProvider
COMPNAME ::= ID / (ID | .ID)
ACTION ::= act = ID
DATA ::= dat = URI
EXTRA ::= [ ID=EXTRAVALUE , ... , ID=EXTRAVALUE ]
CATEGORY ::= cat = [ ID , ... , ID ]
TYPE ::= typ = URI
FLAG ::= flg= [ ID , ... , ID ]
INTERNAL ::= internal = BOOL

EXTRAVALUE ::= String STRING? | boolean BOOL? | int INT?
| long LONG? | float FLOAT? | uri URI? | component COMPNAME?
| int[] INTARRAY? | long[] LONGARRAY? | float[] FLOATARRAY?
INTARRAY ::= INT , ... , INT
LONGARRAY ::= LONG , ... , LONG
FLOATARRAY ::= FLOAT , ... , FLOAT

LETTER ::= (A - Z | a - z)+
ID ::= LETTER (A - Z | a - z | 0 - 9 | _ | . | $)*
URI ::= LETTER (A - Z | a - z | 0 - 9 | _ | . | / | : | * | ? | @ )*

(BOOL, INT, LONG, FLOAT, and STRING denote the corresponding primitive values.)

Intent specification language指定了目标组件的类型,可以指导选择测试方法;
指定了intent字段的类型,在此基础之上我们可以变化生成具体的值来确定一个intent的具体内容。

正如前文说到的,Intent Specification Language很好地解耦了fuzzer和generator,只要遵循这个规范语言的fuzzer和generator都可以自由地组合在一起。而且也支持测试人员手动编写一些规范来进行测试。

image-20220301143302262

5 Flexible Intent Fuzzing Tool with an Automatic Tally of Failures

Fuzz的工作流程如下:

  1. Hwacha接收并分析apk文件,基于AndroidManifest.xml生成intent specification,同时安装apk到设备上。
  2. 基于intent specification生成测试用例,进一步生成直接可用的ADB测试命令行并执行, 然后通过logcat获取日志。
  3. 将日志记录去重然后输出到文档中。

image-20220301143359642

5.1 Generating Intent Test Cases

Intent测试用例生成分为两步:1.分析apk得到Intent Specification。2.基于一定的策略将Intent Specification转换为一系列的groud intent specification,也即一个确定的intent。

5.1.1 Automatic Construction of Intent Specification

说了一大堆,其实就是从AndroidManifest.xml里面去提取intent-filter来形成一个minimal intent specification,这样搞有两个不足之处:

  1. 没有包含动态注册的Broadcast Receiver,当然这个可以排除在目标之外。(静态注册的BroadcastReceiver也是通过intent-filter来声明的)
  2. Androidmanifest.xml里面的intent-filter除了可能包含的cmp,act,category,data之外,不再包含关于extra data的详细信息,所以之前提到的intent vulnerability的场景很难去触发到,除了把extra全部置空之外,没有把某个字段置为错误类型值的机会,因为连字段名都不知道。这里就需要引入一些static&dynamic的方法去获取intent在目标组件处被解析时,会尝试从中提取哪些字段。(文章没有涉及这个工作,不过我认为可以通过CodeQL或者AST的方法来尝试做一下)
5.1.2 A Fuzzing Strategy for Generating Ground Intent Specification

顺着上面生成的intent specification,接下来的工作就是生成具体的intent测试用例,也即ground intent specification,然后通过ADB一梭子fuzz。

作者有这样的观察:为了处理intent,目标组件的代码一般在onCreate()函数一开始时,就通过一系列的条件语句来提取intent的属性值或者extra字段值,然后引入不同的intent响应逻辑中。

作者认为,intent vulnerability要么因为intent满足了一些条件进入if语句之后触发,要么因为intent没能满足所有条件进入else语句然后触发。可以参考前面的示例代码。(有点儿废话了,我认为这里的一系列的条件语句和intent.getXXX()的函数调用反而告诉了我们组件可以成功响应这一类intent应该具备哪些属性和extra字段)

1
2
3
4
5
6
7
8
//示例:已经提取出来的intent specification
{ cmp = Activity com.example.android/.Note
act = android.intent.action.EDIT
[ title = String, content = String ]
} ||
{ cmp = Activity com.example.android/.Note
act = android.intent.action.INSERT
}

根据这种舍本逐末的观察,作者构建了以下三类ground intent specification:

  1. Compatible ground intent specification:
    兼容性intent,包含intent specification所描述的所有属性且属性值相同,可以额外携带一些其他属性和Extra数据。目的就是进入if语句中。(如果if只检查属性值的话)

    1
    2
    3
    4
    5
    { 	cmp = Activity com.example.android/.Note
    act = android.intent.action.EDIT
    [ RzUrx7 = boolean True, HR7Ja6d7 = String AHMlyG0z3jjErO ]
    dat = URI qoFXwARtpfV-LNN
    }
  2. Incompatible ground intent specification:
    非兼容性intent,intent带有正确的属性,但是属性值可以发生变化,同时也可以携带其他属性和Extra数据。

    1
    2
    3
    4
    5
    6
    { 	cmp = Activity com.example.android/.Note
    act = android.intent.action.ADD
    [ key2 = int[] -1233387, -72316,
    dKQn = String xZQbcCTOW]
    typ = video/*
    }
  3. Another Incompatible ground intent specification

    另外一种非兼容性intent,只保留正确的cmp属性,其他属性和Extra数据都随机生成。

    1
    2
    3
    4
    5
    { 	cmp = Activity com.example.android/.Note
    dat = tel:123
    cat = [ ttoIjEWJnpk, vYQEpERvvb, xpWj_Q,
    android.intent.category.APP_CALENDAR]
    }

按照作者的设想,compatible intent会进入if语句,也即intent带有正确的属性值(如category,data,action等),将会测试发现对intent的错误解析逻辑。incompatible intent会进入else语句,这里是响应不正确intent的代码,但是可能并没有设计好逻辑,也许会造成Null等未知的异常。

这里没有对随机生成的算法进行叙述,我认为这是很重要的,因为我认为随机生成的extra很小概率会是目标组件所认可的。

5.2 Executing Intent Test Casees via ADB commands

讲了一下如何基于生成的groud intent specification来构建ADB命令行参数。

然后ADB测试的流程:

  1. clear: 清楚上一个测试的app实例
  2. adb:运行本次测试的adb命令行
  3. collect:通过logcat收集本次测试的日志
  4. analyze:分析这次adb命令行测试有没有导致app中的目标组件异常停止,这里是分析log日志里面有没有诸如”xxx has died”之类的字符串模式

还提了一下频繁测试会导致ADB不稳定,所以设置了一定的等待时间。同时写了一个看门狗程序来检查ADB,情况不对就重启设备。

5.3 Automatically Reporting Failures Without Duplication

因为同一个问题可能会产生多个log日志,所以提出了一个基于最长公共子序列算法的log去重算法。

6 Evaluation

6.1 Failures Count Due to Intent Vulnerability

image-20220301171316078

Failures/appSize 倒是一个不错的衡量app鲁棒性的指标。

image-20220301171324759

6.2 Automatic Classification of Failures in Android Logs

这里的group就是同属于一类的logs,相当于一个单独的vulnerability。

image-20220301171910253

6.3 Execution Time for Intent Fuzz Testing

单个app的测试时长和针对其生成的intent case数量是成比例的。

将failure分类的时间一般还是比较小的;Fuzzer tool本身的鲁棒性很强,过程全自动。

但是设置了单个case 5秒的测试时长,平均一个app的测试时长为3.17h。

image-20220301173023677

image-20220301173103983

6.4 Root Cause Analysis on Intent Vulnerability

image-20220301173450277

image-20220301173702068

image-20220301173707981

image-20220301173717609

image-20220301173724004

image-20220301173730151

Welcome to my other publishing channels