Protect your customer data and your reputation with our state-of-the-art security
Secure valuable gaming revenue streams & maintain user trust with our Unity integration
Secure your e-commerce revenue & safeguard data by layering mobile app protection
I often talk with security and development managers at companies whose applications have been recently attacked. One common theme in these conversations is that they view the whole security incident through the prism of the last stage of the attack. This perception makes them focus too much on certain technical aspects of the attack without taking the holistic approach to their application security.
In this blog, we’ll explore why it’s important to take a step back and think about the big picture. We’ll do this in two parts, focusing on how a typical mobile application man-at-the-end attack unfolds, and which steps a malicious actor takes to achieve their goals. In this post, we will go over the stages of the attack, talk about the most important aspects of every stage, and review some countermeasures you can take as an application developer.
Let us temporarily take the attacker’s perspective for once, and go through the stages of the attack, one-by-one.
The four major attack stages are:
Before an attack can be executed, an attacker has to carefully study the target application to find possible attack vector candidates.
Visual application inspection is important, as it gives the attacker a better understanding of the user workflow, and enables them to find application behavior patterns or user interface “anchors” that can be used to locate a place of interest in the code. For example, such “anchors” could be:
The key to success in this stage is to locate such “anchors” that can be easily identified both in the application user interface and in the application binary. Using this information further down the line during reverse engineering can drastically reduce the amount of code that needs to be read and understood by the attacker. This ultimately translates to less time required to perform the attack.
Here’s an example of how the recon stage could unfold in a freemium mobile application that requires payment to unlock the premium part. An attacker would:
A good reconnaissance can save an attacker hours and even days spent in reverse engineering.
What can you - as an application developer - do to deter this attack phase?
Focus on obfuscation of the application elements that can lead an attacker to a sensitive place in the code. For example, obfuscating the data strings used in the free/premium logic would make it harder to establish a link between the application behavior (displaying a message) and code (a specific function that displays that message).
During the reconnaissance phase, an attacker would locate the place in the code where the application performs the logic that would need to be changed.
The next phase is to execute the attack, which is to modify the application so that the attack goal is achieved. At this stage the focus is on making it work on the attacker’s device, so they can - and likely will - make full use of the compromised execution environment.
One of the easier ways to go about this is to directly access the target process memory and make the required modifications. On a rooted or jailbroken device, an attacker would have the necessary access to perform this operation. Nowadays, there are many tools at an attacker’s disposal to make the task easy, such as Frida or radare2.
Exploring and modifying the target process memory is a quick way to try different options prior to committing to a specific one. A reverse engineer could enumerate classes and functions, modify behavior of specific functions, or even replace them with entirely new implementations.
For example, a function, which checks if a premium product version was purchased, could be easily replaced with a stub that always returns a true value, bypassing all the corresponding checks.
Figure - patching application code in-memory with Frida
What can you do to deter this attack phase?
Focus on preventing debugging, hooking, and repackaging of the application. This would require adding a special logic to the application that detects such attempts and routes the execution path down a different direction.
As you can see, the modern tools available to reverse engineers make it easy to analyze and modify the application’s behavior at runtime. However, it’s still difficult for a non-developer to reproduce the attack.
This creates a new question for the attack actor: How can the results be made available for everyone else so all they have to do is download a modified version of the application?
For example, on iOS, there are two ways to do it: create a tweak for jailbroken devices or distribute a repackaged application. In the first option, the repackaged version can be sideloaded with a free Developer account.
But let’s consider the second method. The advantage of repackaging the application is that it will not require the user of the modified version to jailbreak their device.
To get the job done, the attacker would use a static analysis tool, such as Ghidra. Such tools would provide both a disassembler and an assembler to quickly write and update the application code. Using a static analysis tool, the actor would change the code of the application on disk to replicate the attack logic that was determined during the previous stage.
Figure - patching application code with Ghidra
Once finished, the application would be saved on disk, repackaged, and uploaded to one of the pirate stores on the Internet.
What can you do to deter this attack phase?
This stage of the attack is the most visible for the application developers. This is because the application will leave the reverse engineer’s machine for the first time at the Distribution stage and will become available for download. As a result, the countermeasures the application developers take against unauthorized modifications will often be biased toward prevention or deterrence of the distribution of modified apps. This could include, for example, integrating anti-repackaging checks in the application.
Worth noting is that this tactic, when applied on its own, will usually not deter the attackers for long, since the check will be found and disabled during the earlier attack stages.
The final stage that concludes a successful attack is automation of the execution and distribution process.
Usually, reverse engineers who specialize in making unauthorized modifications of mobile apps have hundreds of apps in their portfolio. Repeating all the attack phases for every application version can be too taxing for them, driving the lead time of new mods up and the size of their portfolio down.
During this stage, the attacker would think which code entities they would use to make the next attack be targeted and executed automatically. More often than not, these can be the names of specific entities, such as classes or methods, or exact code signatures of the functions where the attack takes place.
This approach relies on the fact that the method names and code signatures do not change too often in a real-world application software development life cycle.
For example, the attack actor could make use of the fact that the function to check the license key is always called checkLicenseKey(). Alternatively, if the application is obfuscated without any randomization between the runs, the attacker could just as well account for an obfuscated name, like klaj9129u().
But that does not conclude the list of possibilities. A way to locate the procedure could, for example, use a code signature, knowing that the target function would contain a certain sequence of CPU instructions.
Using this information, the attacker would write a simple script that would locate the targeted code entity, make a modification, and repackage the application. As a result, the modified application may appear on pirate websites only minutes after the new version hits the app store.
What can you do to deter this attack phase?
Code hardening your application against this stage is not easy, as it requires you to remove any recognizable and repeatable signatures in your security-sensitive code. In practice, this requires use of specialized tools, such as DexGuard and iXGuard, that completely change the code signatures with every run, effectively preventing automated application tampering.
A real-world attack on a mobile application is a complex process, which consists of several distinct phases.
In this man-at-the-end attack scenario, an application developer must think of all phases of the attack when planning for an adequate security response. Deterring attackers at earlier stages will pay off later as it raises the entry bar for any bad actors.
At the same time, it is not correct to think that only high-profile skilled reverse engineers can get past all the application defenses. Once the initial analysis is complete, the attack methodology is automated and the results become available for a wide audience who does not have to have any technical or security expertise.
The business risks of such an attack may vary depending on your application, and include financial damage, reputation damage, regulatory incompliance, or privacy breaches.
Therefore it is important to continuously plan, implement, and monitor the outcome of effective security measures for all attack phases.