Recently, several research efforts related to the security of the Android mobile platform showed how often Android applications are affected by severe security vulnerabilities. During the last summer, we decided to investigate how benign and malicious Android apps use a feature that the security community identified as problematic a long time ago: remote-code-loading functionalities.
Our findings were surprising: in fact, we found a number of very popular Android apps (each with more than one million users) that were affected by remote code execution vulnerabilities! Our paper related to this work appeared at NDSS’14, and it was presented this week in San Diego. While we suggest to check out the full paper, here we provide an overview of our findings.
Android apps are allowed to load additional code dynamically from (mostly) arbitrary sources. There is no permission check or anything similar. Once an app is installed on your device, it can even download code from the Internet (provided that it has Internet permission). We figured that sounded a little risky, so we decided to have a deeper look.
Intuitively, there are two problems with dynamic code loading:
- Malicious apps can download dangerous code onto your device. The harmless bike tracking app that you just installed might start downloading code that sends expensive premium SMS. Unfortunately, the providers of Android markets (such as Google Play or the Amazon app store) run malware scanners centrally at the store, and hence those scanners have no chance of ever seeing the code that an app downloads at runtime if it doesn’t want them to see!
- Benign apps might make use of such “feature” for legitimate reasons (e.g., a game loading additional levels at runtime), but what if the developers didn’t think about security? Or, as it happens more often than one would think, what if the developers failed to properly implement the security checks? Well, if an app doesn’t verify the code it loads, then attackers might find their ways to inject malicious code, and hence a single carelessly designed app could open your device to the evil guys out there. For example, this very problem has just been discovered in the CyanogenMod updater.
We set out for a thorough analysis of both aspects of the problem. In a first step, we identified different ways for apps to load external code. A popular and well-known technique is DexClassLoader, a Java class loader that allows you to load Java code from files (which you might just have downloaded). Another way is to create a so-called app context for other apps, which allows you to run their code. Please refer to the paper for a full list of techniques.
The one thing that all the code-loading techniques have in common is that the Android system does not check the code you load. There is no verification that the code has been approved by anyone or originates from a trustworthy developer. iOS, in contrast, strictly enforces that any code run on the device be signed by a certificate chain that ultimately leads to Apple.
After systematically studying how a given Android app can load code, we went on to investigate the two scenarios that we mentioned above. Remember? (1) Malicious apps evading detection and (2) vulnerabilities in benign apps.
It turns out that malicious apps can use dynamic code-loading techniques to evade the centralized malware analysis systems that are common in the Android world (such as the Google Bouncer, used by the Google Play store). Their centralized nature, i.e., the fact that apps are usually analyzed in the app store rather than on the users’ devices, makes the protection systems unable to cope with dynamically loaded code. As an experiment, we published an app on Google Play that safely downloads and executes (innocuous) code from a web server we control. Monitoring requests to the web server we discovered that, during the verification process performed by the Google Bouncer before accepting our app, the remote code was not even accessed.
In this blogpost, however, we will focus on the second aspect: the risk of vulnerabilities in benign apps.
We developed a static analysis tool (based on backward slicing) that is able to detect unsafe usage of code-loading techniques in Android apps with high accuracy. (It is important to stress that an app using such loading techniques is by no means always malicious. Have a look at the paper for a full list of the different legitimate motivations we discovered.)
In an attempt to assess the percentage of vulnerable apps, we ran our detection tool on a set of 1,632 apps from Google Play, each with more than one million installations. Disquietingly, we found that 9.25% of those apps are vulnerable to code injection. The situation among the top 50 free apps is even worse: in fact, we found that 16% are vulnerable!
Among the vulnerabilities that we found was a possible code injection attack against an advertisement framework called AppLovin. We notified the developers of this framework in July 2013, and they acknowledged the large security impact and suggested a fix within hours. We agreed with them to not publicly disclose the vulnerability until the fixed version of the framework was incorporated into a substantial amount of the vulnerable apps. This same vulnerability was then publicly disclosed in November 2013 by MWR. We’d like to stress how fast the AppLovin developers reacted to our discovery and encourage other companies to follow their example.
Finally, we designed a protection scheme that will protect Android users from vulnerable benign apps as well as malicious apps in the context of dynamic code loading. You can find the full details in the paper – here, we’ll just give a brief overview.
The basic idea behind our protection mechanism is code signing: Every piece of code that an app wants to execute must have been signed off by someone you trust. Now, we don’t want to depend on a single company for this signing business. Therefore, our system allows the user to choose trustworthy judges. Anyone is free to start analyzing Android code and providing signatures (actually, we use something that is similar to signatures, but not quite the same), and the users decide whom to trust.
We enforce our code checks by modifying Dalvik, the Java environment in Android. (Dalvik will be replaced by a new system called ART soon, but that’s not too much of a problem for the protection scheme.) Unfortunately, this means that you have to install a new version of Android in order to deploy the protection system. We are aware that this makes its wide adoption considerably harder, but we believe that the compromise is worthwhile because of the strong security guarantees that our system can provide: we can’t protect every existing Android device, but we offer greatly improved security for future devices. It is our hope that the concepts of our protection system will find their way into a future “official” version of Android.
If this short outline of our work has attracted your interest, check out our full NDSS paper to learn the details: which code-loading techniques we found, why benign apps use them, how we circumvented the Google Bouncer and how our protection system works under the hood, to name just a few things!