We wrote about the malicious app Bumble in Part 1. We introduced a bit about its packing and how it communicates to the C2 server. In this post, we want to emphasize on 2 attacks that become popular on Android malware in the past few years. These are overlaying and abusing Android Accessibility Service (we will call it “AAS”).

Overlays

Displaying a window on top of other applications to prevent user from seeing things that the app does maliciously. Overlaying can be used to lure the user and let them fill in sensitive information on malicious app while it is displaying overlayed layer on another application.

The following example shows how a malicious app can use SYSTEM_ALERT_WINDOW to create an overlay ImageView:

The code produces a robot image on top of another application:

An attacking application creates an overlaying window
Overlaying window overlays the Chrome browser

But there are some critical window that this type of malware cannot overlay such as request permission or security settings.

Overlaying window cannot overlays the permission request window

The Bumble app was well designed enough to overcome this problem. Instead of using SYSTEM_ALERT_WINDOW it uses TYPE_ACCESSIBILITY_OVERLAY. With this type of Accessibility feature, the Bumble can create an overlay window over any applications on the device although on the Security Settings window which cannot be done by using SYSTEM_ALERT_WINDOW permission.

A malicious app create an overlay ImageView on the most top layer even over security setting like permission manager

The following image shows the excerpt of code that utilized TYPE_ACCESSIBILITY_OVERYLAY in the Bumble malware:

The following image shows the actual Bumble overlaying window, once the user enables Accessibility feature on the Bumble app, it creates an overlaying window to cover what it is doing such as requesting other permissions and grant them by itself (see next topic, AAS):

Overlaying window created by the Bumble app

Android Accessibility Service (AAS)

We have already introduced the Android Accessibility Service (AAS) in the part 1. For this part we will demonstrate a couple of features that could be used maliciously by malware.

AAS allows the application which its capability to perform actions more than a normal app can to help people with disability. In this article, we cover the following abilities:

  • 1. Read the content from the screen of any application
  • 2. Read the content from button that the user touched on
  • 3. Find a desired view (in this case a button) to perform an action on it
  • 4. Simulate touching instead of the actual user

With these mentioned abilities, it is enough for a malware to steal money from a mobile banking, transfer cryptocurrency from a crypto wallet application, and silently sniff information on applications targeted by the malware.

Because of the AAS is very powerful, Google tried to forbid applications that misused the AAS on its Play Store. See more, https://support.google.com/googleplay/android-developer/answer/10964491?hl=en

1. Able to Read the Content From the Screen

As mentioned in part 1, the accessibilityEventTypes="typeAllMask" directive declared in the service property file (.xml) allows the app to receive all types of Accessibility events. One of them is TYPE_WINDOW_CONTENT_CHANGED. With this even filtering, the app can be noticed if the content of the current window is changed regardless of the focused application (don’t care if the malware is in background or is running as a service). The content changes that cause this type of event to be triggered are, for example, navigate to another page, text changes, view changes, etc.

The following figure shows a sample victim application that requires username (email) and password with password in its login activity:

Sample login activity of the victim demo app

Even the view does not show the password to the user, but let see what AAS retrieves. The following picture shows the value retrieved from the username EditText node:

As expected, the email was retrieved in clear-text format. But let’s see, how about the password. The following pictures show how the password was retrieved from its EditText node:

The password we typed is “temp1234”, as you can see in the above pictures, the AAS can retrieve each character before it was converted to “*” to hide its secrecy. So no doubt, the full password can be also obtained.

At this point, because we don’t publish the source code of the demo applications, someone may have questioned if we wrote a vulnerable app to reveal or facilitate our demo AAS app to retrieve the username and password more easier ? How about this, go and use it against well-known application like Google Authenticator app. We use our AAS app to retrieve the 2FA code from Google Authenticator app:

2. Able to Find Which Button Clicked (Touched)

We’re curious if the AAS can retrieve the exact location on which object or view the user clicked on. We found something interesting. The click event can be retrieved by filtering on the event type TYPE_VIEW_CLICKED (more event types can be filtered, this is one of them).

Excerpt of code used for filtering Accessibility event types, in this case TYPE_VIEW_CLICKED

We use a calculator on Xioami Red Mi 8 application to test if we can retrieve the click event and it is as expected. The following picture shows the calculator button value when we type “785”.

The following pictures show how the value of each TextView was retrieved when it was clicked (the behavior is the same when it comes to Button class) This means even an app which has it own keyboard and random the pin location each time when it spawns, won’t be safe from AAS because it does not depend on location, it depends on the value on the button/textview or any clickable element.

Another word, we can say that the AAS leads to key logging.

3. Able to Find Desired Button and Simulate Tapping

Because the AAS retrieve a lot of information of the UI, we can use it to find an element that we seek. We demonstrate this case by using our AAS app to automatically request READ_SMS permission and find the “ALLOW” button before performing tapping on it. This allows the AAS app to automatically grant any permission it desires to perform another action like read SMS, send SMS, call phone, access location, read contact, write setting, prevent user from accessing specific setting and much more.

An app is allowed to request a permission during runtime. While requesting a permission, the com.google.android.permissioncontroller activity will be invoked and display the following window to let the user allow or deny that request. From the permissioncontroller window, it can be seen that there are 3 buttons which are “ALLOW”, “DENY”, and “DENY & DON’T ASK AGAIN”.

We need to find the “ALLOW” button in order to later click on it. The following code shows an example of finding a desired button using the AAS:

As can be seen above, after finding the “ALLOW” button, the nodeI.performAction(ACTION_CLICK) was called to perform clicking on that button.

FLAG_SECURE

FLAG_SECURE is one of the WindowManager.LayoutParams option that allows that developer to secure the Activity Window. In summary the following protections take place when FLAG_SECURE is set to an Activity:

  • Prevent screen capture
  • Prevent screen recording
  • Prevent screen sharing (e.g. casting to TV)
  • Prevent remote control app like TeamViewer and AnyDesk
  • Prevent background screenshot from being taken by the OS

For example, the following picture shows an activity that has FLAG_SECURE set when it is active.

Sample app with FLAG_SECURE on the View

The following picture shows the mctargetdemo1 app which its FLAG_SECURE set on its activity while being moved to background (switch app). The screen will be protected. The another picture (right) shows the app when its screen was taken but the FLAG_SECURE prevents it.

The FLAG_SECURE becomes useless when a malicious app utilizes AAS. The AAS can retrieve information from the UI elements that were protected by FLAG_SECURE without taking any additional efforts.

A login activity with FLAG_SECURE set

The following pictures demonstrates the username and password can be obtained using the AAS.

Retrieve the password (by each character e.g. “*****6”) from the view with FLAG_SECURE set using the AAS
Retrieve the username from the view with FLAG_SECURE set using the AAS

Even the FLAG_SECURE cannot protect the activity from the AAS, but it is necessary to prevent the app from screen capture/record and remote control application using share screen feature.

IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS

This flag can by circumvented, see our post Accessibility Check Bypass Countermeasure

For a View instance, the developer can use the flag IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS by calling setImportantForAccessibility() method. The method is used for determining how important the View is when the AAS queries the screen and when the app fires an AAS event. IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS flag tells the AAS event fired by the app that the node won’t be reported to AAS, so the AAS has no access to the View set by the flag.

For example we set the IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS on usernameEditText EditText as follows:

So the attacking app cannot access to data in the View (cannot access to the username field):

No usernameEditText EditText reported to the AAS.

Anyway, by setting the flag, we come across this question:

  • If your app supports accessibility to help people with disabilities, the IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS flag will prevent the AAS to access the information on that view.

Detect Accessibility Binding With Untrusted App

The Android Accessibilty Service is the official API for developer to enhance the functionality of the app to help people with disabilities to be able to use the app more easier. Most of the apps use the AAS for good purposes in the same way of Google’s intention. But some of apps misuse the AAS to steal data and making malicious transaction. As mentioned earlier, Google tried to prevent that kind of malicious apps to be listed on Play Store. But we cannot trust on every app event it comes from Play Store.

An application with AAS enabled on is one of indicators tell us that the application may be malicious. We can use this to flag on the application but it is not hundred percent precise so we need the user’s confirmation once we found applications with AAS (e.g. shaking the device to confirm usage while AAS is enabled).

The following excerpt of code can be used for checking if the AAS is enabled and currently bond to an activity of an application on the device.

public static boolean isAccessibilityEnabled(Context context) {
    AccessibilityManager am = (AccessibilityManager) context.getSystemService(Context.ACCESSIBILITY_SERVICE);

    List<AccessibilityServiceInfo> runningServices = am.getEnabledAccessibilityServiceList(AccessibilityEvent.TYPES_ALL_MASK);
    for (AccessibilityServiceInfo service : runningServices) {
        Log.d("[kapiD]", service.toString());
        // if (ids.contains(service.getId())) {
        // Compare your whitelist with the service ID
        //}
        return true;
    }
    return false;
}

// Use it somewhere, usually at onCreate(), onResume(), onStart(), or during sensitive actions like transfer money/crypto and 2FA 
[...]
if (isAccessibilityEnabled(this)) {
    Toast.makeText(LoginActivity.this, "[!] AAS enabled app found, ask user", Toast.LENGTH_LONG).show();
} else {
    Toast.makeText(LoginActivity.this, "[!] No AAS enabled app found", Toast.LENGTH_LONG).show();
}
The demo victim app detects AAS binding on another app (malicious app)

Check Installing Package of Suspected App

This check can by bypassed, see our post Accessibility Check Bypass Countermeasure. In short, implement signing information check to prevent it.

Android provides APIs for checking the installing package name of an app. This allows the developer to retrieve information about which package was used to install an app. We introduce 2 APIs

  • For API level 29 and lower, the app can use the API PackageManager.getInstallerPackageName() for obtaining the installing package name of an app.
  • For API level 30 and higher, the app can use the API PackageManager.getInstallSourceInfo().getInitiatingPackageName(). We recommend using the getInitiatingPackageName() instead of others because the installing package name can be changed by calling PackageManager.setInstallerPackageName(String, String), getInitiatingPackageName() returns the initial installing package name

The following codes demonstrate example of checking the installer of a package:

boolean verifyInstallerId(Context context, String packageName) {

    String installerPackageName = null;
    Boolean result;

    List<String> trustedInstallers = new ArrayList<>(Arrays.asList("com.android.vending"));
   
    // Kapi: getInstallSourceInfo() comes with SDK 30+, otherwise use getInstallerPackageName()
    // Kapi: Because the installing package name can be changed by calling PackageManager#setInstallerPackageName(String, String), getInitiatingPackageName()  return the initial installing package name
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
        try {
            installerPackageName = context.getPackageManager().getInstallSourceInfo(packageName).getInitiatingPackageName();
            Log.i("[KapiD]","[!] Package: " + packageName + ", installer (SDK 30+): " + installerPackageName);
        } catch (PackageManager.NameNotFoundException e) {
            Log.i("[KapiD]", "[!] Package not found: " + packageName);
        }
    } else {
        installerPackageName = context.getPackageManager().getInstallerPackageName(packageName);
        Log.i("[KapiD]","[!] Package: " + packageName + ", installer: " + installerPackageName);
    }

    // Kapi: Compare the installer with trusted installer packages
    result = trustedInstallers.contains(installerPackageName);
    
    return result;
}

The installing package name can be various, for example:

  • When an app was installed from Google Play Store we found that the installing package name will be com.android.vending
  • When an app was installed by downloading from a web application, the installing package name will be com.google.android.packageinstaller
  • When the app was installed by ADB, the installing package name will be null

Summary

Android malware becomes popular way for criminals to steal data or make a malicious transaction on an Android device. Google tried to prevent abusive usage of the AAS by preventing apps that misuse the AAS from being listed on Google Play Store. But the criminal still can trick users to install a malicious app in some ways or another. An effective way to reduce this kind of mobile app cybercrime, in our opinion, is incorporation between developers and users.

The developers should pay attention on security more than before. When a new technology is introduced, it’s likely to be someone who tries to use it in an unintentional way. We, as a part of security community, try to help communicate and provide advisory to developers by researching and writing useful (maybe) articles. We hope this article could help both developers and users to have more understanding on the malware technique and could apply practice in real life.

The users should takecare and be aware on what they are doing. There are 2 things we want to highlight is do not install an app outside the official store. Even we know the website, know the people who bring the app, or even we’re developers that does not mean we know what the app is doing indeed. Another one, is do not give the Accessibilty feature to any app unless the app is known and needed.

Things that users can do when you know that you have installed a malware:

  • Disconnect from the internet in every way (Wi-Fi, Cellular, etc.)
  • Consider Factory Reset your phone
  • Consider changing passwords of your accounts that used to access on that phone

References