【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驗證一下入口函數

驗證方法:
在這裏插入圖片描述
同時根據Activity生命週期看一下Log情況
在這裏插入圖片描述
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的敏感信息,尚不知道如何處理

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章