Android安全開發之Provider元件安全

NO IMAGE

作者:伊樵、呆狐@阿里聚安全

1 Content Provider元件簡介

Content Provider元件是Android應用的重要元件之一,管理對資料的訪問,主要用於不同的應用程式之間實現資料共享的功能。Content Provider的資料來源不止包括SQLite資料庫,還可以是檔案資料。通過將資料儲存層和應用層分離,Content Provider為各種資料來源提供了一個通用的介面。
圖片描述

建立一個自己的Content Provider需要繼承自ContentProvider抽象類,需要重寫其中的onCreate()、query()、insert()、update()、delete()、getType()六個抽象方法,這些方法實現對底層資料來源的增刪改查等操作。還需在AndroidManifest檔案註冊Content Provider,註冊時指定訪問許可權、exported屬性、authority屬性值等。
圖片描述

其它APP使用ContentResolver物件來查詢和操作Content Provider,此物件具有Content Provider中同名的方法名。這樣其他APP接就可以訪問Content Provider對應的資料來源的底層資料,而無須知道資料的結構或實現。
如何定位到具體的資料?
採用Content Uri,一個Content Uri如下所示:

content://com.jaq.providertest.friendsprovider/friends

它的組成一般分為三部分:
1) content://:作為 content Uri的特殊標識(必須);
2) 權(authority):用於唯一標識這個Content Provider,外部訪問者可以根據這個標識找到它;在AndroidManifest中也配置的有;
3) 路徑(path): 所需要訪問資料的路徑,根據業務而定。

這些內容就不具體展開詳談了,詳見參考1

2 風險簡介

如果在AndroidManifest檔案中將某個Content Provider的exported屬性設定為true,則多了一個攻擊該APP的攻擊點。如果此Content Provider的實現有問題,則可能產生任意資料訪問、SQL隱碼攻擊、目錄遍歷等風險。

2.1 私有許可權定義錯誤導致資料被任意訪問

私有許可權定義經常發生的風險是:定義了私有許可權,但是卻根本沒有定義私有許可權的級別,或者定義的許可權級別不夠,導致惡意應用只要宣告這個許可權就能夠訪問到相應的Content Provider提供的資料,造成資料洩露。

以公開的烏雲漏洞WooYun-2014-57590為例:
某網盤客戶端使用了自己的私有許可權,但是在AndroidManifest中卻沒有定義私有許可權,其它APP只要宣告這個許可權就能訪問此網盤客戶端提供的Provider,從而訪問到使用者資料。
在網盤客戶端的AndroidManifest中註冊Provider時,宣告瞭訪問時需要的讀寫許可權,並且許可權為客戶端自定義的私有許可權:
圖片描述

但是在AndroidManifest中卻沒有見到私有許可權“com.huawei.dbank.v7.provider.DBank.READ_DATABASE”和“com.huawei.dbank.v7.provider.DBank.WRITE_DATABASE”的定義:
圖片描述

反編譯客戶端後檢視到的URI,根據這些可以構造訪問到Provider的URI:
圖片描述

編寫POC
以檢視網盤下載的檔案列表為例,
在POC的AndroidManifest中宣告私有許可權,許可權的保護級別定義為最低階“normal”:
圖片描述

主要程式碼為:
圖片描述

拿到資料庫中儲存的下載列表資料:
圖片描述

對應的資料庫:
圖片描述

這樣任意的惡意應用程式就可以訪問到使用者網盤的上傳、下載記錄,網盤裡面存的檔案列表等隱私資訊。

再以公開的烏雲漏洞wooyun-2013-039697為例:

定義了私有許可權,但是保護等級設定成為了dangerous或者normal,這樣的保護等級對於一些應用的Provide重要性相比保護級低了。
Provider為:
圖片描述

私有許可權“com.renren.mobile.android.permission.PERMISSION_ADD_ACCOUNT”的定義為:
圖片描述

反編譯客戶端,看到AcountProvider對應的實現:
圖片描述

編寫POC:
在AndroidManifest中定義和宣告許可權:
圖片描述

主要程式碼為:
圖片描述

可看到使用者的賬戶資訊,包括uid,手機號,加密後的密碼等:
圖片描述

2.2 本地SQL隱碼攻擊

當Content Provider的資料來源是SQLite資料庫時,如果實現不當,而Provider又是暴露的話,則可能會引發本地SQL隱碼攻擊漏洞。
Content Provider的query( )的方法定義為:
圖片描述

其中引數:

uri:為content Uri,要查詢的資料庫
projection:為要查詢的列名
selection和selectionArgs:要指定查詢條件
sortOrder:查詢結果如何排序

query() 與 SQL 查詢對比如下:
圖片描述

如果query( )中使用的是拼接字串組成SQL語句的形式去查詢底層的SQLite資料庫時,容易發生SQL隱碼攻擊。

以烏雲公開漏洞wooyun-2016-0175294為例:
客戶端的com.sohu.sohuvideo.provider.PlayHistoryProvider的exported屬性為“true”:
圖片描述

反編譯客戶端,追蹤PlayHistoryProvider的實現,發現是用拼接字串形式構造原始的SQL查詢語句:
圖片描述

使用drozer工具,證明漏洞:
圖片描述

2.3 目錄遍歷漏洞

對外暴露的Content Provider實現了openFile()介面,因此其他有相應呼叫該Content Provider許可權的應用即可呼叫Content Provider的openFile()介面進行檔案資料訪問。但是如果沒有進行Content Provider訪問許可權控制和對訪問的目標檔案的Uri進行有效判斷,攻擊者利用檔案目錄遍歷可訪問任意可讀檔案,更有甚者可以往手機裝置可寫目錄中寫入任意資料。

例子1 以烏雲公開漏洞wooyun-2013-044407為例:
此APP實現中定義了一個可以訪問本地檔案的Content Provider元件,為com.ganji.android.jobs.html5.LocalFileContentProvider,因為使用了minSdkServison為“8”,targetSdkVersion=”13”,即此Content Provider採用預設的匯出配置,即android:exported=”true”:
圖片描述

該Provider實現了openFile()介面:
圖片描述

通過此介面可以訪問內部儲存app_webview目錄下的資料,由於後臺未能對目標檔案地址進行有效判斷,可以通過”../”實現目錄遍歷,實現對任意私有資料的訪問。

例子2
某社交應用客戶端,使用了的minSDKVersion為8,定義了私有許可權,並且android:protectionLevel設為了signature
圖片描述

有一個對外暴露的Content Provider,即com.facebook.lite.photo.MediaContentProvider,此Provider沒有設定訪問許可權,而另外一個Provider是設定了訪問許可權的:
圖片描述

在MediaContentProvider中實現了openFile()介面,沒有對傳入的URI進行限制和過濾:
圖片描述

此介面本來只想讓使用者訪問照片資訊的,但是卻可以突破限制,讀取其他檔案:
POC:
圖片描述

讀取到其他檔案的內容為:
圖片描述

另外看到Openfile()介面的實現中,如果要訪問的檔案不存在,就會建立此檔案,還有可能的風險就是在應用的目錄中寫入任意檔案。

3 阿里聚安全開發者建議

在進行APP設計時,要清楚哪些Provider的資料是使用者隱私資料或者其他重要資料,考慮是否要提供給外部應用使用,如果不需要提供,則在AndroidManifes檔案中將其exported屬性顯式的設為“false”,這樣就會減少了很大一部分的攻擊面。
人工排查肯定比較麻煩,建議開發者使用阿里聚安全提供的安全掃描服務,在APP上線前進行自動化的安全掃描,儘早發現並規避這樣的風險。

注意:
由於Android元件Content Provider無法在Android 2.2(即API Level 8)系統上設為不匯出,因此建議宣告最低SDK版本為8以上版本(這已經是好幾年前的SDK了,現在一般都會大於此版本);
由於API level 在17以下的所有應用的“android:exported”屬性預設值都為true,因此如果應用的Content Provider不必要匯出,建議顯式設定註冊的Content Provider元件的“android:exported”屬性為false;
如果必須要有資料提供給外部應用使用,則做好設計,做好許可權控制,明確什麼樣的外部應用可以使用,如對於本公司的應用在許可權定義時用相同簽名即可,合作方的應用檢查其簽名;不過還是儘量不提供使用者隱私敏感資訊。

對於必須暴露的Provider,如第二部分遇到的風險解決辦法如下:

3.1 正確的定義私有許可權

在AndroidManifest中定義私有許可權的語法為:
圖片描述

其中android:protectionLevel的可選值分別表示:

  • normal:預設值,低風險許可權,在安裝的時候,系統會自動授予許可權給 application。

  • dangerous:高風險許可權,如發簡訊,打電話,讀寫通訊錄。使用此protectionLevel來標識使用者可能關注的一些許可權。Android將會在安裝程式時,警示使用者關於這些許可權的需求,具體的行為可能依據Android版本或者所安裝的移動裝置而有所變化。

  • signature: 簽名許可權,在其他 app 引用宣告的許可權的時候,需要保證兩個 app 的簽名一致。這樣系統就會自動授予許可權給第三方

  • app,而不提示給使用者。 signatureOrSystem:除了具有相同簽名的APP可以訪問外,Android系統中的程式有許可權訪問。

大部分開放的Provider,是提供給本公司的其他應用使用的,一般的話一個公司打包簽名APP的簽名證書都應該是一致的,這種情況下,Provider的android:protectionLevel應為設為“signature”。

3.2 防止本地SQL隱碼攻擊

注意:一定不要使用拼接來組裝SQL語句。
如果Content Provider的資料來源是SQLite資料庫,如果使用拼接字串的形式組成原始SQL語句執行,則會導致SQL隱碼攻擊。
如下的選擇子句:
圖片描述

如果執行此操作,則會允許使用者將惡意 SQL 串連到 SQL 語句上。
例如,使用者可以為 mUserInput 輸入“nothing; DROP TABLE ** ; ”,這會生成選擇子句

var = nothing; DROP TABLE **;

由於選擇子句是作為SQL語句處理,因此這可能會導致提供程式擦除基礎 SQLite 資料庫中
的所有表(除非提供程式設定為可捕獲 SQL 注入嘗試)。

使用引數化查詢:
要避免此問題,可使用一個“ ? ” 作為可替換引數的選擇子句以及一個單獨的選擇引數陣列。
執行此操作時,使用者輸入直接受查詢約束,而不解釋為 SQL 語句的一部分。
由於使用者輸入未作為 SQL 處理,因此無法注入惡意 SQL。

請使用此選擇子句,而不要使用串連來包括使用者輸入:

String mSelectionClause = “var = ?”;

按如下所示設定選擇引數陣列:

String[] selectionArgs = {“”};

按如下所示將值置於選擇引數陣列中:

selectionArgs[0] = mUserInput;

還可呼叫SQLiteDatabase類中的引數化查詢query()方法:
圖片描述

3.3 防止目錄遍歷

1、去除Content Provider中沒有必要的openFile()介面。
2、過濾限制跨域訪問,對訪問的目標檔案的路徑進行有效判斷:
使用Uri.decode()先對Content Query Uri進行解碼後,再過濾如可通過“../”實現任意可讀檔案的訪問的Uri字串,如:
圖片描述

3.4 通過檢測簽名來授權合作方應用訪問
如果必須給合作方的APP提供Provider的訪問許可權,而合作方的APP簽名證書又於自己公司的不同,可將合作方的APP的簽名雜湊值預埋在提供Provider的APP中,提供Provider的APP要檢查請求訪問此Provider的APP的簽名,簽名匹配通過才讓訪問。

參考

[1]《內容提供程式基礎知識
https://developer.android.com/guide/topics/providers/content-provider-basics.html
[2]《Android app端的sql注入》http://zone.wooyun.org/content/15097
[3]《Android – Content Providers》 http://www.tutorialspoint.com/android/android_content_providers.htm
[4] http://www.compiletimeerror.com/2013/12/content-provider-in-android.html
[5] https://developer.android.com/guide/topics/manifest/permission-element.html?hl=zh-cn
[6] https://developer.android.com/guide/topics/manifest/permission-element.html
[7] http://www.wooyun.org/bugs/wooyun-2013-039697
[8] http://www.wooyun.org/bugs/wooyun-2014-057590
[9] 《Android Content Provider Security》http://drops.wooyun.org/tips/4314
[10] http://www.wooyun.org/bugs/wooyun-2016-0175294
[11]《Android Content Provider Security
http://drops.wooyun.org/tips/4314
[12] http://www.wooyun.org/bugs/wooyun-2013-044407
[13] http://www.wooyun.org/bugs/wooyun-2013-044411
[14] 《Content Provider檔案目錄遍歷漏洞淺析》,https://jaq.alibaba.com/blog.htm?id=61
[15] https://github.com/programa-stic/security-advisories/tree/master/FacebookLite

作者:伊樵、呆狐@阿里聚安全,更多安全技術文章,請訪問阿里聚安全部落格