Android Hacking - Part 5

Activities, Intents, BroadcastReceivers, Services & ContentProviders

·

7 min read

Activities

What is an Activity?

Activities are simply the screens we see when we open an application. Let's say we are greeted with the following Screens (Activities)

  • Login page (LoginActivity.java)

  • Profile page (ProfileActivity.java)

  • Settings page (SettingsActivity.java)

  • Code - functionality (CodeActivity.java)

  • And so on...

All these screens/activities are protected by the Application - Context. Only the app user or a very privileged user can access it. The user would require a username and password to access this application.

Seems alright, where's the issue?

Well let's say the app developer wants to add a functionality that when a user scans a specific QR code it should directly route the user to the CodeActivity.java screen but that is currently not possible. This is where as a developer we will use "Exported" activities. In this case, the developer can simply export the CodeActivity.java activity and the other pages are protected. The problem arises in the functionality where a user can scan the QR code to get to the CodeActivity page but we have a link to the Profile page allowing the user to enter the secure context without logging in.

How do you exploit something like this?

A malicious user can create an app to access the CodeActivity page, redirect to the Profile page and from the Profile page they can visit the Settings page to take over an account for example. However, there maybe states and protections in place safeguarding the encrypted credentials and we may not be able to access those but we would need to verify.

How do you find Exported Activities?

Our good old friend -> AndroidManifest.xml ! Here are the search terms we need to find activities or components are being exported

exported="true"
<intent-filter>...</intent-filter>

Typically the launching activity is exposed to the internet so that apps can launch other apps.

💡
When we set anything to exported="true", it affects all components - Activities, BroadcastReceiver, Services and ContentProvider

Intents

It is typically a description of an operation to be performed. Think communication / messaging. We can interact with other components of an application based on an intent.

Explicit Intent

All the parameters are defined and we know exactly who we are communicating with. Example: If your app has a login -> Profile -> Settings now this means the Intents are set and we are aware of what this is in the app. If required to work with other apps, we use Exported activity.

Implicit Intent

This is created when we do not know the destination application at this time. Example: if your app wants to use an email platform, and you're not sure which one you'll work with like Outlook, Gmail, etc. So we simply create an Implicit intent opening it up to any email provider (Seems crazy but is very common).

Intents work with Activities, BroadcastRecievers and Services but NOT Content Providers.

What does this look like?

Explicit Intent

To start a new activity this is what the code looks like for going from LoginActivity to ProfileActivity activity.

Intent myIntent = new Intent(this, ProfileActivity.class); #create an object and we add two parameters source and destination
this.startActivity(myIntent);

Now if we want to start a new activity with specific parameters like logging in as an admin then it looks like this

Intent myIntent = new Intent(this, ProfileActivity.class); #create an object and we add two parameters source and destination
myIntent.putExtra("username", "admin")
this.startActivity(myIntent);

How does this look like on the ProfileActivity code? It requests intent with the following code and once it's input and successfully logged in as say admin it gets pulled from that getIntent function and is reflected on the Profile page.

@Override
protected voic onCreate(Bundle savedInstanceState){
    Intent intent = getIntent();
    String user = intent.getStringExtra("username");
}

Implicit Intent

Think of a popup or message you get "mailto:test@silentkiller.test" and clicking on this will show an implicit intent that puts out a message saying we have this request for a mail application who can accept this request and the default mail application will say I've got this and open it up.

myIntent.putExtra("mail", "test@exaple.com")

How can we abuse this? Intent sniffing

We have a monitor which is sniffing for intents we can grab as a malicious app running on a phone.

Intent-Filter

Inside the AndroidManifest.xml, we can see the intent filters used for the launching activity. Example is:

<intent-filter>
    <action android:name="android.intent.action.MAIN">
    <category android:name="android.intent.category.LAUNCHER">
</intent-filter>

What this is doing is filtering intents for specific intents on the bus syste. Here we are listening to only certain events, in this case only the action main and category launcher. So, when we launch this app, we can react to the intents.

Read more about intents here - https://developer.android.com/reference/android/content/Intent

Broadcast Receivers

Events

We have multiple system events happening at all times on our phones, think low battery notification, headphones connected, airplane mode and more. This would be broadcast to all apps in the system in case you're typing something and it will trigger a prompt about saving your work, etc.

Another example: When headphones are plugged in then the music app would have a function listening like onReceive() method to decide what to do when connected like lower the volume etc.

Developers will add a priority level where an sms arrives but only the app with the highest priority would receive it like the system messages app.

System sms - 200
Our own app - 100
Bank app - 80

The app that receives it first can decide if it would like to forward this event to other applications of lower priority and this continues. Until then, the lowest priority does not receive the message at all.

💡
In Android 4.0.3 there was a vulnerability where you could set your app priority to 999 which is higher than the system so imagine creating a malicious app to receive all system events and decide to not forward it at all. The user would not be aware of this happening. as there are no popups or notification sounds. evil laugh

Developer priorities are defined in the AndroidManifest.xml until Android 8.x

What does it look like?

<receiver android:enabled="true" android:name=".myBroadcastReceiver">
    <intent-filter>
        <action>.....

    </intent-filter>
</receiver>

Manufacturers can have their own broadcast receivers for compatibility purposes.

Since Android 8.x and higher, we need to register BroadcastRecievers in the java code and not the AndroidManifest.xml

registerReceiver(myBroadcast, new IntentFilter("android.intent.action.BOOT"));

private BroadcastReceiver myBroadcast = new BroadcastReceiver() {

@Override
    public voic onReceive(Context context, Intent intent) {
    //code
    }

It makes it a little difficult and automated tools might miss this so we would have to decompile the application and look through to understand the broadcast receivers in use.

How do we exploit this now?

A broadcast event will be sent by an intent that means it can apply data to the event even sensitive information like a token. An attacker with a malicious app can listen for this intent and it can fetch the token in the broadcast event. Look for sensitive information in intents. (Intent sniffing)

Another issue can be in custom permissions as well if the protection level is not adequate (not "Signature"). As an attacker we can claim these permissions and gain access to that information. We will discuss more on the protection levels in future posts.

The only method we can interact with is the onReceive() method. Having a broadcastreceiver doesn't mean there's an issue it could be benign. We need to inspect the input this method receives as well as the code that lives in it. The definition of the BroadcastReceiver is in the AndroidManifest.xml and Java code.

Services

3rd key component in our Android applications. An application (like a game) needs to keep track of graphics, network requests, file interactions, complex calculations and more. If the application is taking too long to interact will be closed by the Android Core system.

To attack services, it needs to be exported and we need permissions to access the service.

ContentProvider

Typically a database, which means we can interact with them via a content URI like

content://com.android.contacts/contacts/100
# The format is the following:
prefix://authority/table/row
💡
A good search term when reversing the app to get an idea of all content providers content://

ContentResolver

To query the contacts database, within our app we have a ContentResolver which we will use to do so. We define the ContentResolver in our app which asks the Content Provider to provide content to us. If we have the permissions and if it is being exported then we can access as intended.

The ContentProvider must include the following functions to interact with the database:

  • query()

  • update()

  • insert()

  • delete()

Typically the path to the DBs is /data/data/<package>/db. Now we can grab this via adb and if it's not encrypted then we're golden. If not, then try to run the app and see if does not need any user pin/password to unlock the DB. If so, the decryption key is probably found/stored and can be identified in the reversed app.

This should now give us a general understanding and we will learn more as we get into practical examples in the next post.