Intent Attack Surface

Activity

An Activity in android app represents a single screen in that app. If app has multiple screen fr different actions then it will have multiple activities defined.

Read more here: https://developer.android.com/reference/android/app/Activity

Now

        <activity
            android:name="com.example.test.SecondActivity"
            android:exported="false"/>
        <activity
            android:name="com.example.test.MainActivity"
            android:exported="true">
            <intent-filter>
                <action android:name="android.intent.action.MAIN"/>
                <category android:name="android.intent.category.LAUNCHER"/>
            </intent-filter>
        </activity>

In this manifest file we can see there are two activities. Activity which is exported is reachable by outside world. And it is a interest for an attack surface.

If export is set to false, then this activity can only be called from application itself, and not much interesting to an attacker.

Intents

Official Doc: https://developer.android.com/reference/android/content/Intent

My word: Intent is telling Android OS what you intent do and let android handle that intent to approprite apps/services which can handle your intent.

e.g sharing a PDF from internet. When you click on share a list of apps open which can save or share your pdf. Now these apps have registered themselves with OS as application which can handle such user actions beforehand while installing.

In this code on button click SecondActivity class is luanched from MainActivity internally.

But when you have to create a PoC app and interact with target app's Activity class method is lsighlty different where we have to specify package name and fuully qualified class name.

Intents are carriers for inter/intra app communication which means they can carry data along with them.

Functions like getIntent, getIntExtra and such functions (read more)are good indicators that data is being received and processed by target class.

e.g here SecondActivity class expects a intent and check its value and then set text if value is 1337.

Now as an attacker we can send this expected value using putExtra() function

Intent Action

An Intent action in Android defines the type of operation or event that the intent is describing. It acts as an identifier for what the Intent is supposed to do or what behavior it should trigger.

There are built in actions like

here action ACTION_VIEW says display the content at following URI.

actions can be sutom too.

on receiving intent it check if action of intent is GIVE_FLAGor not.

POC app would be then

Intents can specify data filter also to filter what kind of URL it expects

Here scheme of URI must be https.

These intents gets passed from one app to another with help of Android IPC and binders.

Nested Intents.

Some activities can only be started internally. So some exported activities receive some data and process some data to open another activity. It's useful when

  1. Passing Complex or Multi-Step Data

  2. Delegating data

e.g.

In this code we can't just send an intent and reach success function. TO reach success function we need to send a intent which is encapsulated inside another intent which itself is encapsulated inside another intent.

intent---(includes)-->intent2---(includes)-->intent3.

so solution becomes.

We just send outermost intent to app which includes other intents in itself and are parsed and used according to application logic.

Intent Rdirection/Forwarding Attack

In the same problem code you can see if intent3 reason is nextinstead of backit takes the intent3 to launch a new activity. Now this allows us to send an intent to launch an activity which was set to exported=false in manifest file.

As we are sending intent to activity which is exported but then it extract another intent from it and use that to start another activity. This is classic example of intent redirection attack.

solution:

You can see this time we specify for intent3 to target a activity FLag6Activity .

Official Doc: https://developer.android.com/privacy-and-security/risks/intent-redirection

Which also says hwo to mitigate such attack, most simple answer is dont forward intents. IF necessary sanitize and check what this intent will do and is it allowed.

Launch Modes and Tasks

By now you have seen how we are sending multiple intents one after another to target app. By default there is no guarantee on order of intents being processed by target app.

In my case most of the time intents were processed in reverse order. If you need to ensure order you can use multiple buttons to ensure each intent is sent in sequence of button click. Or you can introduce sleep method to pause execution between two intents ensuring intents are passed in order. Ok let's end the detour.

What is Launch modes and tasks.? let's answer this question with another question.

What happens when two same activities are launched. What happens when you press back button where did that activity go.

Answer lies in concept of Launch Modes and Tasks(read official doc here: https://developer.android.com/guide/components/activities/tasks-and-back-stack#TaskLaunchModes)

simple explanation: https://medium.com/@riztech.dev/understanding-android-launch-modes-and-tasks-3397c3065fef#:~:text=Scenario%201%3A%20If%20the%20activity,and%20added%20to%20the%20stack.

my words: Basically every activity launch adds that activity to stack and back button destroys it. But what if you want to resuse same activity which was created earlier. Read the blog you will understand.

in manifest file target app can define how it want to handle multiple launch of same activity, create new one stack, resuse older one, or bring activity on top if it exists in stack before. All of this configurable.

We can also control this behaviour with our intents.

e.g. In this code to reach success method, onNewIntent must be called.

but if send an intent to start this activity everytime onCreate() will be called. given that nothing is configured in manifest file.

and onCreate is only triggered when an already create activity is resued.

Now what should we do? We can tell OS to treat our secondary intent in way which do not create the new activity on stack but resuse it by setting some intent flags.

e.g

  1. FLAG_ACTIVITY_REORDER_TO_FRONT

  2. FLAG_ACTIVITY_SINGLE_TOP

e.g code

Here after launching first intent, we pause execution then force app to resue same activity second time causing onnewIntent()to call.

Intent Receive result.

Intents are bidirectional communication, they can also receive result from raget app. For that we need to use function.

which is used alongside following function to parse results

onActivityResultreceives intent in response which contain all the data in it.

We can parse this intent to extract needed result from the received intent.

Liveoverflow created an awesome script to parse the intent recived and show in a dialog box. Just import it in your project and use it.

sample usage

Explicit vs Implicit Intent

When we specify which package we are targetiing in intent, thats' explicit intent.

When we just specify intent and let OS do its magic that's implicit.

As we have seen explicit intents a lot. Let's discuss implicit.

e.g. In this code we only specify what we intend to do, and OS will open camera app.

How did OS know which app to send intent to, that is becuase:

In camera app manifest file we have this.

Which specified that camera app activity can handle following types of intents.

When you set following flag on your intent it create detailed debug log in logcat.

Implicit Intent call hijacking

We can respond to our target application's implicit calls.

For that we need to make an exported activity. We have creates a secondactivity class which is exported, means our target app can interact with it.

We set intent filters to match what kind of actions we can handle i.e. io.hextree.attacksurface.ATTACK_MEwhich is exactly our target application uses to make implicit calls.

Now in SecondActivity class we can respond to for incomding intents and send results back.

Pending Intent Hijacking

What is Pending Intent?

You create a intent , wrap it in a pending intent wrapper, which means you have a intent which is going to be fulfilled by some another app. You forward that pendingintent to another app to fullfill that intent.

Receiving app recives all the permission of senders app too to execute that intent.

More explain here:

Pending intent real world attack: https://hackerone.com/reports/1161401

Since Android 11 all pending intents are by default immutable, which means reciving app can not update the original intent. We have to explicitly set mutable flag if we want to do it.

More: https://developer.android.com/about/versions/12/behavior-changes-12#pending-intent-mutability

Receiving app uses send()function to fulfill the original intent action.

If receiving intent updates the intent then changes are not overwritten instead they are merged.

Pending intents are used in lots of places within Android. It allows one app to create a start activity intent and give it to another app to start the activity "in its name".

We have seen intent redirects before, and pending intents basically work exactly like that. Except that the "redirected" pending intent will run with the permission of the original app.

While this mitigates the typical intent redirect vulnerability, if we somehow get ahold of a pending intent from a victim app, it could lead to various issues.

Some e.g. Here we have a target app

In this code

When activity is launched it extract a pending intent from a getintent() it recived using tag "PENDING"

Using that pendingintent it modifies the received intent by putting "flag" extra in it. And usi pendingIntent.send to launch the activity defined in original pendingintent.

From our app we can do

We send a pending intent inside a intent that launches our target app. Where pendingIntent is wrapper around a intent which will launch SecondActivity class. When pendingIntent.send is used.

In SecondActivity.classin our app we can retreive data from recived intent.

2nd e.g.

in our target app

In our target app we can a pending intent is created and sent inside another intent with action io.hextree.attacksurface.MUTATE_ME we can export our app activity to handle such intent and modify the incoming pendingintent and add an extra value 42 ans send it back to reach success function.

our android manifest file

Now our secondactivity class handles incoming intent and modify it(we can modify it as it was set mutable originally in target app)

We modify the incoming intent and send it back to launch FLag23Activity class. We didnt even have to setAction as it was originally set in original intent.

"Some activities can also be reached by a website in the browser. This is of course a much more interesting attack model, as it doesn't require a victim to install a malicious app. These web links that result into opening an app are so called deep links." Read more:

These activities need to be marked as BROWSABLE in manifest file.

Deeplinks are handled as intent only. So receiving apps need to declare them in manifest file to let OS know they can handle such deeplinks.

Deeplinks are then extracted from data part of intent for prcoessing.

Apps registers themselves in manifest file like this

Now if a browser tries to open url hex://token?... OS will know my app can handle this deeplink.

Another app can also trigegr this deeplink like this using setdata(Uri.parse())

and receiving app can handle this deeplink using URI object and get paramters value from it.

of course an another malicious app can declare same intent-filter in their manifest file then OS will give choice to user to choose which app should handle your deeplink, an if they choose wrong app then malicous app will end up with sensitive data sent in that URI.

Chrome Intent Scheme

You can launch apps directly from a web page on an Android device with an Android Intent. You can implement a user gesture to launch the app with a custom scheme called intent:

it is only supported in chrome

This intent scheme is very flexible and customizable

e.g.

  1. Intent wihout host and URI

  1. With host and URI

as you can see we can specify specfic package which should handle our intent, we can target specific class name in our package with component. We can add custom schemes with scheme=custom .

We can add extra values also as we do in any intent, as depcited in above example with S.actionto add a string and use B.flag to set boolean value.

from chrome source code, we can see following data can be added to

Using intent:// scheme we have seen that it is easy to configure only intended apps should handle your links. But it can be insecure if an app with package name is installed on system from some side channel.

There is another way to secure deeplink APP Links

you might have encountered a scenario when you open a URL in browser it directly opens in related app.

It doen through app links. using autoverify feature

An App Link is a type of deep link in Android that connects a specific URL to an app. When a user clicks a URL that matches an app link, Android can open the link directly in the app instead of showing a chooser dialog or opening the link in a web browser.

What is AutoVerify?

The autoVerify attribute is used in the AndroidManifest.xml file to enable automatic verification of the app's association with the domain. If verification is successful, the app is automatically set as the default handler for links that match the specified domain.

How AutoVerify Works

  1. The app developer includes the autoVerify="true" attribute in the intent filter for app links.

  2. The domain's root server hosts a assetlinks.json file, which proves the app's ownership of the domain.

  3. Android verifies the association during app installation or update.

e.g

on `https://example.com/.well-known/assetlinks.json` they host this

which verify that this package is allowed to open URL with host example.com .

Last updated