在Android的安全里面,有很大一部分的是组件的安全,有时候自认为对组件安全已经有比较深的理解,但是回过头去看了一下,还是有点疑问,问了几个人,没有得到答案,周末就自己研究一下
当年乌云很火的帖子–Activity安全里面有一句话,是这么写的:
这里当时看的时候也没太注意,直到最近又回顾的时候,才发现:Activity到底在什么时间才会造成信息泄露,又是如何进行攻击才能获得敏感信息?
还有个英文的版本,文章讲的很不错,也有不少案例:
Using Technology in a Safe Way
模式 | 描述 | 首次启动入口函数 | 二次启动 |
---|---|---|---|
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 |
验证方法:
同时根据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();
}
}
<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
<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,
通过上面的验证,我们验证了启动的Activity,如果设置了SingleTask属性,并且当前Activity已经在Task里面时,二次启动,调用的函数时onNewIntent,也就是说,信息泄露的可能性就在这个onNewIntent,不会重新执行onCreate,要想造成信息泄露并且第三方可以获取,调用Activity应该是使用的startActivityForResult,这样如果当前Activity的Intent中存在敏感信息,是不是有可能通过onActivityResult函数接收到?
攻击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();
}
}
不设置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
上面的测试证明了,如果使用setResult传递敏感数据,可能会被攻击者获取。但是这个和task有什么关系?
不设置FLAG_ACTIVITY_NEW_TASK 首先进入的是onCreate函数,并没有经过onNewIntent函数
设置FLAG_ACTIVITY_NEW_TASK这个标识的时候,确实是进入了onNewIntent函数,但是onActivityResult在onNewIntent之前执行,是接收不到任何数据的,也就是说虽然原有的Activity没有被重绘,但是数据无法接收
参考文章:在Android 5.0之后,执行顺序被修改,详细请查看下面参考文章
Android:onActivityResult注意点
未知,请大家补充,有朋友猜测过共享内存,不知道怎么处理,待后续研究
研究了半天,最后没有得到TASK相应的结果:
结论:不要使用setResult传递敏感数据,是有可能被攻击的。
如果其他的人有更好的想法,可以随时联系我进行补充。就目前来看,泄露Activity的敏感信息,尚不知道如何处理