Exploiting Deep Links in Android - Part 3

Exploiting Deep Links in Android - Part 3

So .. what else can we do with deep links?


Local File Inclusion (LFI)

In part 2 we saw how to achieve LFI via the WebView.loadUrl method. In this part we'll explore an alternative that doesn't require the application to contain a WebView.

Let's continue using the ABC Bank Android application as an example, and imagine that their customer support team uses deep links to help customers see important information displayed inside the app.

But in this case, instead of a parameter that specifies a web page, the deep link contains the path to a local file, e.g.:

abcbank://help?file=how-to-enable-mfa.txt

When the application receives this deep link, it opens the local file, reads its contents and displays in inside a TextView, as shown below:

String file = getIntent().getData().getQueryParameter('file');

AssetManager am = getAssets()
InputStream is = am.open(file);
BufferedReader r = new BufferedReader(new InputStreamReader(is));
StringBuilder str_to_display = "";

for (String line; (line = r.readline()) != null; ) {
    str_to_display.append(line).append('\n');
}

TextView tv = findViewById(R.id.myTextView);
tv.setText(str_to_display.toString());

So, what's the danger here? Surely if a user sees their own local data displayed on the screen that's not much of an issue.

Well, for starters it depends on whether or not the application enforces any validations when it comes to the files that can be specified via deep link. If there aren't any validations, then it all comes down to the data can be accessed from the application.

Note, for example, that if the application includes the READ_EXTERNAL_STORAGE permission, it can access files stored in shared directories, including those generated by other apps (note that since API 29 this is not possible since all storage is scoped).

So, it's not much of a leap to imagine that a malicious application could write a file to a shared directory that would then be read by the victim app.

Depending on the WebView configurations, this local file could then read all of the application files and exfiltrate them to an attacker-controlled server.

The attack surface can also be expanded if the application has access to Content Providers, which are interfaces that allow multiple apps to share data.

Cross-Site Request Forgery (CSRF)

Usually, when we talk about CSRF, we're exploiting some Cookie (mis)configuration in order to trick a user into performing an unintended action. A typical scenario would be a phishing link that performs a GET request that does some sort of account state change.

The same scenario is possible in Android if the application registers a deep link that "automatically" performs an action without any additional confirmation from the user.

A very straightforward example of this was reported in 2019 on the Periscope app: in short, it was possible to trick a user of the Periscope application into following another user of the app, simply by tricking the victim into clicking on a deep link similar to:

pscp://user/<any-user-id>/follow

The application, in essence, behaves as the browser by authenticating and sending the request without confirming with the user if they want to perform said action. And the impact is basically whatever feature is supported by the app itself.

Remote Code Execution (RCE)

Achieving RCE is theoretically possible, but very unlikely, since it would require very poor judgement on the part of the application's developers.

Let's go back to the ABC Bank Android app. Their customer support team knows that some OS versions have specific bugs, but their clients are not tech-savvy enough to check the OS by themselves, so they send them a deep link such as this one:

abcbank://help?cmd=cat%20/system/build.prop

When the application receives this deep link, it executes the cat /system/build.prop command using Runtime.getRuntime().exec(), and then the output is displayed on a TextView, just as before:

String command = getIntent().getData().getQueryParameter('cmd');
Process process = Runtime.getRuntime().exec(command)

InputStreamReader isr = InputStreamReader(process.inputStream);
BufferedReader r = new BufferedReader(isr);
StringBuilder str_to_display = "";

for (String line; (line = r.readline()) != null; ) {
    str_to_display.append(line).append('\n');
}

TextView tv = findViewById(R.id.myTextView);
tv.setText(str_to_display.toString());

The client then simply reads the output to the customer support agent, which can then take that info into account when supporting the user.

In this scenario and without any additional validations, it would be possible for an attacker to remotely execute commands on the device by simply tricking a victim into clicking a deep link such as:

deeplink://path?cmd=<the-evil-command>

But, again, it would be a sign of very poor judgement to allow a user to specify commands directly via deep link, particularly without any additional validations, and proof of that is the fact that I couldn't find any actual examples of RCE reported in the wild.


And that's it for now! On the next post I'll look into Mitigation.