【Android学习记录】Activity到底在什么情况下才会造成信息泄露?

1 概述

在Android的安全里面,有很大一部分的是组件的安全,有时候自认为对组件安全已经有比较深的理解,但是回过头去看了一下,还是有点疑问,问了几个人,没有得到答案,周末就自己研究一下
当年乌云很火的帖子–Activity安全里面有一句话,是这么写的:在这里插入图片描述
这里当时看的时候也没太注意,直到最近又回顾的时候,才发现:Activity到底在什么时间才会造成信息泄露,又是如何进行攻击才能获得敏感信息?
还有个英文的版本,文章讲的很不错,也有不少案例:
Using Technology in a Safe Way

2 基础知识

2.1 Android Activity的四种Task方式

模式 描述 首次启动入口函数 二次启动
standard 默认行为。每次启动一个activity,系统都会在目标task新建一个实例。 onCreate onCreate
singleTop 如果目标activity的实例已经存在于目标task的栈顶,系统会直接使用该实例,并调用该activity的onNewIntent()(不会重新create) onCreate 栈顶时onNewIntent
singleTask 在一个新任务的栈顶创建activity的实例。如果实例已经存在,系统会直接使用该实例,并调用该activity的onNewIntent()(不会重新create) onCreate onNewIntent
singleInstance 和"singleTask"类似,但在目标activity的task中不会再运行其他的activity,在那个task中永远只有一个activity。 onCreate onNewIntent

2.2 使用standard和singleTask验证一下入口函数

验证方法:
【Android学习记录】Activity到底在什么情况下才会造成信息泄露?_第1张图片
同时根据Activity生命周期看一下Log情况
【Android学习记录】Activity到底在什么情况下才会造成信息泄露?_第2张图片
APP1的Source:

    public void SendIntentToActivity(View view) {
        Log.d(TAG,"SendIntentToActivity");
        Intent mIntent = new Intent();
        mIntent.setAction("com.test.activitytest2_test");
        startActivity(mIntent);
    }

APP2的Source仅标注几个重要的函数:

public class SecondActivity extends AppCompatActivity {

    private static final String LOGTAG = "Activitytest2TestSecondActivity";
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        Log.d(LOGTAG,new Throwable().getStackTrace()[0].getMethodName());
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_second);
    }
    @Override
    protected void onNewIntent(Intent intent) {
        Log.d(LOGTAG,new Throwable().getStackTrace()[0].getMethodName());
        super.onNewIntent(intent);
    }

    @Override
    protected void onResume() {
        Log.d(LOGTAG,new Throwable().getStackTrace()[0].getMethodName());
        super.onResume();
    }

    @Override
    protected void onRestart() {
        Log.d(LOGTAG,new Throwable().getStackTrace()[0].getMethodName());
        super.onRestart();
    }
}

2.2.1 测试standard模式

        <activity android:name=".SecondActivity">
            <intent-filter>
                <action android:name="com.test.activitytest2_test" />
            intent-filter>
        activity>

自启动测试结果:

2019-11-10 11:32:11.904 9488-9488/com.test.activitytest2 D/Activitytest2TestSecondActivity: onCreate
2019-11-10 11:32:12.064 9488-9488/com.test.activitytest2 D/Activitytest2TestSecondActivity: onResume

当前Activity在栈顶,使用其他应用调用测试结果

2019-11-10 11:52:53.702 10828-10828/com.test.activitytest2 D/Activitytest2TestSecondActivity: onCreate
2019-11-10 11:52:53.887 10828-10828/com.test.activitytest2 D/Activitytest2TestSecondActivity: onResume

可以看到当前的Task栈是不同的,每一个Task里面都有一个新的SecondActivity,mode 都是standard:

   #1 ActivityStack{5757f30 stackId=37 type=standard mode=fullscreen visible=false translucent=true, 1 tasks} type=standard mode=fullscreen
    #0 TaskRecord{81158e2 #42 A=com.test.activitytest2 U=0 StackId=37 sz=2} type=standard mode=fullscreen
     #1 ActivityRecord{9f09a4 u0 com.test.activitytest2/.SecondActivity t42} type=standard mode=fullscreen
     #0 ActivityRecord{1074bae u0 com.test.activitytest2/.MainActivity t42} type=standard mode=fullscreen
   #0 ActivityStack{ee7f7a9 stackId=36 type=standard mode=fullscreen visible=false translucent=true, 1 tasks} type=standard mode=fullscreen
    #0 TaskRecord{e3e3341 #41 A=com.test.activitytest1 U=0 StackId=36 sz=2} type=standard mode=fullscreen
     #1 ActivityRecord{7d3788e u0 com.test.activitytest2/.SecondActivity t41} type=standard mode=fullscreen
     #0 ActivityRecord{af63000 u0 com.test.activitytest1/.MainActivity t41} type=standard mode=fullscreen

在这里插入图片描述

2.2.1 测试singleTask模式

        <activity android:name=".SecondActivity" android:launchMode="singleTask">
            <intent-filter>
                <action android:name="com.test.activitytest2_test" />
            intent-filter>
        activity>

第一次启动测试结果:调用的onCreate

2019-11-10 11:58:47.627 11466-11466/com.test.activitytest2 D/Activitytest2TestSecondActivity: onCreate
2019-11-10 11:58:47.780 11466-11466/com.test.activitytest2 D/Activitytest2TestSecondActivity: onResume

当前Activity在栈顶,使用其他应用调用测试结果,调用的是onNewIntent

2019-11-10 11:58:55.446 11466-11466/com.test.activitytest2 D/Activitytest2TestSecondActivity: onNewIntent
2019-11-10 11:58:55.447 11466-11466/com.test.activitytest2 D/Activitytest2TestSecondActivity: onRestart
2019-11-10 11:58:55.450 11466-11466/com.test.activitytest2 D/Activitytest2TestSecondActivity: onResume

查看当前的Task栈:

   #2 ActivityStack{4c5118c stackId=38 type=standard mode=fullscreen visible=true translucent=false, 1 tasks} type=standard mode=fullscreen
    #0 TaskRecord{b7f16bf #43 A=com.test.activitytest2 U=0 StackId=38 sz=2} type=standard mode=fullscreen
     #1 ActivityRecord{cf74b8c u0 com.test.activitytest2/.SecondActivity t43} type=standard mode=fullscreen
     #0 ActivityRecord{51df2a1 u0 com.test.activitytest2/.MainActivity t43} type=standard mode=fullscreen
   #1 ActivityStack{ee7f7a9 stackId=36 type=standard mode=fullscreen visible=false translucent=true, 1 tasks} type=standard mode=fullscreen
    #0 TaskRecord{e3e3341 #41 A=com.test.activitytest1 U=0 StackId=36 sz=1} type=standard mode=fullscreen
     #0 ActivityRecord{af63000 u0 com.test.activitytest1/.MainActivity t41} type=standard mode=fullscreen

在这里插入图片描述
在Task id=36中不存在这个Activity,说明:通过第二个应用启动,并没有启动新的SecondActivity,而是直接跳转到第一次启动的Activity,

3 信息泄露的可能性分析:

3.1 猜测1:onNewIntent 造成的信息泄露

3.1.1 理论分析

通过上面的验证,我们验证了启动的Activity,如果设置了SingleTask属性,并且当前Activity已经在Task里面时,二次启动,调用的函数时onNewIntent,也就是说,信息泄露的可能性就在这个onNewIntent,不会重新执行onCreate,要想造成信息泄露并且第三方可以获取,调用Activity应该是使用的startActivityForResult,这样如果当前Activity的Intent中存在敏感信息,是不是有可能通过onActivityResult函数接收到?

3.1.2 过程验证

3.1.2.1 源码

攻击APP的源码:

public class MainActivity extends AppCompatActivity {
    private static final int REQUEST_CODE = 1;
    private static String TAG = "TestAttacker1";

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

    public void SendIntentToActivity(View view) {
        Log.d(TAG,new Throwable().getStackTrace()[0].getMethodName());
        Intent mIntent = new Intent();
        mIntent.setClassName("com.test.activitytest2","com.test.activitytest2.SecondActivity");
        mIntent.putExtra("FLAG", "Attacker");
//        mIntent.setFlags(FLAG_ACTIVITY_NEW_TASK);
        startActivityForResult(mIntent, REQUEST_CODE);
    }

    @Override
    public void onActivityResult(int requestCode, int resultCode, Intent data) {
        Log.d(TAG,new Throwable().getStackTrace()[0].getMethodName());
        super.onActivityResult(requestCode, resultCode, data);
        if (resultCode != RESULT_OK) return;
        switch (requestCode) {
            case REQUEST_CODE:
                String pwd = data.getStringExtra("PASSWD");
                Log.d(TAG,pwd);
                break;
        }
    }
}

被攻击APP的源码:伪造一个PASSWD的敏感数据

public class SecondActivity extends AppCompatActivity {

    private static final String LOGTAG = "TestAttacker2";
    private String passwd = "";
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        Log.d(LOGTAG,new Throwable().getStackTrace()[0].getMethodName());
        super.onCreate(savedInstanceState);
        passwd = "passwd";
        setContentView(R.layout.activity_second);
    }

    public void RetResult(View view) {
        // *** POINT 5 *** Sensitive information can be sent since it is sending and receiving all within the same application.
        Intent intent = new Intent();
        intent.putExtra("PASSWD", passwd);
        setResult(RESULT_OK, intent);
        finish();
    }
    @Override
    protected void onNewIntent(Intent intent) {
        Log.d(LOGTAG,new Throwable().getStackTrace()[0].getMethodName());
        Log.d(LOGTAG,intent.getStringExtra("FLAG"));
        super.onNewIntent(intent);
    }

    @Override
    protected void onResume() {
        Log.d(LOGTAG,new Throwable().getStackTrace()[0].getMethodName());
        super.onResume();
    }

    @Override
    protected void onRestart() {
        Log.d(LOGTAG,new Throwable().getStackTrace()[0].getMethodName());
        super.onRestart();
    }
}

3.1.2.2 验证步骤

不设置FLAG_ACTIVITY_NEW_TASK
程序流程如下:

2019-11-11 17:28:28.990 5312-5312/com.test.activitytest1 D/TestAttacker1: SendIntentToActivity
2019-11-11 17:28:29.023 5230-5230/com.test.activitytest2 D/TestAttacker2: onCreate
2019-11-11 17:28:29.064 5230-5230/com.test.activitytest2 D/TestAttacker2: onResume
2019-11-11 17:28:36.957 5312-5312/com.test.activitytest1 D/TestAttacker1: onActivityResult
2019-11-11 17:28:36.959 5312-5312/com.test.activitytest1 D/TestAttacker1: passwd

可以通过setResult将intent的内容读取出来

        Intent intent = new Intent();
        intent.putExtra("PASSWD", passwd);
        setResult(RESULT_OK, intent);

设置FLAG_ACTIVITY_NEW_TASK注意看调用顺序

2019-11-11 17:30:35.053 5445-5445/com.test.activitytest1 D/TestAttacker1: SendIntentToActivity
2019-11-11 17:30:35.066 5445-5445/com.test.activitytest1 D/TestAttacker1: onActivityResult
2019-11-11 17:30:35.083 5230-5230/com.test.activitytest2 D/TestAttacker2: onNewIntent
2019-11-11 17:30:35.083 5230-5230/com.test.activitytest2 D/TestAttacker2: Attacker
2019-11-11 17:30:35.084 5230-5230/com.test.activitytest2 D/TestAttacker2: onRestart
2019-11-11 17:30:35.087 5230-5230/com.test.activitytest2 D/TestAttacker2: onResume

3.1.3 结论

上面的测试证明了,如果使用setResult传递敏感数据,可能会被攻击者获取。但是这个和task有什么关系?
不设置FLAG_ACTIVITY_NEW_TASK 首先进入的是onCreate函数,并没有经过onNewIntent函数

设置FLAG_ACTIVITY_NEW_TASK这个标识的时候,确实是进入了onNewIntent函数,但是onActivityResult在onNewIntent之前执行,是接收不到任何数据的,也就是说虽然原有的Activity没有被重绘,但是数据无法接收

参考文章:在Android 5.0之后,执行顺序被修改,详细请查看下面参考文章
Android:onActivityResult注意点

3.2 猜测2

未知,请大家补充,有朋友猜测过共享内存,不知道怎么处理,待后续研究

4 最终结论

研究了半天,最后没有得到TASK相应的结果:
结论:不要使用setResult传递敏感数据,是有可能被攻击的。
如果其他的人有更好的想法,可以随时联系我进行补充。就目前来看,泄露Activity的敏感信息,尚不知道如何处理

你可能感兴趣的:(Android,安全)