滲透測試-數(shù)據(jù)存儲

以O(shè)WASP移動應(yīng)用安全認(rèn)證標(biāo)準(zhǔn)和移動安全測試指導(dǎo)為基礎(chǔ),這個檢測列表是為了設(shè)計、測試和發(fā)行安全的Android應(yīng)用的安全考慮。

翻譯自:https://github.com/JinxKing/android_app_security_checklist

數(shù)據(jù)存儲

保護(hù)認(rèn)證憑據(jù)、私有信息和其他敏感信息是移動安全的關(guān)鍵。在這一章中,將學(xué)習(xí)Android為本地數(shù)據(jù)存儲提供的API,并進(jìn)行練習(xí)使用。

數(shù)據(jù)存儲總結(jié)起來比較簡單:公共數(shù)據(jù)應(yīng)該是對所有人可用,敏感數(shù)據(jù)和私有數(shù)據(jù)必須被保護(hù),或者設(shè)備存儲之外。

注意敏感數(shù)據(jù)的含義取決于app的處理。數(shù)據(jù)分類會在“移動App安全測試”一章中的“敏感數(shù)據(jù)認(rèn)證”具體描述。

測試敏感數(shù)據(jù)的本地存儲

傳統(tǒng)的建議是盡量少的敏感數(shù)據(jù)永久存儲本地。在大部分實際場景中,然而,一些數(shù)據(jù)類型必須被存儲。比如,當(dāng)每次app啟動時需要用戶輸入一個非常復(fù)雜的密碼,在使用上這不是一個好的做法。大部分app必須本地緩存一些認(rèn)證信息來避免這種情況。如果一個特定場景需要,私人認(rèn)證信息和其他類型的敏感數(shù)據(jù)應(yīng)該被存儲下來。

當(dāng)必須永久存儲但不被app合適的保護(hù),敏感數(shù)據(jù)是危險的。app可能把數(shù)據(jù)存儲在好幾個地方,比如設(shè)備中或者外部SD卡。當(dāng)你嘗試探索這些問題,考慮這些信息可能被處理并存儲在不同的地方。認(rèn)證信息比較有價值,可能會被攻擊。

公開敏感信息有一系列影響。一般來說,一個攻擊者認(rèn)證信息并且用于其他的攻擊,例如社會工程、賬戶劫持、從有支付選項的app中聚集信息。

存儲數(shù)據(jù)對于很多移動app來說是基本。數(shù)據(jù)可以用很多方法永久存儲。下面這些方法被廣泛使用:

  • Shared Preference
  • SQLite Databases
  • Realm Databases
  • Internet Storage
  • External Storage

Shared Preference

Shared Preference API經(jīng)常用來永久存儲鍵值對。數(shù)據(jù)一般被寫在一個解釋文件.xml中。Shared Preference對象可以聲明對所有app公開,也可以聲明私有。誤用經(jīng)常導(dǎo)致敏感信息的暴露。例子:

SharedPreferences sharedPref = getSharedPreferences("key", MODE_WORLD_READABLE);
SharedPreferences.Editor editor = sharedPref.edit();
editor.putString("username", "administrator");
editor.putString("password", "supersecret");
editor.commit();

一旦activity被調(diào)用,文件key.xml會被創(chuàng)建。

  • 用戶名和密碼會被存儲在/data/data/<package-name>/shared_prefs/key.xml
<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
<map>
  <string name="username">administrator</string>
  <string name="password">supersecret</string>
</map>

  • MODE_WORLD_READABLE允許所有應(yīng)用訪問并獲取key.xml的內(nèi)容
root@hermes:/data/data/sg.vp.owasp_mobile.myfirstapp/shared_prefs # ls -la
-rw-rw-r-- u0_a118    170 2016-04-23 16:51 key.xml

注意:MODE_WORLD_READABLEMODE_WORLD_WRITEABLE被API 17(Android 4.2)的應(yīng)用棄用。API 17以下的應(yīng)用會被影響。

SQLite DataBase(Unencrypted)

用庫android.database.sqlite,存儲在SQLite,代碼如下:

SQLiteDatabase notSoSecure = openOrCreateDatabase("privateNotSoSecure",MODE_PRIVATE,null);
notSoSecure.execSQL("CREATE TABLE IF NOT EXISTS Accounts(Username VARCHAR, Password VARCHAR);");
notSoSecure.execSQL("INSERT INTO Accounts VALUES('admin','AdminPass');");
notSoSecure.close();

調(diào)用后,privateNotSoSecure文件會被創(chuàng)建,路徑如下:/data/data/<package-name>/databases/privateNotSoSecure

數(shù)據(jù)庫目錄下除了數(shù)據(jù)庫外還包含文件如下:

  • Journal files.用于提交和回滾的臨時文件
  • Lock file.鎖定和日志功能的一部分,設(shè)計用于提高數(shù)據(jù)庫的并發(fā)和減少寫入饑餓問題。

SQLite DataBase(Encrypted)

用庫SQLCipher,SQLite會被加密

如果適用加密數(shù)據(jù)庫,確定密碼是否硬編碼在資源中(SharePreference),或者隱藏在代碼或者文件系統(tǒng)等地方。取得密鑰的安全方法包括:

  • 當(dāng)app打開時,詢問用戶加密數(shù)據(jù)庫適用PIN或者密碼
  • 存儲密鑰在服務(wù)器中并且只允許從web服務(wù)中訪問

Realm Dtabases

Realm DataBase for Java在開發(fā)者中非常受歡迎,數(shù)據(jù)庫和內(nèi)容用存儲在配置文件中的密鑰加密。

//the getKey() method either gets the key from the server or from a Keystore, or is deferred from a password.
RealmConfiguration config = new RealmConfiguration.Builder()
  .encryptionKey(getKey())
  .build();

Realm realm = Realm.getInstance(config);

如果數(shù)據(jù)庫沒有被加密,可以獲得數(shù)據(jù)。如果被加密,確定密鑰是否被硬編碼在source或者resources中,是否存儲在SharePreference或者其他位置。

Internal Storage

如果存儲在設(shè)備的內(nèi)部存儲中,不能被設(shè)備中的其他app訪問。當(dāng)用戶卸載app,這些文件會被移除。下面的代碼會永久的把數(shù)據(jù)保存在內(nèi)部存儲中。

FileOutputStream fos = null;
try {
   fos = openFileOutput(FILENAME, Context.MODE_PRIVATE);
   fos.write(test.getBytes());
   fos.close();
} catch (FileNotFoundException e) {
   e.printStackTrace();
} catch (IOException e) {
   e.printStackTrace();
}

檢查文件模式確認(rèn)只有app能訪問文件,設(shè)置為MODE_PRIVATE,MODE_WORLD_READABLEMODE_WORLD_WRITEABLE(都被棄用)會有安全風(fēng)險。

搜索類FileInputStream,查找出app內(nèi)那些文件被讀取。

外部存儲

一些Android設(shè)備支持外部存儲,可以使用下列代碼在外部存儲中存儲敏感信息。

File file = new File (Environment.getExternalFilesDir(), "password.txt");
String password = "SecretPassword";
FileOutputStream fos;
    fos = new FileOutputStream(file);
    fos.write(password.getBytes());
    fos.close();

代碼調(diào)用時,會創(chuàng)建文件password.txt,而且卸載時不會刪除。

靜態(tài)分析

本地存儲

檢查資源:

  • 檢查AndroidManifest.xml讀取外部存儲權(quán)限,比如,uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
  • 檢查源代碼中的關(guān)鍵字和API調(diào)用,是否被用來存儲數(shù)據(jù)
    • 文件權(quán)限,比如:
      • MODE_WORLD_READABLEMODE_WORLD_WRITABLE:應(yīng)該避免使用MODE_WORLD_READABLEMODE_WORLD_WRITABLE,因為任何app都能從讀取文件。計時存儲在私有數(shù)據(jù)目錄下,如果數(shù)據(jù)必須與其他應(yīng)用分享,比如content provider。content provider為其他應(yīng)用提供了讀和寫權(quán)限,能動態(tài)授權(quán)
    • 類和函數(shù),比如:
      • SharedPreference類(存儲鍵值對)
      • FileOutputStream類(內(nèi)部存儲和外部存儲)
      • getExternal*函數(shù)(使用外部存儲)
      • getWritableDatabase函數(shù)(寫數(shù)據(jù)庫)
      • getReadableDatabase函數(shù)(讀數(shù)據(jù)庫)
      • getCacheDirgetExternalCacheDirs函數(shù)(使用cache文件) 加密應(yīng)該使用經(jīng)過驗證的SDK函數(shù)實現(xiàn)。下面描述了源代碼中不好的實踐。
  • 存儲通過簡單變換的的“加密”敏感信息。
  • 在沒有Android系統(tǒng)的特性的情況下使用或創(chuàng)建的密鑰,比如Android密鑰存儲庫
  • 密鑰被硬編碼暴露

典型的誤用:硬編碼的加密密鑰 對稱密鑰存儲在設(shè)備上,還原數(shù)據(jù)只是時間問題了??紤]下面的代碼:

this.db = localUserSecretStore.getWritableDatabase("SuperPassword123");

獲取密鑰是微不足道的,因為它包含在源代碼中,并且對于應(yīng)用程序的所有安裝都是相同的。以這種方式加密數(shù)據(jù)是沒有好處的。尋找硬編碼的API密鑰/私鑰和其他有價值的數(shù)據(jù);他們也有類似的風(fēng)險。解密/加密密鑰代表了另一種試圖讓它變得更困難但并非不可能獲得的嘗試。

考慮下面的代碼:

//A more complicated effort to store the XOR'ed halves of a key (instead of the key itself)
private static final String[] myCompositeKey = new String[]{
  "oNQavjbaNNSgEqoCkT9Em4imeQQ=","3o8eFOX4ri/F8fgHgiy/BS47"
};

解密的算法可能如下:

public void useXorStringHiding(String myHiddenMessage) {
  byte[] xorParts0 = Base64.decode(myCompositeKey[0],0);
  byte[] xorParts1 = Base64.decode(myCompositeKey[1],0);

  byte[] xorKey = new byte[xorParts0.length];
  for(int i = 0; i < xorParts1.length; i++){
    xorKey[i] = (byte) (xorParts0[i] ^ xorParts1[i]);
  }
  HidingUtil.doHiding(myHiddenMessage.getBytes(), xorKey, false);
}

驗證密鑰的公共地址:

  • resources(特別是在 res/values/strings.xml)

Example:

<resources>
    <string name="app_name">SuperApp</string>
    <string name="hello_world">Hello world!</string>
    <string name="action_settings">Settings</string>
    <string name="secret_key">My_Secret_Key</string>
  </resources>

  • 配置信息,比如:local.properties 或者 gradle.properties Example:
buildTypes {
  debug {
    minifyEnabled true
    buildConfigField "String", "hiddenPassword", "\"${hiddenPassword}\""
  }
}

KeyStore

Android KeyStore支持相關(guān)安全信任存儲。從Android 4.3,為存儲和使用app私有密鑰提供公共AP。app使用一個公鑰創(chuàng)建一個新的私鑰/公鑰對來加密數(shù)據(jù),用私鑰解密。

使用用戶認(rèn)證能保護(hù)密鑰存儲在Android KeyStore,用戶的鎖屏憑據(jù)用來認(rèn)證。

你能在兩種模式中使用密鑰:

  1. 在授權(quán)后的一段時間內(nèi),用戶才能使用密鑰。在這種模式中,一旦用戶解鎖設(shè)備所有密鑰能被使用。可以為每個密鑰制定認(rèn)證時間。只有當(dāng)用戶啟用了安全鎖屏,可以使用這個選項。如果用戶沒有啟用安全鎖屏,所有密鑰會永久失效
  2. 用戶被授權(quán)使用與一個密鑰相關(guān)聯(lián)的特定加密操作。在這種模式下,用戶必須為涉及到密鑰的每個操作請求一個單獨(dú)的授權(quán)。目前,指紋認(rèn)證是請求這種授權(quán)的唯一方法。

由Android KeyStore承擔(dān)的安全等級取決于設(shè)備的執(zhí)行。大部分的設(shè)備提供硬件備份KeyStore:密鑰在一個可信的執(zhí)行環(huán)境或者安全的環(huán)境中生成和使用,操作系統(tǒng)不能直接訪問他們。意味著加密密鑰不能被簡單地得到,甚至是root設(shè)備。

KeyChain KeyChain類用于存儲和恢復(fù)系統(tǒng)范圍的私鑰和相關(guān)證書。如果某些重要的東西第一次存儲, 用戶需要配置鎖屏pin或者密碼保護(hù)存儲。KeyChain是系統(tǒng)范圍的,所有app能訪問存儲在keystore中的內(nèi)容

檢查源代碼確定原生Android機(jī)制是否確認(rèn)敏感信息。敏感信息應(yīng)該加密,不能明文存儲。如果敏感信息必須被存儲在設(shè)備上,一些KeyStore的API調(diào)用可以用來保護(hù)數(shù)據(jù)。完整步驟:

  • 確定app使用KeyStore和Cipher機(jī)制在設(shè)備上安全存儲加密信息。查詢模型import java.security.KeyStore, import javax.crypto.Cipher, import java.security.SecureRandom,佳偶皮相關(guān)使用。
  • 調(diào)用函數(shù)store(OutputStream stream, char[] password)并使用密碼存儲KeyStore在磁盤上。確認(rèn)密碼由用戶提供,不是硬編碼。

動態(tài)分析

安裝和使用app,至少執(zhí)行過所有功能一次。當(dāng)用戶輸入后數(shù)據(jù)會生成,端點輸出,或者app運(yùn)送,完整如下:

  • 認(rèn)證開發(fā)文件,備份文件和不應(yīng)該包含在發(fā)行中的老文件
  • 確認(rèn)SQLite 數(shù)據(jù)庫可用,是否包含敏感信息。SQLite存儲在/data/data/<package-name>/databases
  • 檢查SharedPreference存儲的xml文件(/data/data/<package-name>/shared_prefs)是否由敏感信息。
  • 檢查文件的權(quán)限(rwx)
  • Realm數(shù)據(jù)庫是否可用/data/data/<package-name>/files/,是否加密,是否包含敏感信息。默認(rèn)文件擴(kuò)展名是realm文件名是default
  • 檢查外部存儲中是否由敏感信息

敏感數(shù)據(jù)的測試日志

日志中的敏感信息可能會暴露給攻擊者或者惡意應(yīng)用。使用日志的兩個類:

  • Log Class
  • Logger Class

使用日志機(jī)制,需要從生產(chǎn)版本中刪除日志記錄。

靜態(tài)分析

檢查app的源代碼的日志,搜查關(guān)鍵字:

  • 函數(shù)和類,比如:
    • android.util.Log
    • Log.d|Log.e|Log.i|Log.v|Log.wtf
    • Logger
  • 關(guān)鍵字和系統(tǒng)輸出:
    • System.out.print|System.out.print
    • logfile
    • logs
  • 當(dāng)準(zhǔn)備發(fā)行版本時,可以使用ProGuard(包含在Android Studio中)刪除相關(guān)代碼。檢查是否所有的andriod.util.Log類和函數(shù)都被移除了,檢查ProGuard的配置文件(proguard-project.txt)選項:
-assumenosideeffects class android.util.Log
{
  public static boolean isLoggable(java.lang.String, int);
  public static int v(...);
  public static int i(...);
  public static int w(...);
  public static int d(...);
  public static int e(...);
  public static int wtf(...);
}

Log.v("Private key [byte format]: " + key);
Log.v(new StringBuilder("Private key [byte format]: ").append(key.toString()).toString());

ProGuard能消除Log.v方法調(diào)用,new StringBuilder會不會消除取決于ProGuard的版本。

這是一種安全風(fēng)險,因為(未使用的)字符串將純文本數(shù)據(jù)泄漏到內(nèi)存中,這可以通過調(diào)試器或內(nèi)存轉(zhuǎn)儲來訪問。

這個問題上的一些選擇:

  • 執(zhí)行一個定制的日志工具,能接收簡單的參數(shù),并在內(nèi)部構(gòu)造日志語句 SecureLog.v("Private key [byte format]: ", key);

然后配置ProGuard取消調(diào)用

  • 在源碼中移除日志而不是bytecode中,下面是簡單的gradle任務(wù):
afterEvaluate {
  project.getTasks().findAll { task -> task.name.contains("compile") && task.name.contains("Release")}.each { task ->
      task.dependsOn('removeLogs')
  }

  task removeLogs() {
    doLast {
      fileTree(dir: project.file('src')).each { File file ->
        def out = file.getText("UTF-8").replaceAll("((android\\.util\\.)*Log\\.([ewidv]|wtf)\\s*\\([\\S\\s]*?\\)\\s*;)", "/*\$1*/")
        file.write(out);
      }
    }
  }
}

動態(tài)分析

一次使用app所有的功能,然后識別應(yīng)用數(shù)據(jù)目錄并找到日志文件/data/data/<package-name>.檢查應(yīng)用日志決定日志數(shù)據(jù)是否生成,一些移動應(yīng)用生成和存儲到這個目錄下。

很多應(yīng)用開發(fā)者仍然使用System.out.println或者printStackTrace而不是一個合適的日志類。因此當(dāng)應(yīng)用開啟、運(yùn)行和關(guān)閉,測試策略必須包括所有輸出生成。檢查System.out.println或者printStackTrace輸出了什么內(nèi)容,可以使用Logcat。兩種方法執(zhí)行Logcat:

  • Logcat是DDMS和Android Studio的一部分。如果app在debug模式中運(yùn)行,日志輸出會顯示在Logcat tab.可以在Logcat中通過定義模式過濾app的日志。
  • 可以用adb執(zhí)行并存儲日志: $ adb logcat > logcat.log

檢查敏感信息是否發(fā)送給第三方

overview

可以在app中嵌入第三方服務(wù),這些服務(wù)可以執(zhí)行追蹤服務(wù),監(jiān)督用戶行為,販賣橫幅廣告,提高用戶體驗等。

缺點是缺少可視化:無法知道第三方庫指定了什么代碼。因此,你應(yīng)該確定唯一一件事,就是沒有敏感信息能被發(fā)送給服務(wù)。

大部分第三方服務(wù)以兩種方法執(zhí)行:

  • 標(biāo)準(zhǔn)庫,比如一個包含在APK中的Android工程jar
  • a full apk

靜態(tài)分析

通過一個IDE向?qū)С绦蚧蛘呤展ぬ砑右粋€庫或者SDK可以自動整合第三方庫。這種情況下,檢查AndroidManifest.xml中是否能訪問SMS,contacts,location是非常有必要的。

檢查源代碼中的API調(diào)用和第三方庫函數(shù)或者SDK。為安全最佳實踐檢查代碼更改。

檢查加載庫確定是否有必要加載、過期或者包含未知的漏洞

所有數(shù)據(jù)發(fā)送給第三方服務(wù)應(yīng)該匿名。被跟蹤到用戶賬戶或者會話的數(shù)據(jù)不發(fā)送給第三方。

動態(tài)測試

檢查所有對嵌入式敏感信息的外部服務(wù)請求。 為了攔截客戶端和服務(wù)器之間的通信,可以通過啟動一個中間人(MITM)攻擊,使用Burp suite或OWASP ZAP來執(zhí)行動態(tài)分析。 一旦通過攔截代理路由流量,就可以嘗試嗅探應(yīng)用程序和服務(wù)器之間的流量。 所有不直接發(fā)送到主函數(shù)主機(jī)的應(yīng)用程序請求都應(yīng)該檢查敏感信息,比如跟蹤器或廣告服務(wù)中的PII。

檢查鍵盤緩存對字符輸入不可用

overview

當(dāng)用戶在輸入時,軟件自動建議數(shù)據(jù)。這個特點對于消息應(yīng)用非常有用。然而,當(dāng)用戶選擇一個輸入框時,鍵盤緩存可能暴露敏感信息。

靜態(tài)分析

在一個activity的布局定義中,可以定義有XML屬性的TextViews。如果XML屬性android:inputType給了值textNoSuggestions,當(dāng)輸入被選定時鍵盤緩存不顯示。用戶將必須手工輸入所有東西。

   <EditText
        android:id="@+id/KeyBoardCache"
        android:inputType="textNoSuggestions"/>

動態(tài)分析

打開app點擊輸入框。

在剪切板中尋找敏感信息

overview

當(dāng)用戶在輸入框輸入數(shù)據(jù)時,他們應(yīng)該使用剪切板來復(fù)制和粘貼數(shù)據(jù)。設(shè)備中的app分享剪切板,所有惡意應(yīng)用可以訪問敏感數(shù)據(jù)。

靜態(tài)分析

識別接收敏感信息的輸入字段,以及降低剪貼板訪問風(fēng)險的對策。重寫輸入字段函數(shù)是一種通用的最佳實踐,它禁用了這些函數(shù)的剪貼板。

EditText  etxt = (EditText) findViewById(R.id.editText1);
etxt.setCustomSelectionActionModeCallback(new Callback() {

            public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
                return false;
            }

            public void onDestroyActionMode(ActionMode mode) {                  
            }

            public boolean onCreateActionMode(ActionMode mode, Menu menu) {
                return false;
            }

            public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
                return false;
            }
        });

longclickable在輸入域中設(shè)置不可用

android:longClickable="false"

動態(tài)分析

啟動app的,點擊接收敏感信息的輸入域。如果顯示了復(fù)制/粘貼選項,說明剪切板功能沒有被禁用。

可以使用Drozer模塊post.capture.clipboard從剪切板中提取數(shù)據(jù)。

dz> run post.capture.clipboard
[*] Clipboard value: ClipData.Item { T:Secretmessage }

檢查敏感信息在IPC機(jī)制中是否暴露

overview

content provider作為Android IPC機(jī)制的一部分,允許app的存儲數(shù)據(jù)被其他應(yīng)用訪問和修改。如果配置不合適,這些機(jī)制可能會泄露

筆者注:intent中的敏感信息容易泄露

靜態(tài)分析

第一個補(bǔ)助是查看AndroidManifest.xml,檢測content provider是否暴露。依據(jù)元素<provider>識別content providers。完整步驟:

  • 檢查屬性android:exported="true"
  • 檢測數(shù)據(jù)是否被權(quán)限(android:permission)保護(hù),權(quán)限限制了暴露程度。
  • 決定android:protectionLevel屬性是否有值signature。這個設(shè)置是指數(shù)據(jù)只能被相同企業(yè)的app訪問(被相同的密鑰簽名)。
  • 如果適用了android:permission,其他應(yīng)用必須聲明一致<uses-permission>??梢允褂?code>android:grantUriPermissions屬性授權(quán)更明確的訪問,還可以用使用<grant-uri-permission>元素限制訪問。

檢查源代碼理解content provider是如何被使用的。搜索關(guān)鍵字:

  • android.content.ContentProvider
  • android.database.Cursor
  • android.database.sqlite
  • .query
  • .update
  • .delete

為了避免SQL注入攻擊,使用參數(shù)化的查詢方法,比如query, update,以及 delete. 確保對所有方法的參數(shù)進(jìn)行適當(dāng)?shù)奶幚恚槐热?,如果全部由用戶輸入組成參數(shù),selection會引起SQL注入。

檢查AndroidManifest

識別所有<provider>元素:

<provider android:authorities="com.mwr.example.sieve.DBContentProvider" android:exported="true" android:multiprocess="true" android:name=".DBContentProvider">
    <path-permission android:path="/Keys" android:readPermission="com.mwr.example.sieve.READ_KEYS" android:writePermission="com.mwr.example.sieve.WRITE_KEYS"/>
</provider>
<provider android:authorities="com.mwr.example.sieve.FileBackupProvider" android:exported="true" android:multiprocess="true" android:name=".FileBackupProvider"/>

以上例子中,有兩個暴露的content provider組件。注意一條路徑("/key")被讀和寫權(quán)限保護(hù)。

檢查源代碼

檢查有沒有敏感信息泄露。

public Cursor query(final Uri uri, final String[] array, final String s, final String[] array2, final String s2) {
    final int match = this.sUriMatcher.match(uri);
    final SQLiteQueryBuilder sqLiteQueryBuilder = new SQLiteQueryBuilder();
    if (match >= 100 && match < 200) {
        sqLiteQueryBuilder.setTables("Passwords");
    }
    else if (match >= 200) {
        sqLiteQueryBuilder.setTables("Key");
    }
    return sqLiteQueryBuilder.query(this.pwdb.getReadableDatabase(), array, s, array2, (String)null, (String)null, s2);
}

這里實際上有兩條路徑,"Keys"和"Passwords",后者沒有被保護(hù)。

當(dāng)訪問URI時,查詢聲明返回所有密碼和路徑Passwords/。我們將會在動態(tài)分析中定位這個問題,顯示需要的URI

動態(tài)分析

測試內(nèi)容提供器

動態(tài)分析一個應(yīng)用content provider,先遍歷攻擊面:將應(yīng)用包名傳遞給Drozer模塊app.provider.info

dz> run app.provider.info -a com.mwr.example.sieve
  Package: com.mwr.example.sieve
  Authority: com.mwr.example.sieve.DBContentProvider
  Read Permission: null
  Write Permission: null
  Content Provider: com.mwr.example.sieve.DBContentProvider
  Multiprocess Allowed: True
  Grant Uri Permissions: False
  Path Permissions:
  Path: /Keys
  Type: PATTERN_LITERAL
  Read Permission: com.mwr.example.sieve.READ_KEYS
  Write Permission: com.mwr.example.sieve.WRITE_KEYS
  Authority: com.mwr.example.sieve.FileBackupProvider
  Read Permission: null
  Write Permission: null
  Content Provider: com.mwr.example.sieve.FileBackupProvider
  Multiprocess Allowed: True
  Grant Uri Permissions: False

在這個例子中兩個content provider是暴露的。沒有權(quán)限就能訪問,除了DBContentProvider中的/Keys路徑。用這些信息,可以重構(gòu)部分內(nèi)容URIs訪問DBContentProvider(content://...)

識別應(yīng)用中content provider URIs ,可以使用Drozer的scanner.provider.finduris模塊。這個模塊猜測路徑確定訪問URIs:

dz> run scanner.provider.finduris -a com.mwr.example.sieve
Scanning com.mwr.example.sieve...
Unable to Query content://com.mwr.example.sieve.DBContentProvider/
...
Unable to Query content://com.mwr.example.sieve.DBContentProvider/Keys
Accessible content URIs:
content://com.mwr.example.sieve.DBContentProvider/Keys/
content://com.mwr.example.sieve.DBContentProvider/Passwords
content://com.mwr.example.sieve.DBContentProvider/Passwords/

一旦你有可以訪問的 content providers表,就可以通過app.provider.query從每個provider中提取數(shù)據(jù):

dz> run app.provider.query content://com.mwr.example.sieve.DBContentProvider/Passwords/ --vertical
_id: 1
service: Email
username: incognitoguy50
password: PSFjqXIMVa5NJFudgDuuLVgJYFD+8w== (Base64 - encoded)
email: incognitoguy50@gmail.com

k可以使用Drozer插入,更新,刪除記錄:

  • 插入
dz> run app.provider.insert content://com.vulnerable.im/messages
                --string date 1331763850325
                --string type 0
                --integer _id 7

  • 更新
dz> run app.provider.update content://settings/secure
                --selection "name=?"
                --selection-args assisted_gps_enabled
                --integer value 0

  • 刪除
dz> run app.provider.delete content://settings/secure
                --selection "name=?"
                --selection-args my_setting

SQL注入

Android平臺推薦SQLite數(shù)據(jù)庫存儲用戶數(shù)據(jù)。因為數(shù)據(jù)庫基于SQL,存在SQL注入漏洞??梢允褂肈rozer模塊app.provier.query測試SQL注入:

dz> run app.provider.query content://com.mwr.example.sieve.DBContentProvider/Passwords/ --projection "'"
unrecognized token: "' FROM Passwords" (code 1): , while compiling: SELECT ' FROM Passwords

dz> run app.provider.query content://com.mwr.example.sieve.DBContentProvider/Passwords/ --selection "'"
unrecognized token: "')" (code 1): , while compiling: SELECT * FROM Passwords WHERE (')

如果一個應(yīng)用存在sql注入,它會返回一個冗長的錯誤信息。Android中的SQL注入可能會修改或者查詢有漏洞的content provider中的數(shù)據(jù)。下列例子中,Drozer模塊app.provider.query用來列出所有的數(shù)據(jù)庫

dz> run app.provider.query content://com.mwr.example.sieve.DBContentProvider/Passwords/ --projection "*
FROM SQLITE_MASTER WHERE type='table';--"
| type  | name             | tbl_name         | rootpage | sql              |
| table | android_metadata | android_metadata | 3        | CREATE TABLE ... |
| table | Passwords        | Passwords        | 4        | CREATE TABLE ... |
| table | Key              | Key              | 5        | CREATE TABLE ... |

SQL注入也可以用于從其他受保護(hù)的表中檢索數(shù)據(jù):

dz> run app.provider.query content://com.mwr.example.sieve.DBContentProvider/Passwords/ --projection "* FROM Key;--"
| Password | pin |
| thisismypassword | 9876 |

還可以使用scanner.provider.injection模塊自動進(jìn)行尋找content provider內(nèi)容:

dz> run scanner.provider.injection -a com.mwr.example.sieve
Scanning com.mwr.example.sieve...
Injection in Projection:
  content://com.mwr.example.sieve.DBContentProvider/Keys/
  content://com.mwr.example.sieve.DBContentProvider/Passwords
  content://com.mwr.example.sieve.DBContentProvider/Passwords/
Injection in Selection:
  content://com.mwr.example.sieve.DBContentProvider/Keys/
  content://com.mwr.example.sieve.DBContentProvider/Passwords
  content://com.mwr.example.sieve.DBContentProvider/Passwords/

基于Content Provider的文件系統(tǒng)

content provider能提供文件系統(tǒng)的入口。這允許app來分享文件(Android沙箱一般會限制)??梢允褂肈rozer模塊app.provider.readapp.provider.download從基于文件的content provider分別讀取和下載文件。這些content providers容易受到目錄遍歷的影響,它允許在目標(biāo)應(yīng)用程序的沙箱中讀取其他受保護(hù)的文件。

dz> run app.provider.download content://com.vulnerable.app.FileProvider/../../../../../../../../data/data/com.vulnerable.app/database.db /home/user/database.db
Written 24488 bytes

使用scanner.provider.traversal模塊自動尋找容易受目錄遍歷影響的content provider

dz> run scanner.provider.traversal -a com.mwr.example.sieve
Scanning com.mwr.example.sieve...
Vulnerable Providers:
  content://com.mwr.example.sieve.FileBackupProvider/
  content://com.mwr.example.sieve.FileBackupProvider

adb查詢content provider

$ adb shell content query --uri content://com.owaspomtg.vulnapp.provider.CredentialProvider/credentials
Row: 0 id=1, username=admin, password=StrongPwd
Row: 1 id=2, username=test, password=test
...

通過用戶接口檢查敏感數(shù)據(jù)暴露

overview

許多應(yīng)用程序要求用戶輸入幾種類型的數(shù)據(jù),例如,注冊一個賬戶或支付款項。 如果應(yīng)用程序不能正確地屏蔽它,當(dāng)在明文顯示數(shù)據(jù)時,敏感數(shù)據(jù)可能會被暴露出來。

對敏感數(shù)據(jù)的屏蔽,通過顯示星號或圓點而不是清晰的文本,應(yīng)該在應(yīng)用程序的活動中執(zhí)行,以防止信息披露和減少諸如肩沖浪等風(fēng)險。

靜態(tài)分析

為了確保應(yīng)用程序屏蔽敏感的用戶輸入,請在EditText的定義中檢查以下屬性:android:inputType="textPassword" 有了這個設(shè)置,dots(而不是輸入字符)將顯示在文本字段中,防止應(yīng)用程序?qū)⒚艽a或pin泄漏到用戶界面。

動態(tài)分析

...

測試敏感數(shù)據(jù)的備份

Android提供自動備份,復(fù)制數(shù)據(jù)和所有app的已安裝的設(shè)置信息。存儲敏感信息的app可能會通過數(shù)據(jù)備份泄露信息。

Android備份選項:

  • Android有內(nèi)置的USB備份設(shè)備。進(jìn)入調(diào)試模式,使用adbadb backup命令穿件完整數(shù)據(jù)備份。
  • Google提供了備份到google服務(wù)器上
  • 兩個備份api:
    • 鍵值對備份,上傳Android備份服務(wù)云。
    • 應(yīng)用程序自動備份。(Android 6.0)
  • OEM提供更多選擇

靜態(tài)分析

本地

AndroidManifest.xml文件中,提供屬性allowBackup。如果設(shè)置為true,就可以被adb備份。

不可備份,則設(shè)置為false。默認(rèn)true。

檢查AndroidManifest.xml中的代碼:

android:allowBackup="true"

無論使用鍵值對備份還是自動備份,必須注意以下事項:

  • 哪個文件被發(fā)送到云上

  • 哪個文件包含敏感信息

  • 發(fā)送到云上的敏感信息是否被加密

  • 自動備份:通過配置allowBackup屬性自動備份。執(zhí)行備份代理時,通過android:fullBackupOnly激活自動備份。

  • 鍵值對備份:為了確保鍵值對備份,必須在manifest文件中定義備份代理:

android:backupAgent

動態(tài)分析

adb拉取備份

$ adb backup -apk -nosystem <package-name>

獲得備份文件轉(zhuǎn)格式

$ dd if=mybackup.ab bs=24 skip=1|openssl zlib -d > mybackup.tar

如果報錯openssl:Error: 'zlib' is an invalid command,用python代替:

dd if=backup.ab bs=1 skip=24 | python -c "import zlib,sys;sys.stdout.write(zlib.decompress(sys.stdin.read()))" > backup.tar

備份工具:Android backup extractor

java -jar android-backup-extractor-20160710-bin/abe.jar unpack backup.ab

$ tar xvf mybackup.tar

自動截屏泄露信息

應(yīng)用切換,系統(tǒng)會保存應(yīng)用屏幕截圖作為背景,這會導(dǎo)致安全問題

靜態(tài)分析

如下設(shè)置會使屏幕內(nèi)容為空白

getWindow().setFlags(WindowManager.LayoutParams.FLAG_SECURE,
                WindowManager.LayoutParams.FLAG_SECURE);

setContentView(R.layout.activity_main);

動態(tài)分析

點開看一下....

檢查內(nèi)存中的敏感數(shù)據(jù)

這一屆描述怎樣通過進(jìn)程存內(nèi)存中大數(shù)據(jù)暴露

識別存儲在內(nèi)存中的敏感信息,

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 178,733評論 25 709
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,506評論 19 139
  • 有時候,生活太忙碌,我們只顧低頭疾步前行 有時候,生活太枯燥,我們只是忘了抬頭看看頭頂?shù)奶炜?清晨,正午,傍晚,夜...
    答應(yīng)小姐閱讀 221評論 0 0
  • 感恩今天是新的一天,晨起進(jìn)行了冥想。充滿力量,最近身體不舒服,總想睡覺。進(jìn)而又補(bǔ)充了睡眠。 感恩jj小仙女說中我要...
    花開見佛笑閱讀 243評論 0 0
  • 咳咳?? 之前做的外包項目中要實現(xiàn)一個時間軸,效果類似這種 思路是cell中添加一條線,然后在view中再添加兩個U...
    她說的黃山閱讀 1,636評論 0 2

友情鏈接更多精彩內(nèi)容