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

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

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

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

保護(hù)認(rèn)證憑據(jù)雅倒、私有信息和其他敏感信息是移動安全的關(guān)鍵绅络。在這一章中别惦,將學(xué)習(xí)Android為本地?cái)?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í)際場景中,然而,一些數(shù)據(jù)類型必須被存儲窜司。比如,當(dāng)每次app啟動時需要用戶輸入一個非常復(fù)雜的密碼妹孙,在使用上這不是一個好的做法倦炒。大部分app必須本地緩存一些認(rèn)證信息來避免這種情況。如果一個特定場景需要刁卜,私人認(rèn)證信息和其他類型的敏感數(shù)據(jù)應(yīng)該被存儲下來志电。

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

公開敏感信息有一系列影響蚀乔。一般來說,一個攻擊者認(rèn)證信息并且用于其他的攻擊菲茬,例如社會工程吉挣、賬戶劫持派撕、從有支付選項(xiàng)的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è)計(jì)用于提高數(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_PRIVATEMODE_WORLD_READABLEMODE_WORLD_WRITEABLE(都被棄用)會有安全風(fēng)險(xiǎn)娘扩。

搜索類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,因?yàn)槿魏蝍pp都能從讀取文件减俏。計(jì)時存儲在私有數(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)過驗(yàn)證的SDK函數(shù)實(shí)現(xiàn)历筝。下面描述了源代碼中不好的實(shí)踐。
  • 存儲通過簡單變換的的“加密”敏感信息廊谓。
  • 在沒有Android系統(tǒng)的特性的情況下使用或創(chuàng)建的密鑰梳猪,比如Android密鑰存儲庫
  • 密鑰被硬編碼暴露

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

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

獲取密鑰是微不足道的,因?yàn)樗谠创a中叠荠,并且對于應(yīng)用程序的所有安裝都是相同的匿沛。以這種方式加密數(shù)據(jù)是沒有好處的。尋找硬編碼的API密鑰/私鑰和其他有價(jià)值的數(shù)據(jù);他們也有類似的風(fēng)險(xiǎn)榛鼎。解密/加密密鑰代表了另一種試圖讓它變得更困難但并非不可能獲得的嘗試逃呼。

考慮下面的代碼:

//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);
}

驗(yàn)證密鑰的公共地址:

  • 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)用戶啟用了安全鎖屏防症,可以使用這個選項(xià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ù)會生成有咨,端點(diǎn)輸出琐簇,或者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)選項(xiàng):
-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)險(xiǎn)识椰,因?yàn)椋ㄎ词褂玫模┳址畬⒓兾谋緮?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àn)等踏兜。

缺點(diǎ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í)踐檢查代碼更改。

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

所有數(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ù)克锣。這個特點(diǎn)對于消息應(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點(diǎn)擊輸入框氨鹏。

在剪切板中尋找敏感信息

overview

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

靜態(tài)分析

識別接收敏感信息的輸入字段,以及降低剪貼板訪問風(fēng)險(xiǎn)的對策肢础。重寫輸入字段函數(shù)是一種通用的最佳實(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的传轰,點(diǎn)擊接收敏感信息的輸入域剩盒。如果顯示了復(fù)制/粘貼選項(xiàng),說明剪切板功能沒有被禁用慨蛙。

可以使用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>泡徙。可以使用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ù)奶幚恚槐热缂偶危绻坑捎脩糨斎虢M成參數(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);
}

這里實(shí)際上有兩條路徑珍昨,"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ù)宛乃。因?yàn)閿?shù)據(jù)庫基于SQL,存在SQL注入漏洞蒸辆≌髁叮可以使用Drozer模塊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沙箱一般會限制)酸些。可以使用Drozer模塊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ù)闯第,例如市栗,注冊一個賬戶或支付款項(xiàng)。 如果應(yīng)用程序不能正確地屏蔽它咳短,當(dāng)在明文顯示數(shù)據(jù)時填帽,敏感數(shù)據(jù)可能會被暴露出來。

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

靜態(tài)分析

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

動態(tài)分析

...

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

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

Android備份選項(xiàng):

  • 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"

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

  • 哪個文件被發(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

如果報(bào)錯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)分析

點(diǎn)開看一下....

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

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

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

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市闸溃,隨后出現(xiàn)的幾起案子整吆,更是在濱河造成了極大的恐慌,老刑警劉巖圈暗,帶你破解...
    沈念sama閱讀 217,277評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件掂为,死亡現(xiàn)場離奇詭異,居然都是意外死亡员串,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,689評論 3 393
  • 文/潘曉璐 我一進(jìn)店門昼扛,熙熙樓的掌柜王于貴愁眉苦臉地迎上來寸齐,“玉大人欲诺,你說我怎么就攤上這事∶祓校” “怎么了扰法?”我有些...
    開封第一講書人閱讀 163,624評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長毅厚。 經(jīng)常有香客問我塞颁,道長,這世上最難降的妖魔是什么吸耿? 我笑而不...
    開封第一講書人閱讀 58,356評論 1 293
  • 正文 為了忘掉前任祠锣,我火速辦了婚禮,結(jié)果婚禮上咽安,老公的妹妹穿的比我還像新娘伴网。我一直安慰自己,他們只是感情好妆棒,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,402評論 6 392
  • 文/花漫 我一把揭開白布澡腾。 她就那樣靜靜地躺著,像睡著了一般糕珊。 火紅的嫁衣襯著肌膚如雪动分。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,292評論 1 301
  • 那天红选,我揣著相機(jī)與錄音刺啦,去河邊找鬼。 笑死纠脾,一個胖子當(dāng)著我的面吹牛玛瘸,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播苟蹈,決...
    沈念sama閱讀 40,135評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼糊渊,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了慧脱?” 一聲冷哼從身側(cè)響起渺绒,我...
    開封第一講書人閱讀 38,992評論 0 275
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎菱鸥,沒想到半個月后宗兼,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,429評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡氮采,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,636評論 3 334
  • 正文 我和宋清朗相戀三年殷绍,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片鹊漠。...
    茶點(diǎn)故事閱讀 39,785評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡主到,死狀恐怖茶行,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情登钥,我是刑警寧澤畔师,帶...
    沈念sama閱讀 35,492評論 5 345
  • 正文 年R本政府宣布,位于F島的核電站牧牢,受9級特大地震影響看锉,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜塔鳍,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,092評論 3 328
  • 文/蒙蒙 一伯铣、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧献幔,春花似錦懂傀、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,723評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至郑兴,卻和暖如春犀斋,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背情连。 一陣腳步聲響...
    開封第一講書人閱讀 32,858評論 1 269
  • 我被黑心中介騙來泰國打工叽粹, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人却舀。 一個月前我還...
    沈念sama閱讀 47,891評論 2 370
  • 正文 我出身青樓虫几,卻偏偏與公主長得像,于是被迫代替她去往敵國和親挽拔。 傳聞我的和親對象是個殘疾皇子辆脸,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,713評論 2 354

推薦閱讀更多精彩內(nèi)容

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