Exploiting Deep Links in Android - Part 5 (Testing)

Enumeration

Enumeration should always start with the App Manifest, which can be extracted from any .apk with a tool such as apktool, e.g.:

~ apktool d com.twitter.android_2021-10-22.apk 
I: Using Apktool 2.5.0 on com.twitter.android_2021-10-22.apk
I: Loading resource table...
I: Decoding AndroidManifest.xml with resources...
I: Loading resource table from file: /Users/inesmartins/Library/apktool/framework/1.apk
I: Regular manifest package...
I: Decoding file-resources...
I: Decoding values */* XMLs...
I: Baksmaling classes.dex...
I: Baksmaling classes2.dex...
I: Baksmaling classes3.dex...
I: Baksmaling classes4.dex...
I: Baksmaling classes5.dex...
I: Baksmaling classes6.dex...
I: Baksmaling classes7.dex...
I: Baksmaling classes8.dex...
I: Baksmaling classes9.dex...
I: Copying assets and libs...
I: Copying unknown files...
I: Copying original files...
I: Copying META-INF/services directory

~ open com.twitter.android_2021-10-22/AndroidManifest.xml

When you open the manifest, you should start by looking for <intent-filter> elements:

Note the scheme, host and path attributes inside <data> elements to understand what deep links are supported, and check the category and action elements, as they give you clues about how the deep links are used.

The example below shows a Scheme URL with scheme="myapp" and host="path", which means the following URI could be handled by the app: myapp://path.

<activity android:name=".MyUriActivity">
  <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="path" />
  </intent-filter>
</activity>

When analysing app links, make sure the intent filter includes the  android:autoVerify="true" attribute, which enables app link verification:

<activity android:name=".MyUriActivity">
  <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" />
      <data android:scheme="http" android:host="www.myapp.com" android:path="/my/app/path" />
      <data android:scheme="https" android:host="www.myapp.com" android:path="/my/app/path" />
  </intent-filter>
</activity>

The simplest way to check if a link is correctly verified is to just enter it onto a mobile browser and check that the link is automatically (without any user interaction) opened with the intended app.

If you're looking for additional information, you can also use this simple Python tool that I've developed. This library supports a bunch of different test modes and options, but most likely the verify-applinks option will give you what you need:

~ python3 Android-App-Link-Verification-Tester/deeplink_analyser.py \
-apk com.twitter.android_2021-10-22.apk \
-p com.twitter.android \
-op verify-applinks

[...]

The APK's signing certificate's SHA-256 fingerprint is: 
0F:D9:A0:CF:B0:7B:65:95:09:97:B4:EA:EB:DC:53:93:13:92:39:1A:A4:06:53:8A:3B:04:07:3B:C2:CE:2F:E9

[...]

Checking http://mobile.twitter.com/.*

✓ includes autoverify=true
✓ includes VIEW action
✓ includes BROWSABLE category
✓ includes DEFAULT category
✓ DAL verified

  Relations: 
    - [Standard] delegate_permission/common.get_login_creds
    - [Standard] delegate_permission/common.handle_all_urls
    - [Custom]   delegate_permission/common.use_as_origin

Checking http://twitter.com/.*

✓ includes autoverify=true
✓ includes VIEW action
✓ includes BROWSABLE category
✓ includes DEFAULT category
✓ DAL verified

  Relations: 
    - [Standard] delegate_permission/common.get_login_creds
    - [Standard] delegate_permission/common.handle_all_urls
    - [Custom]   delegate_permission/common.use_as_origin

[...]

Read more about relation strings here: https://developers.google.com/digital-asset-links/v1/relation-strings

Static and Dynamic Analysis

In order to understand what data is being taken from deep links and how that data is being processed, check for occurrences of:

  • getIntent()
  • .getData()
  • .getExtras()
  • Regex search: \.get\w+Extra\(  -> finds occurrences .getStringExtra(,  .getParcelableExtra(, etc.)

To understand how the app’s WebViews are controlling which URLs are loaded and how, look for:

  • shouldOverrideUrlLoading
  • shouldInterceptRequest

Finally, to see how the data or URIs are being loaded into the app's WebViews, check for any calls to:

  • .loadUrl(
  • .loadData(

Note that, not only are these methods relevant when doing static analysis, you can also "hook" them with Frida when doing dynamic analysis.


Automated Tests and Fuzzing

If you already know which deep link you you want to test, adb is the way to go:

~ adb shell am start -W -a android.intent.action.VIEW -d <url>

where:
-a ~> action
-W ~> wait for launch to complete
-d ~> URL

If you want to build and send a specific Intent URL, you can create a new Android app using Android Studio, as shown below:

Finally, if you have no idea what you're looking for, maybe give fuzzing a try with drozer's fuzzinozzer module:

dz> run intents.fuzzinozer --complete_test --package com.twitter.android --save_state

And this is it, the end of the road for this series.
Hope you learned something!