Android App Penetration Testing & Frida Hooks

Crafting Custom Frida Scripts: Full Control over Android WebView JavaScript Interfaces

Google AdSense Native Placement - Horizontal Top-Post banner

Introduction

Android applications often leverage WebView components to display web content, whether it’s an internal help page, a login portal, or even a full-fledged web application. A common practice for bridging native Android functionality with JavaScript running within the WebView is through JavaScript interfaces. These interfaces allow JavaScript code to invoke Java methods directly, opening a powerful channel for communication. However, if not implemented carefully, they can introduce significant security vulnerabilities, potentially leading to remote code execution (RCE) or sensitive data exposure. This article dives deep into using Frida, a dynamic instrumentation toolkit, to identify, inspect, manipulate, and even inject new functionality into these WebView JavaScript interfaces, granting penetration testers and security researchers unparalleled control over the application’s runtime behavior.

Prerequisites

Before we begin, ensure you have the following tools and setup:

  • A rooted Android device or an emulator (e.g., Android Studio AVD or Genymotion).
  • Frida-server running on your Android device/emulator.
  • Frida-tools installed on your host machine (pip install frida-tools).
  • adb (Android Debug Bridge) configured and working.
  • Basic understanding of JavaScript and Java.

Understanding Android WebView JavaScript Interfaces

The core mechanism for exposing Java objects to JavaScript in an Android WebView is the addJavascriptInterface() method. When a Java object is added using this method, all its public methods become accessible to the JavaScript context via a named interface. For instance:

WebView webView = findViewById(R.id.myWebView);webView.getSettings().setJavaScriptEnabled(true);webView.addJavascriptInterface(new MyJavaObject(), "AndroidBridge");webView.loadUrl("file:///android_asset/mypage.html");

In the corresponding mypage.html, JavaScript could then interact with MyJavaObject methods:

<script>  function callAndroid() {    if (typeof AndroidBridge !== 'undefined') {      AndroidBridge.showMessage('Hello from JavaScript!');      const result = AndroidBridge.getData();      console.log('Data from Android:', result);    }  }</script>

Historically, this feature has been a source of vulnerabilities, particularly on older Android versions (API < 17), due to the ability to use Java Reflection via JavaScript to execute arbitrary code. Even on newer versions, poorly implemented interfaces can expose sensitive functionalities.

Identifying JavaScript Interfaces with Frida

Our first step in gaining control is to identify where and how these interfaces are added. We can hook the android.webkit.WebView.addJavascriptInterface method to log details about each interface being exposed.

Frida Script: Enumerating Interfaces

Java.perform(function() {  var WebView = Java.use('android.webkit.WebView');  WebView.addJavascriptInterface.implementation = function(object, name) {    console.log('[+] Detected JavaScript Interface:');    console.log('  Name: ' + name);    console.log('  Object Class: ' + object.getClass().getName());    console.log('  Methods:');    var methods = object.getClass().getMethods();    methods.forEach(function(method) {      console.log('    - ' + method.getName());    });    this.addJavascriptInterface(object, name); // Call the original method  };  console.log('[*] Hooked android.webkit.WebView.addJavascriptInterface');});

To run this script:

frida -U -l your_script.js -f com.example.app --no-pause

This script will print the name of each interface, its Java class, and all its public methods whenever addJavascriptInterface is called by the target application.

Intercepting and Manipulating Interfaces

Once we identify a target interface, we can delve deeper by hooking its specific methods. This allows us to inspect arguments, modify return values, or even prevent original method calls.

Frida Script: Hooking an Interface Method

Let’s assume we found an interface named AndroidBridge with a method showMessage(String message) and getData().

Java.perform(function() {  var bridgeClass = Java.use('com.example.app.MyJavaObject'); // Replace with the actual class name  if (bridgeClass) {    console.log('[*] Found MyJavaObject class.');    bridgeClass.showMessage.implementation = function(message) {      console.log('[+] Intercepted showMessage call:');      console.log('  Original Message: ' + message);      // Modify the message before it's displayed/processed      var modifiedMessage = 'Frida intercepted: ' + message;      this.showMessage(modifiedMessage); // Call original method with modified message      console.log('  Modified Message: ' + modifiedMessage);    };    bridgeClass.getData.implementation = function() {      console.log('[+] Intercepted getData call.');      var originalData = this.getData(); // Call original method      console.log('  Original Data: ' + originalData);      // Return a custom value      return 'Frida custom data: ' + originalData;    };    console.log('[*] Hooked showMessage and getData methods of MyJavaObject.');  }});

This script demonstrates how to alter both input parameters and return values, offering fine-grained control over the interface’s behavior.

Injecting Custom JavaScript

Beyond manipulating existing interfaces, we often want to inject our own JavaScript into the WebView context to execute arbitrary code or call the exposed Java interfaces directly from our injected script.

Method 1: Using evaluateJavascript

On Android API level 19 (KitKat) and above, evaluateJavascript() provides a clean way to execute JavaScript in the main frame. However, apps might not always use this method, or we might need to inject earlier.

Method 2: Hooking loadUrl or WebChromeClient

A more robust approach is to hook methods that load content into the WebView or handle page events. We can inject our script as soon as the page loads, or even before.

Frida Script: Injecting JavaScript on Page Load

Java.perform(function() {  var WebView = Java.use('android.webkit.WebView');  WebView.loadUrl.overload('java.lang.String').implementation = function(url) {    console.log('[+] Loading URL: ' + url);    this.loadUrl(url); // Call original loadUrl    // Inject JavaScript after page load    var customJs = "javascript: (function() {" +               "  console.log('Frida injected script running!');" +               "  if (typeof AndroidBridge !== 'undefined') { " +               "    AndroidBridge.showMessage('Message from Frida injected JS!'); " +               "    var secret = AndroidBridge.getData(); " +               "    console.log('Secret from Frida injected JS: ' + secret); " +               "  } " +               "  // Add new function to window object " +               "  window.fridaAlert = function(msg) { alert('Frida:' + msg); }; " +               "})();";    this.evaluateJavascript(customJs, null); // Inject JS  };  console.log('[*] Hooked WebView.loadUrl to inject JavaScript.');});

This script effectively injects a payload after every loadUrl call, allowing us to interact with the WebView’s JavaScript context and any exposed interfaces.

Advanced Techniques: Proxying and Dynamic Modification

For more complex scenarios, we might want to dynamically create new JavaScript interfaces or modify existing ones beyond simply hooking methods. Frida allows us to create Java objects on the fly and add them to the WebView.

Frida Script: Injecting a New JavaScript Interface

Java.perform(function() {  var WebView = Java.use('android.webkit.WebView');  var JavaScriptInterface = Java.registerClass({    name: 'com.frida.CustomJavaScriptInterface',    implements: [],    methods: {      fridaSayHello: {        returnType: 'void',        argumentTypes: ['java.lang.String'],        implementation: function(name) {          console.log('[+] Custom Frida Interface: Hello, ' + name + ' from Frida!');        }      },      fridaGetSecret: {        returnType: 'java.lang.String',        argumentTypes: [],        implementation: function() {          return 'This is a secret from Frida!';        }      }    }  });  // Hook loadUrl to inject our new interface  WebView.loadUrl.overload('java.lang.String').implementation = function(url) {    console.log('[+] Loading URL: ' + url);    this.loadUrl(url); // Call original loadUrl    // Create an instance of our custom interface    var customInterface = JavaScriptInterface.$new();    // Add it to the WebView    this.addJavascriptInterface(customInterface, 'FridaCustomAPI');    console.log('[*] Injected new JavaScript interface: FridaCustomAPI');    // Inject JS to test it    var testJs = "javascript:(function() {" +               "  if (typeof FridaCustomAPI !== 'undefined') {" +               "    FridaCustomAPI.fridaSayHello('World!');" +               "    var secret = FridaCustomAPI.fridaGetSecret();" +               "    console.log('FridaCustomAPI Secret: ' + secret);" +               "  }" +               "})();";    this.evaluateJavascript(testJs, null);  };});

This advanced script not only hooks loadUrl but also dynamically defines a new Java class, instantiates it, and registers it as a new JavaScript interface within the WebView, effectively expanding the application’s attack surface or remediation capabilities.

Conclusion

Frida provides an incredibly powerful and flexible toolkit for dynamic analysis of Android applications, particularly when dealing with WebView JavaScript interfaces. By understanding how these interfaces are implemented and leveraging Frida’s instrumentation capabilities, security researchers can identify vulnerabilities, manipulate application logic, and even inject custom code to achieve full control over the WebView context. This deep level of interaction is crucial for uncovering subtle flaws and thoroughly assessing the security posture of Android applications that rely heavily on WebView components. Always ensure responsible and ethical use of these techniques in authorized testing environments.

Android Mobile Specs & Compare Directory

Are you researching mobile hardware properties, processor SoCs, GPU chipsets, or RAM configurations? Access our complete specs catalog to compare up to 5 devices side-by-side!

Compare Devices Specs →
Google AdSense Inline Placement - Content Footer banner