[TOC]
## 1 Activity組件導出檢測 # 05001
Activity組件對外暴露會導致數據泄露和惡意的dos攻擊。
風險等級: `提醒`
檢測規則:
1. 先檢查組件的android:exported屬性的值,若為true或者未設置改屬性的值,則繼續進行第2步;若為false,則該組件不具有導出性質,是安全的。
2. 若組件的含有<intent-filter>標簽,即使未設置exported屬性,默認也會設置為true,若組件不含有<intent-filter>標簽,則未設置exported屬性值,默認會設置為true。根據該規則,可以確認exported是true還是false。如果是false則是安全的,若是true,則繼續進行第3步。
3. 判斷android:permission屬性的值,空、normal、dangerous是不安全的,signature、signatureOrSystem是安全的。
建議:
* 最小化組件暴露。對不會參與跨應用調用的組件添加`android:exported=false`屬性
* 設置組件訪問權限。對跨應用間調用的組件或者公開的receiver、service、activity和activity-alias設置權限,同時將權限的protectionLevel設置為`signature`或`signatureOrSystem`
* 組件傳輸數據驗證。對組件之間,特別是跨應用的組件之間的數據傳入與返回做驗證和增加異常處理,防止惡意調試數據傳入,更要防止敏感數據返回
## 2 Service組件導出檢測 # 05002
Service組件對外暴露會導致數據泄露和惡意的dos攻擊。
風險等級: `提醒`
檢測規則:
規則同1
建議:
* 最小化組件暴露。對不會參與跨應用調用的組件添加`android:exported=false`屬性
* 設置組件訪問權限。對跨應用間調用的組件或者公開的receiver、service、activity和activity-alias設置權限,同時將權限的protectionLevel設置為`signature`或`signatureOrSystem`
* 組件傳輸數據驗證。對組件之間,特別是跨應用的組件之間的數據傳入與返回做驗證和增加異常處理,防止惡意調試數據傳入,更要防止敏感數據返回
## 3 Receiver組件導出檢測 # 05003
Receiver組件對外暴露會導致數據泄露和惡意的dos攻擊。
風險等級: `提醒`
檢測規則:
規則同1
建議:
* 最小化組件暴露。對不會參與跨應用調用的組件添加`android:exported=false`屬性
* 設置組件訪問權限。對跨應用間調用的組件或者公開的receiver、service、activity和activity-alias設置權限,同時將權限的protectionLevel設置為`signature`或`signatureOrSystem`
* 組件傳輸數據驗證。對組件之間,特別是跨應用的組件之間的數據傳入與返回做驗證和增加異常處理,防止惡意調試數據傳入,更要防止敏感數據返回
## 4 Provider組件導出檢測 # 05004
provider組件導出可能會帶來信息泄露隱患。API Level在17以下的所有應用的`android:exported`屬性默認值為true,17及以上默認值為false。
風險等級:`提醒`
檢測規則:
規則同1
建議:
* 最小化組件暴露。對不會參與跨應用調用的組件添加`android:exported=false`屬性
* 設置組件訪問權限。對導出的provider組件設置權限,同時將權限的protectionLevel設置為`signature`或`signatureOrSystem`
* 由于contentprovider無法在android2.2(API-8)申明為私有。故建議將`minSdkVersion`設為8以上。
## (未實現)ContentProvider目錄遍歷漏洞檢測
該漏洞由于Content Provider組件暴露,沒有對Content Provider組件訪問權限進行限制且對Uri路徑沒有進行過濾,攻擊者通過Content Provider實現的OpenFile接口進行攻擊,如通過`../`的方式訪問任意的目錄文件,造成隱私泄露。

風險等級:`提醒`
檢測方法:
找出導出的provider組件
使用Android安全分析框架*Drozer*動態分析APK,需要安裝安卓模擬器(安裝了*Genymotion*,基于X86 + ARM支持包)
使用adb進行端口轉發,轉發到Drozer使用的端口31415
adb forward tcp:31415 tcp:31415
在Android設備上開啟Drozer?Agent
在PC上啟動Drozer
drozer console connect
在Drozer控制臺中使用命令,分析目標APK是否存在目錄遍歷漏洞
run scanner.provider.traversal -a <package-name>
建議:
* 將不必要導出的Content Provider設置為不導出
* 由于Android組件Content Provider無法在Android 2.2(即API Level 8)系統上設為不導出,因此如果應用的Content Provider不必要導出,**阿里聚安全**建議聲明最低SDK版本為8以上版本;由于API level 在17以下的所有應用的`android:exported`屬性默認值都為true,因此如果應用的Content Provider不必要導出,阿里聚安全建議顯示設置注冊的Content Provider組件的`android:exported`屬性為false;
* 去除沒有必要的`openFile()`接口
* 如果應用的Content Provider組件沒有必要實現`openFile()`接口,阿里聚安全建議移除該Content Provider的不必要的`openFile()`接口
* 過濾限制跨域訪問,對訪問的目標文件的路徑進行有效判斷
* 使用`Uri.decode()`先對Content Query Uri進行解碼后,再過濾如可通過`../`實現任意可讀文件的訪問的Uri字符串
* 設置權限來進行內部應用通過Content Provider的數據共享
* 使用簽名驗證來控制Content Provider共享數據的訪問權限,如設置`protectionLevel=signature或signatureOrSystem`
* 公開的content provider確保不存儲敏感數據
* 提供asset文件時注意權限保護
## 5 Provider:grant-uri-permission屬性檢測 # 05005
`grant-uri-permission`若設置為true,可被其它程序員通過uri訪問到content provider的內容,容易造成信息泄露。默認是false。
風險等級:`提醒`
問題示例:
```
<provider
android:grantUriPermissions="true">
...
</provider>
```
建議:
如無需對外提供數據,則將content provider的`android:grantUriPermissions`設置為false。
```
<provider
android:grantUriPermissions="false">
...
</provider>
```
查閱更多:
* https://security.tencent.com/index.php/blog/msg/6
## 6 Intent-Based攻擊檢測 # 05006
在AndroidManifest文件中定義了android.intent.category.BROWSABLE屬性的組件,可以通過瀏覽器喚起,這會導致遠程命令執行漏洞攻擊。
風險等級:`低危`
問題示例:
Activity只有配置了category filter才有被android.intent.category.BROWSABLE通過這種方式在瀏覽器中打開
通過掃描Minifest中的所有組件,檢測出所有組件有中intent-filter帶有`<category android:name="android.intent.category.BROWSABLE"/>`屬性的,將其標注為問題代碼代碼段,并報出低危風險的提示。
```
<activity android:name=".MainActivity">
<intent-filter>
<category android:name="android.intent.category.BROWSABLE" />
</intent-filter>
</activity>
```
建議:
* APP中任何接收外部輸入數據的地方都是潛在的攻擊點,**檢查并過濾**來自網頁的參數
* 不要通過網頁傳輸敏感信息,有的網站為了引導已經登錄的用戶到APP上使用,會使用腳本動態的生成URL Scheme的參數,其中包括了用戶名、密碼或者登錄態token等敏感信息,讓用戶打開APP直接就登錄了。惡意應用也可以注冊相同的URL Sechme來截取這些敏感信息。Android系統會讓用戶選擇使用哪個應用打開鏈接,但是如果用戶不注意,就會使用惡意應用打開,導致敏感信息泄露或者其他風險。
## 7 Intent Scheme URL漏洞攻擊檢測 # 05007
Intent Scheme URI是一種特殊的URL格式,用來通過Web頁面啟動已安裝應用的Activity組件,大多數主流瀏覽器都支持此功能。
Android Browser的攻擊手段——Intent Scheme URLs攻擊。這種攻擊方式利用了瀏覽器保護措施的不足,通過瀏覽器作為橋梁間接實現Intend-Based攻擊。相比于普通Intend-Based攻擊,這種方式極具隱蔽性,
如果在app中,沒有檢查獲取到的load_url的值,攻擊者可以構造釣魚網站,誘導用戶點擊加載,就可以盜取用戶信息。所以,對Intent URI的處理不當時,就會導致基于Intent的攻擊。
風險等級:`高危`
問題示例:
如果瀏覽器支持Intent Scheme URI語法,一般會分三個步驟進行處理:
1. 利用Intent.parseUri解析uri,獲取原始的intent對象
2. 對intent對象設置過濾規則
3. 通過Context.startActivityIfNeeded或者Context.startActivity發送intent;其中步驟2起關鍵作用,過濾規則缺失或者存在缺陷都會導致Intent Schem URL攻擊
關鍵在于Intent.parseUri函數,比較安全的使用Intent Scheme URI方法是,如果使用了Intent.parseUri函數,獲取的intent必須嚴格過濾,intent至少包含以下三個策略:
* addCategory("android.intent.category.BROWSABLE")
* setComponent(null)
* setSelector(null)
通過掃描出所有調用了Intent.parseUri方法的路徑,并檢測是否使用上述的三個策略。若三者均使用則認為安全,否則需要標注為`高危`
~~~
// convert intent scheme URL to intent object
Intent intent = Intent.parseUri(uri);
// forbid launching activities without BROWSABLE category
intent.addCategory("android.intent.category.BROWSABLE");
// forbid explicit call
intent.setComponent(null);
// forbid intent with selector intent
intent.setSelector(null);
// start the activity by the intent
context.startActivityIfNeeded(intent, -1)
Intent intent_2 = Intent.parseUri(uri);
~~~
建議:
Intent.parseUri函數返回的Intent對象需要按照以下方式進行實現,才可以認為是安全的。
```
// 將intent的URI(intent scheme URL)轉換為intent對象
Intent intent = Intent.parseUri(uri);
// 禁止在沒有設置可瀏覽的目錄(BROWSABLE category)的時候啟動活動
intent.addCategory("android.intent.category.BROWSABLE");
// 禁止顯式調用(explicit call)
intent.setComponent(null);
// 禁止intent的選擇器(selector)
intent.setSelector(null);
// 通過intent啟動活動
context.startActivityIfNeeded(intent, -1)
```
## 8 應用本地拒絕服務漏洞檢測 # 05008
Android系統提供了Activity、Service和Broadcast Receiver等組件,并提供了Intent機制來協助應用間的交互與通訊,Intent負責對應用中一次操作的動作、動作涉及數據、附加數據進行描述,Android系統則根據此Intent的描述,負責找到對應的組件,將Intent傳遞給調用的組件,并完成組件的調用。
Android應用本地拒絕服務漏洞源于程序沒有對Intent.GetXXXExtra()獲取的異常或者畸形數據處理時沒有進行異常捕獲,從而導致攻擊者可通過向受害者應用發送此類空數據、異常或者畸形數據來達到使該應用Crash的目的,簡單的說就是攻擊者通過Intent發送空數據、異常或畸形數據給受害者應用,導致其崩潰。
風險等級:`低危`
問題示例:
~~~
//源于程序沒有對getAction()等獲取到的數據進行空指針判斷,從而導致了空指針異常導致應用崩潰
Intent i = new Intent();
try {
if (i.getAction().equals("TestForNullPointerException")) {
Log.d("TAG", "Test for Android Refuse Service Bug");
}
} catch Exception {
}
Intent abc = new Intent();
Intent kkk = new Intent();
if (abc.getAction().equals("TestForNullPointerException")) {
Log.d("TAG", "Test for Android Refuse Service Bug");
}
//源于程序沒有對getSerializableExtra()等獲取到的數據進行類型判斷而進行強制類型轉換,從而導致類型轉換異常導致拒絕服務漏洞
Intent a = getIntent();
try {
String test = (String) a.getSerializableExtra("serializable_key");
} catch Exception {
//針對異常進行操作
}
//源于程序沒有對getIntegerArrayListExtra()等獲取到的數據數組元素大小判斷,導致數組訪問越界而造成拒絕服務漏洞
Intent intent = getIntent();
ArrayList<Integer> intArray = intent.getIntegerArrayListExtra("user_id");
if (intArray != null) {
for (int i = 0; i < 10; i++) {
intArray.get(i);
}
}
//源于程序沒有找到從getSerializableExtra()獲取到的序列化對象的類定義,因此導致發生類未定義的異常導致拒絕服務漏洞
a.getSerializableExtra("key");
~~~
## 9 manifest中定義組件未實現檢測 # 05009
在manifest文件中定義的組件導出,且沒有代碼實現,則攻擊者可以通過ddos攻擊導致app奔潰。
風險等級:`中危`
問題示例:
首先獲取app源碼中所有的類路徑(包名+類名),然后檢測manifest中聲明的所有組件是否存在于類路徑中即可。
建議:
刪除manifest文件中無效的導出組件
## 10 Debug或Test敏感測試組件泄露檢測 # 05010
一些app在正式發布前,為了方便調試app,都會在app里集成一些調試或測試界面。這些測試界面可能包含敏感的信息。
風險等級:`低危`或`中危`
問題示例:
遍歷manifest文件中的所有組件名稱,找出所有帶有debug或test等測試字樣的關鍵字組件,并根據組件的intent-filter屬性構造intent發送讓組件彈出進行檢測
建議:
在正式發布前移除所有的測試組件
## 11 Intent不安全反射風險檢測 # 05011
通過Intent接收的Extra參數來構造反射對象會導致從不受信任的源加載類。攻擊者可以通過巧妙地構造達到加載其它類的目的。
風險等級:`低危`
問題示例:
Step1:檢測出導出的組件
Step2:在導出的組件下,檢測兩個關鍵函數,分別是:getIntent()和Class.forName("....")
```
public class SecondActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_second);
//這里的intent使用了getStringExtra方法加載了一個名稱為className的類
Intent intent = getIntent();
String className = intent.getStringExtra("className");
String methodName = intent.getStringExtra("methodName");
try {
Class<?> clz = null;
//嘗試以反射的方式構造className的實例
clz = Class.forName(className);
Date object = (Date) clz.newInstance();
Method method = clz.getMethod(methodName);
Toast.makeText(getApplicationContext(), method.invoke(object, null) + "======", Toast.LENGTH_LONG).show();
} catch (Exception e) {
e.printStackTrace();
}
}
}
```
逆向后對應的smali代碼如下:
```
...
invoke-virtual {p0}, Lcom/bug/intent/reflection/SecondActivity;->getIntent()Landroid/content/Intent;
...
invoke-static {v0}, Ljava/lang/Class;->forName(Ljava/lang/String;)Ljava/lang/Class;
...
```
建議:
* 不要通過Intent接收的Extra傳播的反射函數
* 將接受反射的組件設置為非導出組件