渗透测试-数据存储

以OWASP移动应用安全认证标准和移动安全测试指导为基础,这个检测列表是为了设计、测试和发行安全的Android应用的安全考虑。

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

数据存储

保护认证凭据、私有信息和其他敏感信息是移动安全的关键。在这一章中,将学习Android为本地数据存储提供的API,并进行练习使用。

数据存储总结起来比较简单:公共数据应该是对所有人可用,敏感数据和私有数据必须被保护,或者设备存储之外。

注意敏感数据的含义取决于app的处理。数据分类会在“移动App安全测试”一章中的“敏感数据认证”具体描述。

测试敏感数据的本地存储

传统的建议是尽量少的敏感数据永久存储本地。在大部分实际场景中,然而,一些数据类型必须被存储。比如,当每次app启动时需要用户输入一个非常复杂的密码,在使用上这不是一个好的做法。大部分app必须本地缓存一些认证信息来避免这种情况。如果一个特定场景需要,私人认证信息和其他类型的敏感数据应该被存储下来。

当必须永久存储但不被app合适的保护,敏感数据是危险的。app可能把数据存储在好几个地方,比如设备中或者外部SD卡。当你尝试探索这些问题,考虑这些信息可能被处理并存储在不同的地方。认证信息比较有价值,可能会被攻击。

公开敏感信息有一系列影响。一般来说,一个攻击者认证信息并且用于其他的攻击,例如社会工程、账户劫持、从有支付选项的app中聚集信息。

存储数据对于很多移动app来说是基本。数据可以用很多方法永久存储。下面这些方法被广泛使用:

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

Shared Preference

Shared Preference API经常用来永久存储键值对。数据一般被写在一个解释文件.xml中。Shared Preference对象可以声明对所有app公开,也可以声明私有。误用经常导致敏感信息的暴露。例子:

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

一旦activity被调用,文件key.xml会被创建。

  • 用户名和密码会被存储在/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允许所有应用访问并获取key.xml的内容
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)的应用弃用。API 17以下的应用会被影响。

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();

调用后,privateNotSoSecure文件会被创建,路径如下:/data/data/<package-name>/databases/privateNotSoSecure

数据库目录下除了数据库外还包含文件如下:

  • Journal files.用于提交和回滚的临时文件
  • Lock file.锁定和日志功能的一部分,设计用于提高数据库的并发和减少写入饥饿问题。

SQLite DataBase(Encrypted)

用库SQLCipher,SQLite会被加密

如果适用加密数据库,确定密码是否硬编码在资源中(SharePreference),或者隐藏在代码或者文件系统等地方。取得密钥的安全方法包括:

  • 当app打开时,询问用户加密数据库适用PIN或者密码
  • 存储密钥在服务器中并且只允许从web服务中访问

Realm Dtabases

Realm DataBase for Java在开发者中非常受欢迎,数据库和内容用存储在配置文件中的密钥加密。

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

如果数据库没有被加密,可以获得数据。如果被加密,确定密钥是否被硬编码在source或者resources中,是否存储在SharePreference或者其他位置。

Internal Storage

如果存储在设备的内部存储中,不能被设备中的其他app访问。当用户卸载app,这些文件会被移除。下面的代码会永久的把数据保存在内部存储中。

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

检查文件模式确认只有app能访问文件,设置为MODE_PRIVATEMODE_WORLD_READABLEMODE_WORLD_WRITEABLE(都被弃用)会有安全风险。

搜索类FileInputStream,查找出app内那些文件被读取。

外部存储

一些Android设备支持外部存储,可以使用下列代码在外部存储中存储敏感信息。

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

代码调用时,会创建文件password.txt,而且卸载时不会删除。

静态分析

本地存储

检查资源:

  • 检查AndroidManifest.xml读取外部存储权限,比如,uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
  • 检查源代码中的关键字和API调用,是否被用来存储数据
    • 文件权限,比如:
      • MODE_WORLD_READABLEMODE_WORLD_WRITABLE:应该避免使用MODE_WORLD_READABLEMODE_WORLD_WRITABLE,因为任何app都能从读取文件。计时存储在私有数据目录下,如果数据必须与其他应用分享,比如content provider。content provider为其他应用提供了读和写权限,能动态授权
    • 类和函数,比如:
      • SharedPreference类(存储键值对)
      • FileOutputStream类(内部存储和外部存储)
      • getExternal*函数(使用外部存储)
      • getWritableDatabase函数(写数据库)
      • getReadableDatabase函数(读数据库)
      • getCacheDirgetExternalCacheDirs函数(使用cache文件) 加密应该使用经过验证的SDK函数实现。下面描述了源代码中不好的实践。
  • 存储通过简单变换的的“加密”敏感信息。
  • 在没有Android系统的特性的情况下使用或创建的密钥,比如Android密钥存储库
  • 密钥被硬编码暴露

典型的误用:硬编码的加密密钥 对称密钥存储在设备上,还原数据只是时间问题了。考虑下面的代码:

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

获取密钥是微不足道的,因为它包含在源代码中,并且对于应用程序的所有安装都是相同的。以这种方式加密数据是没有好处的。寻找硬编码的API密钥/私钥和其他有价值的数据;他们也有类似的风险。解密/加密密钥代表了另一种试图让它变得更困难但并非不可能获得的尝试。

考虑下面的代码:

//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支持相关安全信任存储。从Android 4.3,为存储和使用app私有密钥提供公共AP。app使用一个公钥创建一个新的私钥/公钥对来加密数据,用私钥解密。

使用用户认证能保护密钥存储在Android KeyStore,用户的锁屏凭据用来认证。

你能在两种模式中使用密钥:

  1. 在授权后的一段时间内,用户才能使用密钥。在这种模式中,一旦用户解锁设备所有密钥能被使用。可以为每个密钥制定认证时间。只有当用户启用了安全锁屏,可以使用这个选项。如果用户没有启用安全锁屏,所有密钥会永久失效
  2. 用户被授权使用与一个密钥相关联的特定加密操作。在这种模式下,用户必须为涉及到密钥的每个操作请求一个单独的授权。目前,指纹认证是请求这种授权的唯一方法。

由Android KeyStore承担的安全等级取决于设备的执行。大部分的设备提供硬件备份KeyStore:密钥在一个可信的执行环境或者安全的环境中生成和使用,操作系统不能直接访问他们。意味着加密密钥不能被简单地得到,甚至是root设备。

KeyChain KeyChain类用于存储和恢复系统范围的私钥和相关证书。如果某些重要的东西第一次存储, 用户需要配置锁屏pin或者密码保护存储。KeyChain是系统范围的,所有app能访问存储在keystore中的内容

检查源代码确定原生Android机制是否确认敏感信息。敏感信息应该加密,不能明文存储。如果敏感信息必须被存储在设备上,一些KeyStore的API调用可以用来保护数据。完整步骤:

  • 确定app使用KeyStore和Cipher机制在设备上安全存储加密信息。查询模型import java.security.KeyStore, import javax.crypto.Cipher, import java.security.SecureRandom,佳偶皮相关使用。
  • 调用函数store(OutputStream stream, char[] password)并使用密码存储KeyStore在磁盘上。确认密码由用户提供,不是硬编码。

动态分析

安装和使用app,至少执行过所有功能一次。当用户输入后数据会生成,端点输出,或者app运送,完整如下:

  • 认证开发文件,备份文件和不应该包含在发行中的老文件
  • 确认SQLite 数据库可用,是否包含敏感信息。SQLite存储在/data/data/<package-name>/databases
  • 检查SharedPreference存储的xml文件(/data/data/<package-name>/shared_prefs)是否由敏感信息。
  • 检查文件的权限(rwx)
  • Realm数据库是否可用/data/data/<package-name>/files/,是否加密,是否包含敏感信息。默认文件扩展名是realm文件名是default
  • 检查外部存储中是否由敏感信息

敏感数据的测试日志

日志中的敏感信息可能会暴露给攻击者或者恶意应用。使用日志的两个类:

  • Log Class
  • Logger Class

使用日志机制,需要从生产版本中删除日志记录。

静态分析

检查app的源代码的日志,搜查关键字:

  • 函数和类,比如:
    • android.util.Log
    • Log.d|Log.e|Log.i|Log.v|Log.wtf
    • Logger
  • 关键字和系统输出:
    • System.out.print|System.out.print
    • logfile
    • logs
  • 当准备发行版本时,可以使用ProGuard(包含在Android Studio中)删除相关代码。检查是否所有的andriod.util.Log类和函数都被移除了,检查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方法调用,new StringBuilder会不会消除取决于ProGuard的版本。

这是一种安全风险,因为(未使用的)字符串将纯文本数据泄漏到内存中,这可以通过调试器或内存转储来访问。

这个问题上的一些选择:

  • 执行一个定制的日志工具,能接收简单的参数,并在内部构造日志语句 SecureLog.v("Private key [byte format]: ", key);

然后配置ProGuard取消调用

  • 在源码中移除日志而不是bytecode中,下面是简单的gradle任务:
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);
      }
    }
  }
}

动态分析

一次使用app所有的功能,然后识别应用数据目录并找到日志文件/data/data/<package-name>.检查应用日志决定日志数据是否生成,一些移动应用生成和存储到这个目录下。

很多应用开发者仍然使用System.out.println或者printStackTrace而不是一个合适的日志类。因此当应用开启、运行和关闭,测试策略必须包括所有输出生成。检查System.out.println或者printStackTrace输出了什么内容,可以使用Logcat。两种方法执行Logcat:

  • Logcat是DDMS和Android Studio的一部分。如果app在debug模式中运行,日志输出会显示在Logcat tab.可以在Logcat中通过定义模式过滤app的日志。
  • 可以用adb执行并存储日志: $ adb logcat > logcat.log

检查敏感信息是否发送给第三方

overview

可以在app中嵌入第三方服务,这些服务可以执行追踪服务,监督用户行为,贩卖横幅广告,提高用户体验等。

缺点是缺少可视化:无法知道第三方库指定了什么代码。因此,你应该确定唯一一件事,就是没有敏感信息能被发送给服务。

大部分第三方服务以两种方法执行:

  • 标准库,比如一个包含在APK中的Android工程jar
  • a full apk

静态分析

通过一个IDE向导程序或者收工添加一个库或者SDK可以自动整合第三方库。这种情况下,检查AndroidManifest.xml中是否能访问SMS,contacts,location是非常有必要的。

检查源代码中的API调用和第三方库函数或者SDK。为安全最佳实践检查代码更改。

检查加载库确定是否有必要加载、过期或者包含未知的漏洞

所有数据发送给第三方服务应该匿名。被跟踪到用户账户或者会话的数据不发送给第三方。

动态测试

检查所有对嵌入式敏感信息的外部服务请求。 为了拦截客户端和服务器之间的通信,可以通过启动一个中间人(MITM)攻击,使用Burp suite或OWASP ZAP来执行动态分析。 一旦通过拦截代理路由流量,就可以尝试嗅探应用程序和服务器之间的流量。 所有不直接发送到主函数主机的应用程序请求都应该检查敏感信息,比如跟踪器或广告服务中的PII。

检查键盘缓存对字符输入不可用

overview

当用户在输入时,软件自动建议数据。这个特点对于消息应用非常有用。然而,当用户选择一个输入框时,键盘缓存可能暴露敏感信息。

静态分析

在一个activity的布局定义中,可以定义有XML属性的TextViews。如果XML属性android:inputType给了值textNoSuggestions,当输入被选定时键盘缓存不显示。用户将必须手工输入所有东西。

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

动态分析

打开app点击输入框。

在剪切板中寻找敏感信息

overview

当用户在输入框输入数据时,他们应该使用剪切板来复制和粘贴数据。设备中的app分享剪切板,所有恶意应用可以访问敏感数据。

静态分析

识别接收敏感信息的输入字段,以及降低剪贴板访问风险的对策。重写输入字段函数是一种通用的最佳实践,它禁用了这些函数的剪贴板。

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在输入域中设置不可用

android:longClickable="false"

动态分析

启动app的,点击接收敏感信息的输入域。如果显示了复制/粘贴选项,说明剪切板功能没有被禁用。

可以使用Drozer模块post.capture.clipboard从剪切板中提取数据。

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

检查敏感信息在IPC机制中是否暴露

overview

content provider作为Android IPC机制的一部分,允许app的存储数据被其他应用访问和修改。如果配置不合适,这些机制可能会泄露

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

静态分析

第一个补助是查看AndroidManifest.xml,检测content provider是否暴露。依据元素<provider>识别content providers。完整步骤:

  • 检查属性android:exported="true"
  • 检测数据是否被权限(android:permission)保护,权限限制了暴露程度。
  • 决定android:protectionLevel属性是否有值signature。这个设置是指数据只能被相同企业的app访问(被相同的密钥签名)。
  • 如果适用了android:permission,其他应用必须声明一致<uses-permission>。可以使用android:grantUriPermissions属性授权更明确的访问,还可以用使用<grant-uri-permission>元素限制访问。

检查源代码理解content provider是如何被使用的。搜索关键字:

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

为了避免SQL注入攻击,使用参数化的查询方法,比如queryupdate,以及 delete. 确保对所有方法的参数进行适当的处理;比如,如果全部由用户输入组成参数,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")被读和写权限保护。

检查源代码

检查有没有敏感信息泄露。

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",后者没有被保护。

当访问URI时,查询声明返回所有密码和路径Passwords/。我们将会在动态分析中定位这个问题,显示需要的URI

动态分析

测试内容提供器

动态分析一个应用content provider,先遍历攻击面:将应用包名传递给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是暴露的。没有权限就能访问,除了DBContentProvider中的/Keys路径。用这些信息,可以重构部分内容URIs访问DBContentProvider(content://...)

识别应用中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中提取数据:

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数据库存储用户数据。因为数据库基于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 (')

如果一个应用存在sql注入,它会返回一个冗长的错误信息。Android中的SQL注入可能会修改或者查询有漏洞的content provider中的数据。下列例子中,Drozer模块app.provider.query用来列出所有的数据库

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注入也可以用于从其他受保护的表中检索数据:

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

还可以使用scanner.provider.injection模块自动进行寻找content provider内容:

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的文件系统

content provider能提供文件系统的入口。这允许app来分享文件(Android沙箱一般会限制)。可以使用Drozer模块app.provider.readapp.provider.download从基于文件的content provider分别读取和下载文件。这些content providers容易受到目录遍历的影响,它允许在目标应用程序的沙箱中读取其他受保护的文件。

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
...

通过用户接口检查敏感数据暴露

overview

许多应用程序要求用户输入几种类型的数据,例如,注册一个账户或支付款项。 如果应用程序不能正确地屏蔽它,当在明文显示数据时,敏感数据可能会被暴露出来。

对敏感数据的屏蔽,通过显示星号或圆点而不是清晰的文本,应该在应用程序的活动中执行,以防止信息披露和减少诸如肩冲浪等风险。

静态分析

为了确保应用程序屏蔽敏感的用户输入,请在EditText的定义中检查以下属性:android:inputType="textPassword" 有了这个设置,dots(而不是输入字符)将显示在文本字段中,防止应用程序将密码或pin泄漏到用户界面。

动态分析

...

测试敏感数据的备份

Android提供自动备份,复制数据和所有app的已安装的设置信息。存储敏感信息的app可能会通过数据备份泄露信息。

Android备份选项:

  • Android有内置的USB备份设备。进入调试模式,使用adbadb backup命令穿件完整数据备份。
  • Google提供了备份到google服务器上
  • 两个备份api:
    • 键值对备份,上传Android备份服务云。
    • 应用程序自动备份。(Android 6.0)
  • OEM提供更多选择

静态分析

本地

AndroidManifest.xml文件中,提供属性allowBackup。如果设置为true,就可以被adb备份。

不可备份,则设置为false。默认true。

检查AndroidManifest.xml中的代码:

android:allowBackup="true"

无论使用键值对备份还是自动备份,必须注意以下事项:

  • 哪个文件被发送到云上

  • 哪个文件包含敏感信息

  • 发送到云上的敏感信息是否被加密

  • 自动备份:通过配置allowBackup属性自动备份。执行备份代理时,通过android:fullBackupOnly激活自动备份。

  • 键值对备份:为了确保键值对备份,必须在manifest文件中定义备份代理:

android:backupAgent

动态分析

adb拉取备份

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

获得备份文件转格式

$ 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

自动截屏泄露信息

应用切换,系统会保存应用屏幕截图作为背景,这会导致安全问题

静态分析

如下设置会使屏幕内容为空白

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

setContentView(R.layout.activity_main);

动态分析

点开看一下....

检查内存中的敏感数据

这一届描述怎样通过进程存内存中大数据暴露

识别存储在内存中的敏感信息,

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,456评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,370评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,337评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,583评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,596评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,572评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,936评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,595评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,850评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,601评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,685评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,371评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,951评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,934评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,167评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,636评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,411评论 2 342

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,397评论 25 707
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,585评论 18 139
  • 有时候,生活太忙碌,我们只顾低头疾步前行 有时候,生活太枯燥,我们只是忘了抬头看看头顶的天空 清晨,正午,傍晚,夜...
    答应小姐阅读 191评论 0 0
  • 感恩今天是新的一天,晨起进行了冥想。充满力量,最近身体不舒服,总想睡觉。进而又补充了睡眠。 感恩jj小仙女说中我要...
    花开见佛笑阅读 176评论 0 0
  • 咳咳🙊 之前做的外包项目中要实现一个时间轴,效果类似这种 思路是cell中添加一条线,然后在view中再添加两个U...
    她说的黄山阅读 1,565评论 0 2