
Hack-proof mobile applications: hardening tips from a security pro
Developers of mobile applications are faced with the same problem that their desktop counterparts have been facing for years: once you have finished building your application, you have to distribute it to your users who are free to do whatever they like with it.
Users are free to pull your application apart, look at what is inside, modify it and in some cases, redistribute it and steal intellectual property.
Unfortunately, allowing users the freedom to handle your application in this way has consequences beyond intellectual property theft. Attackers are also free to look at your application in order to identify vulnerabilities, disable security controls or modify it in order to conduct further attacks. This article will outline various application hardening techniques which aim to defend applications from being inspected and manipulated in this way.
Mobile attack methodologies
Let’s consider two attacks conducted against mobile applications. One attack looks to identify and exploit issues with the application itself, while the other attempts to use the application in order to target its server-side component.
Scenario one
The first attack is where attackers directly look for vulnerabilities or weaknesses in a mobile application in order to achieve their goal. An example of this kind of attack might be where an attacker identifies some unencrypted user data being stored on the device which can be stolen and used elsewhere in order to steal banking data from the victim. The attacker may then choose to create malware to harvest this information from infected devices, and attempt to distribute the malware to their victims in the usual manner, such as phishing or drive-by downloads.
In this scenario, the attacker has moved through the steps within the following high level methodology, similar to Lockheed Martin’s Cyber Kill Chain [1]:
The attacker has conducted reconnaissance to identify the vulnerability, created a piece of malware to exploit it during weaponisation and then delivered the malware to the victim users. At this point, the malware can exploit the vulnerability and carry out its objective to steal money.
Scenario two
The second attack is where the attacker uses the mobile application in order to carry out further attacks against the server the application speaks to. An example of this attack would be where the attacker modifies the client application in such a way that the attacker can freely and repeatedly send specially crafted messages to the server in order to assess the communication protocol for vulnerabilities.
The attacker in the second scenario will follow a very similar set of steps when attacking the server, however a few steps are added at the beginning where they assess and modify the mobile client:
Notice how the first two steps of both methodologies are exactly the same. An attacker must always conduct reconnaissance and weaponisation against the mobile application before following either path. Since these steps are mutual to both attack paths, and are near the beginning of this chain, it is an ideal place to stop or slow an attacker down.
Attacking an unhardened application
Let’s consider an attack against a hypothetical Android application, MWR Pay. MWR Pay is a reasonably secure application, which features controls such as SSL pinning, two factor authentication and makes use of the client-server model. This attack will be similar to the one described in scenario two.
During the reconnaissance stage, our attacker will be reverse engineering and debugging the application. Since MWR Pay is written in Java, it is reasonably easy to convert the intermediate Java byte code into human readable Java code. Once this has been done, our attacker is free to analyze the structure of the application, identify where our security controls such as SSL pinning are implemented, and start looking for vulnerabilities in our application such as logic errors which surround critical functionality.
Also during reconnaissance, the attacker may decide to debug the application and watch it execute, to gain a finer understanding of the areas of interest which were identified during reverse engineering.
As our attacker moves into the weaponisation stage, they are likely to need elevated permissions on the device in order to start carrying out modifications such as hooking and patching. As such, the attacker is likely to root the device, which is easily done on many devices due to publicly available code available for this purpose.
Once the device has been rooted, the application can now be hooked in order to have its program flow modified during runtime, which is particularly useful when circumventing existing protections within the application. For example, an attacker who wants to communicate with MWR Pay’s server will want to disable or modify the SSL pinning routines – a task which can be achieved via runtime hooking.
Finally, it is possible to directly modify the application binary to achieve the same effect as the previously described hooking. Patching an application may often be seen as a last resort to an attacker, but is certainly a possible avenue of attack if runtime hooking doesn’t do the job. Directly modifying the binary requires some detailed and intricate knowledge on the part of the attacker, whereas runtime hooking can be achieved relatively easily using well documented, publicly available tools.
At this stage our attacker has gained an intimate knowledge of MWR Pay’s internals, has modified it to remove security controls and can send specially crafted messages to the server. The attacker is now in a position to start assessing MWR Pay’s server.
Attacking a hardened application
MWR Pay 2.0 features a series of protections to guard against being debugged, hooked, modified and run on a rooted device. It also features code level obfuscation. Let’s consider the same attacker carrying out the previously described reconnaissance and weaponisation steps.
Reverse engineering is carried out in the same manner as described during the previous attack however, this time the attacker cannot convert the Java byte code into human readable Java code due to the obfuscation that is in place. The application still functions in the same way, so it is entirely possible to determine how the app works just as before, however, now it requires hours of scrutiny in order to extract even the smallest information regarding program flow.
When the attacker attempts to run the application on a rooted device, debug it or hook it as it runs, the application now crashes. This is because MWR Pay’s protections are detecting that the execution environment should not be trusted and is halting.
Regarding debugging, the application’s anti-debug protection is very basic: MWR Pay now queries the operating system to determine if it is being debugged and halts execution if this test is positive. MWR Pay’s guard against being run on a rooted device is to check the file system for files or packages which are associated with a root user. Finally the last of MWR Pay’s new protections concerns maintaining the applications’ integrity, which is achieved by runtime verification of code segments against known values.
Many of these protections are somewhat simple and can be bypassed by the attacker, however at a significant cost. It is not easy for the attacker to know how each protection has been implemented within the application due to the various ways they can each be achieved, and as such bypassing protections is often a process of slowly and blindly eliminating each possibility.
The difficulty of circumventing a single protection is also greatly increased due to the presence of other protections. For instance, when attempting to bypass the root user detection, code obfuscation makes the task of identifying how the application verifies the presence of a root user significantly harder.
Being reasonable
Implementing all of the discussed protections takes time and effort on behalf of the developer, not only the attacker. The primary disadvantage of these types of protections is that an amount of maintenance is required in order to have them operate properly across many different versions of operating system and devices. The advantage however, is the ability to defend against an attack during its early stages.
These kinds of protections are not designed to make an application secure and free of vulnerabilities, but they are designed to slow and frustrate attackers such that they cannot progress along their methodology and identify that the application is vulnerable.
A large amount of time and effort has to be spent just to start finding weaknesses within the application and as such, an application without any vulnerabilities would have no use of these protections. Since it is impossible however to make such a statement about any application, these protections should be considered as part of a defence in depth approach which aims to thwart attackers at every stage of their attack, including reconnaissance.
About the author:
Luke Drakeford is security consultant at MWR InfoSecurity – mwrinfosecurity.com
Related articles:
Cutting down on personal data leaks
IoT cybersecurity: is EDA ready to deliver?
Fully encrypted? Watch your back!
[1] https://www.lockheedmartin.co.uk/us/what-we-do/information-technology/cyber-security/cyber-kill-chain.html
