Android Hacking - Part 5
Activities, Intents, BroadcastReceivers, Services & ContentProviders
Photo by Markus Winkler on Unsplash
Table of contents
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.
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.
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
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.