Intent Vulnerabilities
Intent overview
An Intent provides a facility for performing late runtime binding between the code in different applications. In other words, an Intent allows you to request an action from another app component. Although Intents facilitate communication between components in several ways, there are three fundamental use cases:
Starting an activity
You can start a new instance of an Activity by passing an Intent to startActivity(). The Intent describes the activity to start and carries any necessary data. If you want to receive a result from the activity when it finishes, call startActivityForResult().
Delivering a broadcast
The system delivers various broadcasts for system events, such as when the system boots up or the device starts charging. You can deliver a broadcast to other apps by passing an Intent to sendBroadcast() or sendOrderedBroadcast().
Starting a service
You can communicate with a background Service by passing an Intent to Context.startService() or Context.bindService().
Intent structure
The primary information contained in an Intent is the following:
Action is a string that specifies the generic action to perform (such as view or pick). In the case of a broadcast intent, this is the action that took place and is being reported. It is possible to specify custom actions for use by intents within a app (or for use by other apps to invoke components in the app), but usually action constants are used (such as ACTION_VIEW or ACTION_SEND) that defined by the Intent class or other framework classes.
Data is the URI (a Uri object) that references the data to be acted on and/or the MIME type of that data. The type of data supplied is generally dictated by the intent's action. For example, if the action is ACTION_EDIT, the data should contain the URI of the document to edit.
Component name is the name of the component to start. If you need to start a specific component in your app, you should specify the component name.
Category is a string containing additional information about the kind of component that should handle the intent (such as CATEGORY_BROWSABLE or CATEGORY_LAUNCHER).
Component name, action, data, and category properties represent the defining characteristics of an intent. By reading these properties, the Android system is able to resolve which app component it should start.
An intent can carry additional information that does not affect how it is resolved to an app component. An intent can also supply the following information:
Extras are key-value pairs that carry additional information required to accomplish the requested action. Just as some actions use particular kinds of data URIs, some actions also use particular extras.
Flags are defined in the Intent class that function as metadata for the intent. The flags may instruct the Android system how to launch an activity (for example, which task the activity should belong to) and how to treat it after it's launched (for example, whether it belongs in the list of recent activities).
Intent filter
An intent filter is an expression in an app's manifest file that specifies the type of intents that the component would like to receive. For instance, declaring an intent filter for an activity makes it possible for other apps to directly start the activity with a certain kind of intent. Likewise, an activity without any declared intent filters can be started only with an explicit intent.
Each intent filter is defined by an <intent-filter> element in the app's manifest file, nested in the corresponding app component (such as an <activity>
element). Inside the <intent-filter>
, the type of intents to accept is specified using one or more of the following three elements:
<action> declares the intent action accepted, in the
name
attribute.<data> declares the type of data accepted, using one or more attributes that specify various aspects of the data URI (
scheme
,host
,port
,path
) and MIME type.<category> declares the intent category accepted, in the name attribute.
To receive implicit intents, it is necessary to include the CATEGORY_DEFAULT category in the intent filter. The methods startActivity()
and startActivityForResult()
treat all intents as if they declared the CATEGORY_DEFAULT
category. If this category is not declared in the intent filter, no implicit intents will resolve to an activity.
For example, here's an activity declaration with an intent filter to receive an ACTION_SEND intent when the data type is text:
For all activities, it is necessary to declare intent filters in the manifest file. However, filters for broadcast receivers can be registered dynamically by calling registerReceiver() and then unregistered with unregisterReceiver(). Doing so allows an app to listen for specific broadcasts during only a specified period of time while the app is running.
Intent types
There are two types of intents:
Explicit intents
Implicit intents
Explicit intents
Explicit intents specify which application will satisfy the intent, by supplying either the target app's package name or a fully-qualified component class name.
For example, if you built a service in your app, named DownloadService
, designed to download a file from the web, you can start it with the following code:
Implicit intents
Implicit intents do not name a specific component, but instead declare a general action to perform, which allows a component from another app to handle it.
For example, if you have content that you want the user to share with other people, create an intent with the ACTION_SEND
action and add extras that specify the content to share. When you call startActivity()
with that intent, the user can pick an app through which to share the content.
Intent resolution
An implicit intent is delivered through the system to start another activity as follows:
Activity A
creates an Intent with an action description and passes it tostartActivity()
.The
Android System
searches for the best activity for the intent by comparing it to intent filters based on three aspects: Action, data (both URI and data type) and category. The page describes how intents are matched to the appropriate components according to the intent filter declaration in an app's manifest file.When a match is found, the system starts the matching activity (
Activity B
) by invoking itsonCreate()
method and passing it the Intent.
If the Android System
does not find any activity (across all apps) the ActivityNotFoundException will be thrown.
If multiple intent filters are compatible the system will launch the App Chooser. It displays a dialog so the user can pick which app to use.
You can control an app's position in the list using the android:priority="num"
attribute within the intent filter
Security issues
Abusing an activity's return value
If an app launched an implicit intent using startActivityForResult() an intercepting app can use setResult() to pass data into the onActivityResult() of the app.
There are two types of such actions:
System action which usually lead to reading of arbitrary files.
Custom actions which can lead to different vulnerabilities that depend on app's implementation.
Custom actions
Assume, an app expects a url in the data to open it in the WebView:
You can start to handle the com.victim.PICK_ARTICLE
actions and pass an arbitrary url for opening in the WebView:
AndroidManifest.xml
EvilActivity.java
System actions
The standard Android actions such as:
android.intent.action.PICK
to choose a photoandroid.intent.action.GET_CONTENT
to choose filesandroid.media.action.IMAGE_CAPTURE
to create a photoetc.
are used to obtain the URI of a file (document, image, video) selected by the user and to process it in an app (e.g. by sending it to a server). Most Android/Java libraries can't work with InputStream
returned by Android ContentResolver
to send data to a server. Therefore, apps very often cache URI data into a file before processing. This can lead to reading/writing of arbitrary files.
Arbitrary file reading
Assume, an app obtains a URI and caches a file to an external directory (for example, SD card), the vulnerable app could look as follows:
In this case you can create an app that will return a link to a file in a private directory of a targeted app:
AndroidManifest.xml
PickerActivity.java
When a victim clicks on the attacker's app in the activity picker list, the /data/data/com.victim/databases/credentials
file is automatically copied to SD card and can be read by any app with the android.permission.READ_EXTERNAL_STORAGE
permission.
Arbitrary file writing
Assume, an app obtains the content
URI and caches files from a ContentProvider to a temporary directory, the vulnerable app could look as follows:
In this case, you can pass a name with path-traversal to the getFileName()
method using your own ContentProvider:
AndroidManifest.xml
EvilContentProvider.java
This allows you to bypass the borders of the /data/data/com.victim/cache/
directory and write the file to the /data/data/com.victim/lib-main/lib.so
. If the targeted app loads this native library, this leads to arbitrary code execution in the victim's context.
Access arbitrary components
Since the Intent is Parcelable
, objects belonging to this class can be passed as extra data to another intent. This can be used to create a proxy component (activity, broadcast receiver or service) that take an embedded intent and pass it to dangerous methods such as startActivity()
or sendBroadcast()
. As a result, you can force an app to start an unexported component that can't be started directly from another app, or grant yourself access to app's content providers.
For example, suppose, an app have an unexported activity that performs certain unsafe actions and an exported activity that is used as a proxy:
AndroidManifest.xml
ProxyActivity.java
AuthWebViewActivity.java
In this example the AuthWebViewActivity
passes the user authentication session to the URL obtained from the url parameter.
Export restrictions mean you can't directly access AuthWebViewActivity
and a direct call throws the java.lang.SecurityException
with the Permission Denial: AuthWebViewActivity not exported from uid 1337
message:
However, you can force a victim to start AuthWebViewActivity
on their own:
There are no security violations because the app has access to all of its own components. Therefore, it allows you to bypass the built-in restrictions of the Android.
By itself, starting hidden components does not have much security impact and requires abuse of the functionality of the hidden components:
Access arbitrary components via WebView
Bypass protection
Developers can implement a filtering of received intents and explicitly set a component to handle the intent to null
:
In this case, you can bypass the app's explicit intent protection by specifying an unexported component via a selector:
The selector will be used when trying to find entities that can handle the Intent, instead of the main contents of the Intent.
However, developers can explicitly set a selector to null
:
Even so, you can create an implicit intent to match the intent-filter
of some unexported activity:
Insecure activity launches
If an app uses implicit intents with certain private data to launch activities you can start to handle the same actions to intercept the private data. For example, suppose a banking app uses an implicit intent with card data to launch an activity:
You can intercept card data as follows:
AndroidManifest.xml
EvilActivity.java
Insecure broadcasts
If an app uses implicit intents to deliver a broadcast you can register a broadcast receiver with the same action and intercept a user's broadcast from a different app. For example, suppose a messaging service requests new messages from the server and passes them to a broadcast receiver that is responsible for displaying them on the user's screen:
Since implicit broadcasts are delivered to each receiver registered on the device, across all apps, you can register the following broadcast receiver to intercept a user's broadcast:
AndroidManifest.xml
EvilReceiver.java
References
Last updated