Anatomy of an Android Malware Dropper

0

Recently, at EFF’s Threat Lab, we have focused heavily on the Android malware ecosystem and provided tools for its analysis. We have discovered many examples of Android malware in the tor-hydra Family have surfaced, masquerading as banking apps to trick unsuspecting customers into installing them. In this post, we will take an example of such a sample and analyze it using open source tools available for everyone.

At a glance

The example we will be looking at was first seen on March 1st, 2022. This particular malware presents itself as a banking app for BAWAG, a well-known financial institution in Austria. On first run, the app prompts the user to grant the app Accessibility Services permission. Accessibility services permission grants an app broad access to read the screen and mimic user interaction. After permission is granted, the app itself will be placed in the background. Any attempt by the user to uninstall the app is prevented by the app interrupting and closing the uninstall dialogs. Trying to reopen the app also fails – nothing happens.

Analyzing Android Package APK

AndroidManifest.xml

The Android app‘s manifest file contains a list of permissions, activities, and services that an app provides. If an activity isn’t listed in the app manifest, the app can’t launch that activity. Use an Android static analyzer such as jadx or apktool We can look at the manifest XML. The malware app’s manifest asks for a variety of permissions, including the ability to read and send SMS messages (a common way of spreading malware), request installation and deletion of packages, read contacts, initiate calls and to request the accessibility service mentioned above. It also references a number of classes that are not defined anywhere in ours jadx-reverse code:

  • com.ombththz.ufqsuqx.bot.components.commands.NLService
  • com.ombththz.ufqsuqx.bot.components.injects.system.InjAccessibilityService
  • com.ombththz.ufqsuqx.bot.components.locker.LockerActivity
  • com.ombththz.ufqsuqx.bot.components.locker.LockerActivity$DummyActivity
  • com.ombththz.ufqsuqx.bot.components.screencast.ScreencastService
  • com.ombththz.ufqsuqx.bot.components.screencast.ScreencastStartActivity
  • com.ombththz.ufqsuqx.bot.components.screencast.UnlockActivity
  • com.ombththz.ufqsuqx.bot.components.socks5.Socks5ProxyService
  • com.ombththz.ufqsuqx.bot.HelperAdmin$MyHomeReceiver
  • com.ombththz.ufqsuqx.bot.PermissionsActivity
  • com.ombththz.ufqsuqx.bot.receivers.MainReceiver
  • com.ombththz.ufqsuqx.bot.sms.ComposeSmsActivity
  • com.ombththz.ufqsuqx.bot.sms.HeadlessSmsSendService
  • com.ombththz.ufqsuqx.bot.sms.MmsReceiver
  • com.ombththz.ufqsuqx.bot.sms.SmsReceiver
  • com.ombththz.ufqsuqx.core.injects_core.Screen
  • com.ombththz.ufqsuqx.core.injects_core.Worker
  • com.ombththz.ufqsuqx.core.PeriodicJobReceiver
  • com.ombththz.ufqsuqx.core.PeriodicJobService
  • com.ombththz.ufqsuqx.MainActivity
  • info.pluggabletransports.dispatch.service.DispatchReceiver
  • info.pluggabletransports.dispatch.service.DispatchService
  • info.pluggabletransports.dispatch.service.DispatchVPN
  • org.torproject.android.service.OrbotService

The fact that the manifest references activities, services, and receivers it intends to run without defining them is the first clue that it’s an “Android dropper”.

Unpack Android droppers

An Android dropper is malware that disguises its behavior by hiding its payload and only decoding and loading the code it needs at runtime. As Ahmet Bilal Can explains, this makes it harder for AV and security researchers to detect the malware by doing “reflection, obfuscation, code flow flattening and trash codes [the] Stealth unpacking process.” Although stealthy, the steps the malware takes to hide itself can still be detected and subverted with a little help from the Frida dynamic instrumentation toolkit. Frida is able to insert itself into the control flow of a running app and introduce its own code. This can be useful for spotting typical methods that malware uses to cloak itself and load the underlying payload. In this case we can use a short script to detect that Java classes are loaded dynamically:

var classLoader = Java.use('java.lang.ClassLoader');
var loadClass = classLoader.loadClass.overload('java.lang.String', 'boolean');

loadClass.implementation = function(str, bool){
  console.log("== Detected ClassLoader usage ==");
  console.log("Args: ", str, bool);
  return this.loadClass(str, bool)
}

If we run this code, we get

$ frida -U -f com.ombththz.ufqsuqx -l class-loader-usage.js --no-pause
     ____
    / _  |   Frida 15.1.16 - A world-class dynamic instrumentation toolkit
   | (_| |
    > _  |   Commands:
   /_/ |_|       help      -> Displays the help system
   . . . .       object?   -> Display information about 'object'
   . . . .       exit/quit -> Exit
   . . . .
   . . . .   More info at https://frida.re/docs/home/
Spawned `com.ombththz.ufqsuqx`. Resuming main thread!                   
[Android Emulator 5554::com.ombththz.ufqsuqx]-> == Detected ClassLoader usage ==
Args:  com.honey.miletes.k false
== Detected ClassLoader usage ==
Args:  android.support.v4.content.FileProvider false
== Detected ClassLoader usage ==
Args:  com.ombththz.ufqsuqx.App false
== Detected ClassLoader usage ==
Args:  com.ombththz.ufqsuqx.MainActivity false
== Detected ClassLoader usage ==
Args:  com.ombththz.ufqsuqx.core.injects_core.Worker false
== Detected ClassLoader usage ==
Args:  com.ombththz.ufqsuqx.bot.PermissionsActivity false
== Detected ClassLoader usage ==
Args:  org.torproject.android.service.OrbotService false

Our missing classes are actually loaded dynamically!

Previous iterations of tor-hydra Dynamically Loaded Malware a dex File (an Android Dalvik executable) that could be viewed with adb logcatand used the system call unlink to delete this file that would be seen in a strace Call. For this app we can use the command

monkey -p com.ombththz.ufqsuqx -c android.intent.category.LAUNCHER 1 && set `ps -A | grep com.ombththz.ufqsuqx` && strace -p $2

to see the syscalls in real time. We have not observed unlink used in this example, so this iteration did something different. Java provides a method in java.io.File called deletewhich doesn’t trigger it unlink system call. With this script we can detect when this method is used, know about the file that was attempted to delete and make it a non-operation:

var file = Java.use("java.io.File")

file.delete.implementation = function(a){ 
  console.log("=> Detected and bypassed Java file deletion: ", this.getAbsolutePath());
  return true;
}  

The first deleted files are of interest:

=> Detected and bypassed Java file deletion:  /data/user/0/com.ombththz.ufqsuqx/tyfkjfUjju/HjIgfhjyqutIhjf/tmp-base.apk.gjGyTF88583765359401054429.88g
=> Detected and bypassed Java file deletion:  /data/user/0/com.ombththz.ufqsuqx/tyfkjfUjju/HjIgfhjyqutIhjf/dfGgIgyj.HTgj
=> Detected and bypassed Java file deletion:  /data/user/0/com.ombththz.ufqsuqx/tyfkjfUjju/HjIgfhjyqutIhjf/base.apk.gjGyTF81.88g
=> Detected and bypassed Java file deletion:  /data/user/0/com.ombththz.ufqsuqx/tyfkjfUjju/HjIgfhjyqutIhjf
=> Detected and bypassed Java file deletion:  /data/user/0/com.ombththz.ufqsuqx/shared_prefs/multidex.version.xml.bak
=> Detected and bypassed Java file deletion:  /data/user/0/com.ombththz.ufqsuqx/shared_prefs/pref_name_setting.xml.bak
=> Detected and bypassed Java file deletion:  /data/user/0/com.ombththz.ufqsuqx/files
=> Detected and bypassed Java file deletion:  /data/user/0/com.ombththz.ufqsuqx/shared_prefs/prefs30.xml.bak
=> Detected and bypassed Java file deletion:  /data/user/0/com.ombththz.ufqsuqx/files/all_tor.zip

As soon as we issue one adb pull to download the base.apk.gjGyTF81.88g File from the device that we can use jadx again to see if this includes the missing class definitions referenced in the manifest.

Examination of the unpackaged payload

Looking at these files, there is a string obfuscation method that appears thousands of times throughout the code, unchanged from instance to instance:

private static String $(int i, int i2, int i3) {
    char[] cArr = new char[i2 - i];
    for (int i4 = 0; i4 < i2 - i; i4++) {
        cArr[i4] = (char) ($[i + i4] ^ i3);
    }
    return new String(cArr);
}

Wherever we see a call that looks $(166, 217, 28670) in the code it refers to this function and uses the $ Variable in the same scope to return a string. We can use a Java sandbox like this to define the local scope $ variable, the $ method and output the decoded string.

in the sources/com/ombththz/ufqsuqx/bot/network/TorConnectionHelper.java We see a method that looks like a promising lead loadAdminUrl. decryption of $(556, 664, 4277) we get a base64 encoded onion address:

http://loa5ta2rso7xahp7lubajje6txt366hr3ovjgthzmdy7gav23xdqwnid.onion/api/mirrors

This address is available over the Tor network and contains a base64-encoded URL that points to the Command and Control (C&C) server, the server from which the malware operator issues commands. The author of this post contacted the Tor project on March 7th and informed them about this C&C server. With app bootstrap, the Tor network is connected by code adopted by Orbot to discover the C&C server, and then the Tor connection is immediately disconnected. The domain was referenced when this study was first performed yuuzzlllaa.xyzbut this has since changed zhgggga.in. Upon accessing, we see a login page for the C&C server admin:

C&C login page

One of the key features of the Tor network is censorship resistance. Being able to access the Tor network allows you to access information and websites that, due to the way the network is built, cannot be easily removed. This is a good thing for dissidents in censorship regimes or whistleblowers trying to get privileged information to reporters: the services they rely on will be available even when their opponents don’t want them to. However, this is a double-sided coin – in this case, malware is also able to direct victims’ devices to C&C servers in a way that cannot be shut down. There is no way to have one without the other and keep the integrity of the network intact. In this case the clearnet domain yuuzzlllaa.xyz was probably switched off after notification and the malware operator then spun up another domain under zhgggga.in without much disruption to malware command and control. In these cases, reporting malicious C&C domains seems like a game of wack-a-mole: as soon as you take down one, the next one pops up.

In the file com/ombththz/ufqsuqx/bot/DexTools.java we see an interesting method run()which loads a phase 2 payload from the admin C&C url path /payload. That is a dex File that can be decoded jadx to an App ID of com.fbdevs.payload. Unfortunately, for our analysis, this file contains mostly uninteresting and non-malicious code.

With view on om/ombththz/ufqsuqx/bot/components/ Path, many of the components appear to be inherited directly from Android BianLian Malware whose excellent analysis can be found here. One of the components that was not included in this previous iteration is under the socks5 Path that opens a proxy server to a specific host to receive commands and launch attacks. All components are activated and controlled by the C&C server via a Firebase Cloud Messaging (FCM) connection, allowing messages for specific devices.

fight against malware

Despite relatively state-of-the-art techniques employed to thwart analysis, some powerful, publicly available, open-source tools were used to break the flow of control and reverse-engineer this sample. More sophisticated malware detects hardware profiles and can determine that it is running in an emulator and change its behavior to further hide its core functionality. Still others deploy malicious code in lower-stage payloads to further bury its true behavior. However, this example shows how a few simple steps can be taken to peel back these layers to eventually discover the flow of control of a new class of malware. In the future, other samples in this class can be analyzed in the same way to track changes in the ecosystem and how malware authors respond to attempts to mitigate their effectiveness.

Analyzing malware and tracking its evolution is important to combat it. This not only leads to better signatures for using and protecting users from antivirus software, but also helps us to understand what protections are required at the operating system level and provides recommendations for platform security. At times, this can result in C&C servers being shut down, giving the botnets’ targets a much-needed respite. And finally, it gives users visibility into the software running on their devices so they can take back control.

Share.

Comments are closed.