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的敏感信息,尚不知道如何處理