Exported services are another attack surface in android.
used for long running bakcground tasks.
e.g. Media player in background.
Note:
Activity: Runs in the foreground and renders the UI
Broadcast Receiver: Runs in the background to execute a minimal task
Service: Executes long running tasks in the background
Services are also defined in manifest file. But that services can be protected with specific permission means apps with that permission can only call that service.
Receiving app handles our intent with onStart()function
public int onStartCommand(Intent intent, int i, int i2) {
Log.i("Flag24Service", Utils.dumpIntent(this, intent));
if (intent.getAction().equals("io.hextree.services.START_FLAG24_SERVICE")) {
success();
}
return super.onStartCommand(intent, i, i2);
}
Since latest release for us able to start a service from a target app. The target app must be running in background.
Also since android 11, we need to specifically define in our manifest file that which app's service we want to interact with.
Note: similar to broadcasts, services can't start an activity if the target app is in background. The target app must be in use only then an activity of that app can be launched. It's ui thing so that user don't get disturebed.
There is generally bindable service which is used. So an application can bind with target seervice for exchanging data with that app.
in that bindservice function is used. It is facilitated by android IPC interface. In that function onBindbecomes interesting to us.
After identifying an exposed Service in the android manifest, the next step should be looking at the onBind() method to determine if the service can be bound to or not.
When the onBind() method returns nothing or even throws an exception, then the service definetly cannot be bount to.
@Override // android.app.Service
public IBinder onBind(Intent intent) {
throw new UnsupportedOperationException("Not yet implemented");
}
There are also services where the onBind() method returns something, but it's only an internally bindable service, thus from our perspective it's a non-bindable service. These kind of services can usually be recognized by naming convention of "LocalBinder".
If app's service can only be bound to from inside the sme app. As an attacker this is essentially non-bindable service. i.e. LocalBinder.
package com.example.services;
import android.app.Service;
import android.content.Intent;
import android.os.*;
import android.util.Log;
import java.util.UUID;
public class Flag26Service extends Service {
// Constant for message handling
public static final int MSG_SUCCESS = 42;
// Generates a unique secret key for the service instance
public static String secret = UUID.randomUUID().toString();
// Messenger for handling incoming messages
final Messenger messenger = new Messenger(new IncomingHandler(Looper.getMainLooper()));
/**
* Handler to process incoming messages from clients.
*/
class IncomingHandler extends Handler {
String echo; // Placeholder variable, currently unused
// Constructor initializing the handler with a specified looper
IncomingHandler(Looper looper) {
super(looper);
this.echo = ""; // Initialize echo
}
@Override
public void handleMessage(Message message) {
Log.i("Flag26Service", "Received message: " + message.what);
// Process only specific message types
if (message.what == MSG_SUCCESS) {
Flag26Service.this.success(this.echo);
} else {
super.handleMessage(message);
}
}
}
@Override
public IBinder onBind(Intent intent) {
Log.i("Flag26Service", "Service bound with intent: " + Utils.dumpIntent(this, intent));
return this.messenger.getBinder(); // Return the Messenger's binder for client communication
}
/**
* Placeholder method for success handling.
* The original code called `success()`, but the method was missing.
*/
private void success(String message) {
Log.i("Flag26Service", "Success method called with message: " + message);
// TODO: Implement logic for handling success cases
}
}
new IncomingHandler(Looper.getMainLooper())
IncomingHandler is a custom class extending Handler.
Looper.getMainLooper() ensures that the handler runs on the main thread (UI thread) instead of a background thread.
This is important because services do not have their own UI thread by default, so handlers must explicitly specify a looper.
new Messenger(...)
Messenger wraps the Handler, allowing it to receive Message objects from other components (such as Activities, Services, or even separate apps).
It provides a safe way to handle IPC (Inter-Process Communication) without directly dealing with AIDL (Android Interface Definition Language).
handleMessagefunction is overrided to handle incoming message logic from our app.
this message inside handleMessage can contain all kind of data an intent can support.
onBindfunction returns an object of type Messenger which can be binded by other apps.