Android Activtity Security

wyzsk發表於2020-08-19
作者: 瘦蛟舞 · 2014/11/18 11:45

0x00 科普


Android每一個Application都是由Activity、Service、content Provider和Broadcast Receiver等Android的基本元件所組成,其中Activity是實現應用程式的主體,它承擔了大量的顯示和互動工作,甚至可以理解為一個"介面"就是一個Activity。

Activity是為使用者操作而展示的視覺化使用者介面。比如說,一個activity可以展示一個選單項列表供使用者選擇,或者顯示一些包含說明的照片。一個短訊息應用程式可以包括一個用於顯示做為傳送物件的聯絡人的列表的activity,一個給選定的聯絡人寫簡訊的activity以及翻閱以前的簡訊和改變設定的activity。儘管它們一起組成了一個內聚的使用者介面,但其中每個activity都與其它的保持獨立。每個都是以Activity類為基類的子類實現。

一個應用程式可以只有一個activity,或如剛才提到的簡訊應用程式那樣,包含很多個。每個activity的作用,以及其數目,自然取決於應用程式及其設計。一般情況下,總有一個應用程式被標記為使用者在應用程式啟動的時候第一個看到的。從一個activity轉向另一個的方式是靠當前的activity啟動下一個。

0x01 知識要點


參考:http://developer.android.com/guide/components/activities.html

生命週期

啟動方式

顯示啟動

配置檔案中註冊元件

<activity android:name=".ExampleActivity" android:icon="@drawable/app_icon">
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
</activity>

直接使用intent物件指定application以及activity啟動

Intent intent = new Intent(this, ExampleActivity.class);
startActivity(intent);

未配置intent-filter的action屬性,activity只能使用顯示啟動。

私有Activity推薦使用顯示啟動。

隱式啟動

Intent intent = new Intent(Intent.ACTION_SEND);
intent.putExtra(Intent.EXTRA_EMAIL, recipientArray);
startActivity(intent);

載入模式launch mode

Activity有四種載入模式:

  • standard:預設行為。每次啟動一個activity,系統都會在目標task新建一個例項。
  • singleTop:如果目標activity的例項已經存在於目標task的棧頂,系統會直接使用該例項,並呼叫該activity的onNewIntent()(不會重新create)
  • singleTask:在一個新任務的棧頂建立activity的例項。如果例項已經存在,系統會直接使用該例項,並呼叫該activity的onNewIntent()(不會重新create)
  • singleInstance:和"singleTask"類似,但在目標activity的task中不會再執行其他的activity,在那個task中永遠只有一個activity。

設定的位置在AndroidManifest.xml檔案中activity元素的android:launchMode 屬性:

<activity android:name="ActB" android:launchMode="singleTask"></activity>

Activity launch mode 用於控制建立task和Activity例項。預設“standard“模式。Standard模式一次啟動即會生成一個新的Activity例項並且不會建立新的task,被啟動的Activity和啟動的Activity在同一個棧中。當建立新的task時,intent中的內容有可能被惡意應用讀取所以建議若無特別需求使用預設的standard模式即不配置launch mode屬性。launchMode能被Intent 的flag覆蓋。

taskAffinity

android系統中task管理Activity。Task的命名取決於root Activity的affinity。

預設情況下,app中的每個Activity都使用app的包名作為affinity。而Task的分配取決於app,故預設情況下一個app中所有的Activity屬於同一task。要改變task的分配,可以在AndroidManifest.xml檔案中設定affinity的值,但是這樣做會有不同task啟動Activity攜帶的intent中的資訊被其他應用讀取的風險。

FLAG_ACTIVITY_NEW_TASK

intent flag中一個重要的flag

啟動Activity時透過setFlags()或者addFlags()方法設定intent的flags屬效能夠改變launch mode,FLAG_ACTIVITY_NEW_TASK標記代表建立新的task(被啟動的Activity既不在前臺也不在後臺)。FLAG_ACTIVITY_MULTIPLE_TASK標記能和FLAG_ACTIVITY_NEW_TASK同時設定。這種情況下必會建立的task,所以intent中不應攜帶敏感資料。

Task

stack:Activity承擔了大量的顯示和互動工作,從某種角度上將,我們看見的應用程式就是許多個Activity的組合。為了讓這許多 Activity協同工作而不至於產生混亂,Android平臺設計了一種堆疊機制用於管理Activity,其遵循先進後出的原則,系統總是顯示位於棧頂的Activity,位於棧頂的Activity也就是最後開啟的Activity。

Task:是指將相關的Activity組合到一起,以Activity Stack的方式進行管理。從使用者體驗上講,一個“應用程式”就是一個Task,但是從根本上講,一個Task是可以有一個或多個Android Application組成的

如果使用者離開一個task很長時間,系統會清理棧頂以下的activity,這樣task被從新開啟時,棧頂activity就被還原了。

Intent Selector

多個Activity具有相同action時,當此呼叫此action時會彈出一個選擇器供使用者選擇。

許可權

android:exported

一個Activity元件能否被外部應用啟動取決於此屬性,設定為true時Activity可以被外部應用啟動,設定為false則不能,此時Activity只能被自身app啟動。(同user id或者root也能啟動)

沒有配置intent-filter的action屬性exported預設為false(沒有filter只能透過明確的類名來啟動activity故相當於只有程式本身能啟動),配置了intent-filter的action屬性exported預設為true。

exported屬性只是用於限制Activity是否暴露給其他app,透過配置檔案中的許可權申明也可以限制外部啟動activity。

android:protectionLevel

http://developer.android.com/intl/zh-cn/guide/topics/manifest/permission-element.html

normal:預設值。低風險許可權,只要申請了就可以使用,安裝時不需要使用者確認。

dangerous:像WRITE_SETTING和SEND_SMS等許可權是有風險的,因為這些許可權能夠用來重新配置裝置或者導致話費。使用此protectionLevel來標識使用者可能關注的一些許可權。Android將會在安裝程式時,警示使用者關於這些許可權的需求,具體的行為可能依據Android版本或者所安裝的移動裝置而有所變化。

signature:這些許可權僅授予那些和本程式應用了相同金鑰來簽名的程式。

signatureOrSystem:與signature類似,除了一點,系統中的程式也需要有資格來訪問。這樣允許定製Android系統應用也能獲得許可權,這種保護等級有助於整合系統編譯過程。

<!-- *** POINT 1 *** Define a permission with protectionLevel="signature" -->
<permission
android:name="org.jssec.android.permission.protectedapp.MY_PERMISSION"
android:protectionLevel="signature" />
<application
android:icon="@drawable/ic_launcher"
android:label="@string/app_name" >
<!-- *** POINT 2 *** For a component, enforce the permission with its permission attribute -->
<activity
android:name=".ProtectedActivity"
android:exported="true"
android:label="@string/app_name"
android:permission="org.jssec.android.permission.protectedapp.MY_PERMISSION" >
<!-- *** POINT 3 *** If the component is an activity, you must define no intent-filter -->
</activity>

關鍵方法

  • onCreate(Bundle savedInstanceState)
  • setResult(int resultCode, Intent data)
  • startActivity(Intent intent)
  • startActivityForResult(Intent intent, int requestCode)
  • onActivityResult(int requestCode, int resultCode, Intent data)
  • setResult (int resultCode, Intent data)
  • getStringExtra (String name)
  • addFlags(int flags)
  • setFlags(int flags)
  • setPackage(String packageName)
  • getAction()
  • setAction(String action)
  • getData()
  • setData(Uri data)
  • getExtras()
  • putExtra(String name, String value)

0x02 Activity分類


Activity型別和使用方式決定了其風險和防禦方式,故將Activity分類如下: Private、Public、Parter、In-house

private activity


私有Activity不應被其他應用啟動相對是安全的

建立activity時:

1、不指定taskAffinity //task管理activity。task的名字取決於根activity的affinity。預設設定中Activity使用包名做為affinity。task由app分配,所以一個應用的Activity在預設情況下屬於相同task。跨task啟動Activity的intent有可能被其他app讀取到。

2、不指定lunchMode //預設standard,建議使用預設。建立新task時有可能被其他應用讀取intent的內容。

3、設定exported屬性為false

4、謹慎處理從intent中接收的資料,不管是否內部傳送的intent

5、敏感資訊只能在應用內部操作

使用activity時:

6、開啟activity時不設定FLAG_ACTIVITY_NEW_TASK標籤 //FLAG_ACTIVITY_NEW_TASK標籤用於建立新task(被啟動的Activity並未在棧中)。

7、開啟應用內部activity使用顯示啟動的方式

8、當putExtra()包含敏感資訊目的應是app內的activity

9、謹慎處理返回資料,即可資料來自相同應用

public activity


公開暴露的Activity元件,可以被任意應用啟動

建立activity:

1、設定exported屬性為true

2、謹慎處理接收的intent

3、有返回資料時不應包含敏感資訊

使用activity:

4、不應傳送敏感資訊

5、當收到返回資料時謹慎處理

Parter、in-house部分參閱http://www.jssec.org/dl/android_securecoding_en.pdf

安全建議

  • app內使用的私有Activity不應配置intent-filter,如果配置了intent-filter需設定exported屬性為false。
  • 使用預設taskAffinity
  • 使用預設launchMode
  • 啟動Activity時不設定intent的FLAG_ACTIVITY_NEW_TASK標籤
  • 謹慎處理接收的intent以及其攜帶的資訊
  • 簽名驗證內部(in-house)app
  • 當Activity返回資料時候需注意目標Activity是否有洩露資訊的風險
  • 目的Activity十分明確時使用顯示啟動
  • 謹慎處理Activity返回的資料,目的Activity返回的資料有可能是惡意應用偽造的
  • 驗證目標Activity是否惡意app,以免受到intent欺騙,可用hash簽名驗證
  • When Providing an Asset Secondhand, the Asset should be Protected with the Same Level of Protection
  • 儘可能的不傳送敏感資訊,應考慮到啟動public Activity中intent的資訊均有可能被惡意應用竊取的風險

0x04 測試方法


檢視activity:

  • 反編譯檢視配置檔案AndroidManifest.xml中activity元件(關注配置了intent-filter的及未設定export=“false”的)
  • 直接用RE開啟安裝後的app檢視配置檔案
  • Drozer掃描:run app.activity.info -a packagename
  • 動態檢視:logcat設定filter的tag為ActivityManager

啟動activity:

  • adb shell:am start -a action -n package/componet
  • drozer: run app.activity.start --action android.action.intent.VIEW ...
  • 自己編寫app呼叫startActiviy()或startActivityForResult()
  • 瀏覽器intent scheme遠端啟動:/tips/?id=2893

0x05 案例


案例1:繞過本地認證

WooYun: 華為網盤android客戶端本地密碼繞過(非root也可以)

繞過McAfee的key驗證,免費啟用。

$ am start -a android.intent.action.MAIN -n com.wsandroid.suite/com.mcafee.main.MfeMain

案例2:本地拒絕服務

WooYun: 快玩瀏覽器android客戶端本地拒絕服務

WooYun: 雪球android客戶端本地拒絕服務漏洞

WooYun: Tencent Messenger(QQ) Dos vulnerability(critical)

WooYun: Tencent WeiBo multiple Dos vulnerabilities(critical)

WooYun: Android原生的Settings應用存在必現崩潰問題(可造成拒絕服務攻擊) (涉及fragment)

案例3:介面劫持

WooYun: android利用懸浮視窗實現介面劫持釣魚盜號

案例4:UXSS

漏洞存在於Chrome Android版本v18.0.1025123,class "com.google.android.apps.chrome.SimpleChromeActivity" 允許惡意應用注入js程式碼到任意域. 部分 AndroidManifest.xml配置檔案如下

<activity android:name="com.google.android.apps.chrome.SimpleChromeActivity" android:launchMode="singleTask" android:configChanges="keyboard|keyboardHidden|orientation|screenSize">
        <intent-filter>
            <action android:name="android.intent.action.VIEW" />
            <category android:name="android.intent.category.DEFAULT" />
        </intent-filter>
</activity>

Class "com.google.android.apps.chrome.SimpleChromeActivity" 配置

相關文章