Intent Vulnerabilities
Last updated
Last updated
An 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
You can start a new instance of an Activity by passing an Intent to . 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 .
Delivering a
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 or .
Starting a
You can communicate with a background Service by passing an Intent to or .
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 or ) that defined by the Intent class or other framework classes.
Data is the URI (a 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 , 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 or ).
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.
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.
There are two types of intents:
Explicit intents
Implicit 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 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.
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 to startActivity()
.
When a match is found, the system starts the matching activity (Activity B
) by invoking its onCreate()
method and passing it the Intent.
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.
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.
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
The standard Android actions such as:
android.intent.action.PICK
to choose a photo
android.intent.action.GET_CONTENT
to choose files
android.media.action.IMAGE_CAPTURE
to create a photo
etc.
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.
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:
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:
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
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
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 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).
Each intent filter is defined by an 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:
declares the intent action accepted, in the name
attribute.
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.
declares the intent category accepted, in the name attribute.
To receive implicit intents, it is necessary to include the 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 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 and then unregistered with . Doing so allows an app to listen for specific broadcasts during only a specified period of time while the app is running.
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 describes how intents are matched to the appropriate components according to the intent filter declaration in an app's manifest file.
If the Android System
does not find any activity (across all apps) the will be thrown.
If an app launched an implicit intent using an intercepting app can use to pass data into the of the app.
Developers can implement a filtering of received intents and to null
:
In this case, you can bypass the app's explicit intent protection by specifying an unexported component via a :