The Evolution of Phishing-Based Malware Attacks on Banking Apps

For years now accessibility services have often been used by Android malware to target bank applications. As a result, banking applications started protecting themselves against this threat. However mobile application protection is a cat-and-mouse game and attackers have made their next move. In this blog post, we will quickly go over how the attack used to work, and the protections implemented to counter it. Then, we will show how attackers are now tampering with mobile bank applications to remove those protections. Finally we will explain the new protections you should implement to mitigate this threat.
Accessibility service abuse as the initial target
Since 2022, much of the fraud performed on Android mobile banking apps has been performed through accessibility services abuse. Accessibility services are a useful set of Android features that help people with disabilities use their devices. One such service offers the ability to read any text displayed by an app or entered by the user; it can then also perform user actions.
Because these capabilities are both powerful and universally available, attackers started creating malicious applications that could abuse this accessibility service in order to spy on or control mobile banking applications. Recent threats like Crocodilus or TrickMo offer prime examples. A threat actor uses phishing to trick a user into installing a malicious application on their device. Malware that successfully gains accessibility services permissions can then do things like intercept login information as it is typed into a legit banking app or use screen overlays to manipulate the user into disclosing sensitive information. The application can then automate fraudulent fund transfers without the user’s knowledge.
If you want to learn more on how it works in detail, we covered it in depth in this blog post.
It’s quite easy to detect usage of accessibility services on a device. It’s much harder, however, to only detect the abuse instances – and avoid punishing the users who have a legitimate need for these functions. To help protect against these types of attacks, we released a comprehensive guide on how to implement defenses (including code samples) as part of our security research center as well as built these features into DexGuard, so you can conveniently apply the recommendations to your application. An application using these protections cannot be targeted by accessibility services attacks.
But anti-fraud is always a cat-and-mouse game. Threat actors can make real money by targeting mobile banking apps and other financial services applications, so they have adapted their tools and techniques over time.
Today’s tampering and repackaging attacks
Since the beginning of the year, we have observed an increase in a variation of the previous attack which relies on mobile application tampering. Let’s start by looking at how a tampering attack works. Similar to many attacks, the attacker will start by reverse engineering the application to locate interesting code to tamper with.
Then, to perform the actual tampering the attacker will:
- Disassemble the application to get the smali code.
- Modify the smali code.
- Repackage the application.
The first and third steps are quite generic. For a Java application, it can be done using apktool – outputting .smali
files, which are text files containing the assembly code of the application.
During the second step, the attacker will modify those .smali
files by adding or removing instructions directly in the file. While it might sound complex, this is actually very easy to do. Let’s imagine you have a LicenseUtil class in your app, with a function checking if the license is expired:
The associated .smali
file will look like this:
In order to bypass the verification, an attacker can simply modify the .smali
file like this:
Once the application is repackaged with this tampered file, the isExpired method will always return False.
Once the tampering is done, the attacker needs to distribute the repackaged application. This step is more social engineering than technical. The fraudster will initiate a phishing campaign (e.g., sending SMS to distribute links to third-party websites hosting the fraudulent applications).
Now that we understand the basics of a repackaging attack, let’s take a look at how attackers have recently been attempting to bypass accessibility (or “a11y”) checks on banking apps that implement some malware protections.
Bypassing a11y checks
Local bypass of a11y checks
Most common a11y checks rely on first recovering the list of a11y services installed on the device. This list can then be correlated with other malware indicators (e.g., sideloaded apps, device admin permissions, package name whitelisting/blacklisting).
Here is an example of how this can be achieved for an application with a11y and whitelisting:
We have observed that attackers are bypassing these types of checks in the following way:
Patching smali code is very easy to do and only requires adding two instructions:
While patching smali code in this way isn’t very challenging, figuring out which class/method to patch can require a lot of reverse engineering. But not in this case:
- Smali are just text files.
- The attacker wants to get all instances where there is the
Landroid/view/accessibility/AccessibilityManager;->getEnabledAccessibilityServiceList
string. - To achieve this, the attacker can just use grep.
In order to make the identification of those classes/methods more difficult, it is possible to obfuscate your code. Just keep in mind that you need to use a real obfuscation tool – not something that just renames classes/methods (e.g., R8, Proguard). Indeed, when your application is calling a system API, it needs to use its real name, so R8/Proguard can’t rename it hence the pattern remains. Later, we will see how purpose-built obfuscation tools such as DexGuard can be used to mitigate this
Bypassing a11y globally
We have observed another technique which uses a Proxy pattern to simultaneously target all a11y checks. This is typically carried out in three steps.
First, the attacker creates a proxy class that changes the behavior of the AccessibilityManager:
Next, the attacker creates a class to replace the original AccessibilityManager using its own MaliciousAccessibilityManager:
Finally, the attacker can call this early in the original application lifecycle (e.g., during attachBaseContext):
With this approach, the attacker doesn’t actually have to locate all the checks in order to bypass most of them.
Bypassing screen recording protections
Another generic patch that we have observed is targeting screen recording protections. On Android, an application can prevent screenshot or screen recording at the Activity level by specifying a flag:
In Smali, the code will look like this:
Note that 0x2000 is the value corresponding to FLAG_SECURE.
To bypass this protection, the attacker can simply remove the call to setFlags:
Because the newly patched application no longer calls setFlags, the activities of taking screenshots or making screen recording are no longer prevented.
Similar to an a11y check bypass attack, an attacker can easily figure out which Activity is setting flags by searching for the string Landroid/view/Window;->setFlags
and then verifying that the constant is equal to 0x2000.
Again, proper code obfuscation is needed to make this protection harder for malicious actors to identify.
Bypassing anti-tampering protection
Anti-tampering is a feature of runtime application self-protection (RASP) that allows you to be confident that your mobile application has not been cloned or modified. It is a critical security capability that both protects your company (e.g., a cloned app can bypass fraud protections, lead to compliance issues, and negatively impact your public brand) and your users (e.g., a cloned app may embed malicious code that will steal user credentials or initiate unauthorized account actions).
We previously described how attackers can remove malware detection from mobile banking apps by modding and redistributing a malicious clone. But since most banking apps today are already using anti-tampering controls, attackers are now targeting anti-tampering protections as well. Our analysis revealed two different techniques being used for this by threat actors.
Generic anti-tampering bypass
Attackers can choose from a wide assortment of open source tools designed for bypassing anti-tampering controls (e.g., APKKiller, ApkSignatureKill, ApkSignatureKillerEx). A popular one, MT Manager, is even distributed as an Android app that allows an attacker to directly tamper with and repackage an app from their Android device.
The specific technique used to bypass anti-tampering protection depends on which tool an attacker uses. Techniques include:
- Hooking an Android system API (such as PackageManager.getPackageInfo, SigningInfo.getApkContentsSigners) to return information corresponding to the original application.
- Hooking native code to change the file read by the APK. An example of that would consist of:
- Storing the original APK in the assets of the repackaged application.
- When the application starts, the attacker code will copy the APK from assets to <package_id>/original.apk.
- Hooking the open function from libc; when the RASP code tries to open the application (which is stored in /data/app/<app_dir>/base.apk), it will replace the filename by /data/data/<package_id>/original.apk.
- The RASP code will perform its verification on the original APK and won’t detect the repackaging.
- On top of those well-known techniques, several non-public mobile app tampering tools rely on more advanced techniques that we choose not to discuss here.
Depending on the RASP solution you are using, one of these open source tools may be enough to bypass your anti-tampering protection. In this case, the attacker doesn’t have to do any extra-work.
Targeted anti-tampering bypass
The threat coverage of a RASP solution is one important thing, but the way it is applied is another really important part. If the RASP code is not injected in enough places or is easy to locate, it can lead to a targeted bypass.
Let’s look at at a concrete example:
Let’s assume the function isTampered is able to detect all repackaging tools, what can the attackers do next?
Since there is no way to prevent the tampering detection, they will target the reaction instead:
-They can patch the code in the onCreate method which verifies if the application is tampered with to decide if it should crash or not:
-They can also replace the function isTampered by:
- This simple example shows several security properties that are very important if you want the best mobile application protection:
- RASP checks should be injected in multiple places in the code, not just at entry points of the application.
- RASP code should be entangled/merged with the application code to make it very hard to distinguish one from the other.
- Obfuscation should be used to prevent identification of RASP code.
New phishing flow
Since these attacks require tampering with a mobile application, it’s legitimate to question why an attacker would only patch a11y or screen recording protections. They could potentially patch more things in the app (e.g., adding code in LoginActivity to send user credentials to an attacker’s backend) and no longer have to rely on a11y to do anything at all.
One reason why they are doing this is because it is easier: they can rely on more generic code while limiting the amount of reverse engineering needed. Additionally, they might already have a11y malware installed on the victim device. This malware also has additional permission such as REQUEST_DELETE_PACKAGES
andREQUEST_INSTALL_PACKAGES
, which allows it to uninstall the legitimate banking application, then download and install the maliciously modified banking application. By doing this, they can keep using their initial attack.
How to protect mobile apps against new attacks
Attackers are smart and their tools and techniques are always evolving. So you need to adopt a multi-layered approach to protecting your mobile applications from attacks.
Obfuscation
As explained above, attackers first need to find the code they want to patch. To slow them down and avoid automation of the attack, it is important to apply proper obfuscation to the sensitive parts of your code.
So, what is proper obfuscation? In this blog, we saw that attacks rely on finding system API calls; so it becomes important to apply call hiding. For instance, here is the a11y protection before and after call hiding:
As you can see, system API calls are no longer visible. However, there are still some strings (e.g., “accessibility”) which can be used to locate the check. So you should also apply string encryption to remove those:
On top of that, you should consider using other features such as control flow flattening and class encryption to protect your most sensitive code.
Defining which code is sensitive is highly dependent on what your mobile application is doing – but you should at least obfuscate the code in charge of user or backend interaction, secure storage, as well the protections you apply to your code.
It is also important to note that while obfuscation won’t prevent the attack, it will increase the time needed to perform it. It also limits scalability by forcing the attacker to re-do the attack on each new version of your application.
To make the most of code obfuscation, it is important to choose a solution that is resilient enough to not allow automatic de-obfuscation. It should also be applied in a different way each time you build your application to “reset the clock” for the attacker.
Anti-tampering
Since the new attack flow relies on repackaging, it won’t be a surprise that anti-tampering plays a critical role in defending mobile applications against it. As we have seen, many techniques can be used to bypass anti-tampering protections. As such, it is essential to choose a mobile application security solution capable of detecting all known tools and preventing techniques used at multiple levels.
Additionally, it is important to correctly configure your RASP solution to ensure checks are injected correctly – with sufficient numbers of injections that are indistinguishable from your code. You also should make sure you react in a smart way and not leave additional traces which could help the attacker understand the detection.
Finally, you want to reset the clock for the attacker each time you release a new version of your application – for instance, by randomizing the places where RASP code is injected or changing the way it works or looks.
Threat monitoring
Knowledge is power. Threat monitoring will allow you to observe any attacks happening on your mobile app in real time. With that information, you can gain insights on the specific technique used by the attacker and then adjust your protections for the next release of your application. If you are already using DexGuard and want to get those insights, you just have to enable ThreatCast.
Secure application version fingerprinting
Sadly things can and will go wrong. As such, you need a disaster recovery plan. When you realize that “Version A” of your mobile application has been tampered with and you’ve now released a new “Version B” to replace it, you also need to prevent the older vulnerable version from being used.
To do that, you will have to send a version identifier to your mobile app’s backend. One common way to do this is to send the version from your Manifest file. However, the security level provided by this is very low; we have seen cases where attackers were also modifying the version in the Manifest to bypass verification checks. So, similar to anti-tampering protection, the way you are computing and sending the version identifier plays an important role in the actual level of mobile app security achieved.
Control your release cycle
Real attackers are not hacking your app for fun. They work like a real company and want to make money from their mobile app security attacks. So, you should try to limit their ROI.
Let’s consider a scenario where attackers need an average of two weeks to clone your mobile application:
- If you release a new version of your mobile app every eight weeks, then they can commit fraud for six weeks.
- If you release a new version of your app every three weeks, then they can commit fraud for only one week – which means less money for them.
- If you release a new version of your app every week, then they won’t be able to make money at all.
Keep in mind that:
- You probably can’t reduce your release cycle to one week without annoying your legit users.
- Attackers learn over time. So while it may take them two months to break your mobile app security the first time, they will probably go faster the second or third time they are doing it.
- This is especially true if you use basic protections – ones that don't rely on polymorphism, which helps reset the clock.
If you want to benefit from instant mobile application security policy updates and more dynamic threat detections without having to re-release your app, you should look into mobile app attestation which can offer some of these benefits.