Qu’est-ce qu’un deep link ?

Deep Links attaques bonnes pratiques securite

Les deep links sont des URI (Uniform Resource Identifier) prédéfinies qui permettent d’accéder directement à une activité dans une application web ou mobile lorsque l’on clique dessus.

Ces liens se trouvent généralement sur des pages au sein d’une application web ou dans les webviews d’une application mobile. Lorsque l’utilisateur clique sur un deep link, et qu’il possède l’application permettant d’ouvrir ce type de lien, une popup lui propose d’ouvrir le lien avec l’application correspondante.

Exemple d’utilisation :

L’utilisateur accède à un lien sur une page web depuis le navigateur de son téléphone. Prenons par exemple le lien suivant : https://deeplink.example/show

Si une application sur le téléphone de l’utilisateur est configurée pour ouvrir les liens avec le domaine « deeplink.example » et le protocole « https », alors, lorsque l’utilisateur va cliquer sur ce lien, il va voir l’écran suivant lui indiquant qu’il peut ouvrir le lien avec plusieurs applications :

Exemple deep link

Ici, trois applications sont capables d’ouvrir les liens du type : https://deeplinkexample/show

À noter que si une seule application est capable d’ouvrir un lien spécifique, l’écran de choix précédent n’apparait pas et l’application capable d’ouvrir le lien se lance directement.

Si l’utilisateur sélectionne l’application « my deeplink app », il sera directement redirigé vers une activité de l’application. Dans notre cas, c’est l’activité « DeepLinkExample1 » qui a été configurée pour ouvrir ce type de lien. Elle indique seulement que le lien en question a bien été ouvert :

Nous allons voir qu’une mauvaise utilisation des deep links peut conduire à des vulnérabilités parfois importantes. C’est pourquoi nous recommandons de ne pas les utiliser dans vos applications. Mais avant cela, regardons les deep links d’un peu plus près.

Comment fonctionnent les deep links ?

Pour qu’une application puisse ouvrir un deep link, il est nécessaire de créer un « Intent filter » associé à une activité. Pour rappel, une intention ou intent est un élément qui va permettre à une activité d’effectuer une action suite à la réception de données.

Dans le cas des deep links, il est nécessaire d’indiquer à une activité que lorsqu’un certain type de lien est ouvert sur le téléphone, l’activité concernée doit pouvoir répondre et potentiellement effectuer une action avec ce lien. Cela va permettre au téléphone de proposer une application pour l’ouverture d’un certain type d’URI comme nous l’avons vu dans la section précédente.

La définition d’un intent filter se fait dans le fichier « AndroidManifest.xml » de l’application. Il est important de noter qu’une activité qui peut ouvrir un deep link doit forcément être exportée. L’attribut  android:exported="true"  doit donc systématiquement être ajouté à l’activité.

Ci-dessous un exemple de définition d’un deep link :

<activity
    android:name=".DeepLinkExample1"
    android:exported="true">
    <intent-filter android:label="my deeplink app">
        <action android:name="android.intent.action.VIEW" />

        <category android:name="android.intent.category.DEFAULT" />
        <category android:name="android.intent.category.BROWSABLE" />

        <data android:scheme="https" />
        <data android:host="deeplink.example" />
    </intent-filter>
</activity>

Dans cet exemple, il est indiqué que l’activité «DeepLinkExample1 » possède un intent filter lui permettant d’ouvrir des URI avec le domaine « deeplink.exemple » et le schéma « https ». Par exemple notre activité peut être utilisée pour ouvrir les liens suivants :

https://deeplink.example
https://deeplink.example/hello_world

C’est l’élément data de l’intent filter qui permet de configurer le type de liens ouvrable par l’activité.

Dans cet exemple, nous avons une URL classique, mais l’intérêt des deep links c’est de pouvoir ouvrir n’importe quel type d’URI. Voici un autre exemple :

<activity
    android:name=".DeepLinkActivity"
    android:exported="true"
    android:label="DeepLink">
    <intent-filter android:label="filter_view_example_vaadata">
        <action android:name="android.intent.action.VIEW" />

        <category android:name="android.intent.category.DEFAULT" />
        <category android:name="android.intent.category.BROWSABLE" />

        <data android:scheme="vaadata" />
        <data android:host="test" />
        <data android:path="/hello"/>
    </intent-filter>
</activity>

Ici, l’activité « DeepLinkActivity » sera en mesure d’ouvrir les liens de type vaadata://test/hello, vaadata://test/hello?test=1 ou vaadata://test/hello?a=1&test=coucou.

Vous l’aurez compris, il est possible de définir n’importe quel type d’URI, que seule votre application sera capable de comprendre et d’ouvrir.

Pour détecter les deep links lors d’un pentest d’application mobile, il convient donc de décompiler l’APK et lire le fichier AndroidManifest.xml afin de détecter la présence de deep links.

Quelles sont les vulnérabilités courantes des deep links et les attaques les exploitant ?

Comme tout mécanisme permettant d’interagir directement avec l’application mobile, les deep links peuvent constituer un point d’entrée potentiel pour conduire des attaques contre les utilisateurs.

Deux scénarios d’attaques sont possibles en fonction de la manière dont sont utilisés les deep links :

  • Les deep links mis en place par les développeurs de l’application sont utilisés pour transférer des données sensibles. L’attaque consiste donc à essayer de voler ces données.
  • Les paramètres transmis dans les deep links sont utilisés de façon non sécurisée dans le code de l’application. L’idée consiste ici à comprendre comment sont utilisés les deep links afin d’exploiter des vulnérabilités plus classiques comme des XSS, des IDOR, contournement de protection contre les CSRF, etc.

Transfert de données sensibles via deep links

Dans ce cas de figure, le risque réside dans le fait que des informations sensibles sont transmises à une application mobile via un deep link. Il faut garder à l’esprit que n’importe qui peut créer une application malveillante capable d’ouvrir les mêmes deep links que l’application légitime. Si l’attaquant arrive à faire installer (via du social engineering par exemple) à sa ou ses victimes son application malveillante et que les utilisateurs utilisent l’application malveillante pour ouvrir les deep links contenant les données sensibles, alors l’attaquant sera en mesure d’exfiltrer les données.

Bien que peu courant, l’impact potentiel de ce scénario d’attaque peut être très fort et conduire au vol de comptes utilisateurs.

Imaginons par exemple une organisation avec une application web et des utilisateurs. Une fonctionnalité d’oubli de mot de passe est présente. Elle permet l’envoi d’un email à l’utilisateur contenant un lien pour réinitialiser le mot de passe. Ce lien contient un jeton unique dans l’URL (comme myapp://reset-password/?code=THECODE par exemple) pour identifier l’utilisateur au moment du changement de mot de passe. Dans l’application mobile de cette organisation, une activité est capable d’ouvrir le lien reçu dans le mail. Cette activité fait appel à une webview pour que l’utilisateur puisse changer son mot de passe depuis son téléphone.

Le scénario d’attaque est très simple : l’attaquant crée une application qui ouvre le même type de lien que ceux que l’on retrouve dans le mail d’oubli de mot passe. Via du social engineering, il arrive à piéger un ou plusieurs utilisateurs. Ces derniers utilisent l’application malveillante pour ouvrir le lien d’oubli de mot de passe. L’application malveillante exfiltre l’URL et l’attaquant peut changer le mot de passe des utilisateurs ciblés.

La probabilité d’exploitation reste assez faible, car du social engineering est requis. En revanche, ce type de faille a déjà été détecté sur des applications avec des milliers voire des millions d’utilisateurs. La probabilité qu’un attaquant arrive à piéger l’un d’entre eux est alors beaucoup plus forte.

Utilisation dangereuse des paramètres transmis via les deep links

Dans ce scénario, le problème ne vient pas du type de données transmis dans les deep links, mais de la manière dont sont utilisés les paramètres transmis dans l’application. En effet, si le deep link est utilisé pour transmettre des valeurs à l’application et que l’application utilise ces valeurs directement sans les vérifier ni les nettoyer, cela peut conduire à de nombreuses failles. L’impact dépend de la manière dont est utilisé le paramètre dans le code de l’application. Il convient donc d’analyser l’utilisation des deep links au niveau du code de l’application. Voici quelques exemples :

  • Si la valeur d’un paramètre passé dans le deep link est utilisée telle quelle dans une webview ou le JavaScript est autorisé (« myWebView.settings.javaScriptEnabled = true ») cela peut conduire à une XSS.
  • Si la valeur d’un paramètre est utilisée pour effectuer une action sensible, les conséquences peuvent varier en fonction de l’action (par exemple charger une page distante dans une webview en fonction d’un paramètre du deep link. Cela peut conduire à la création d’un lien malveillant qui chargera du contenu contrôlé par l’attaquant)
  • Si la valeur d’un paramètre est passée à la fonction «Runtime.getRuntime().exec() », cela peut conduire à une RCE.
  • Dans certains cas, les deep links peuvent servir à contourner des protections comme les CSP ou des protections contre les CSRF présents sur une application web, mais non présent sur le mobile.

Voyons ensemble l’exemple d’une XSS via des deep links. Imaginons une application Android avec une activité définie de la façon suivante dans le fichier AdroidManifest.xml :

<activity
    android:name=".DeepLinkXSS"
    android:exported="true">
    <intent-filter android:label="filter_view_example_xss">
        <action android:name="android.intent.action.VIEW" />

        <category android:name="android.intent.category.DEFAULT" />
        <category android:name="android.intent.category.BROWSABLE" />

        <data android:scheme="xss" />
        <data android:host="test" />
    </intent-filter>
</activity>

Cette activité est donc conçue pour ouvrir les liens de type « xss://test/ »

Dans le code Java de l’activité nous avons le code suivant :

public class DeepLinkXSS extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_deep_link_xss);
        WebView xssdeeplink = findViewById(R.id.xss_webview);
        xssdeeplink.getSettings().setJavaScriptEnabled(true);
        xssdeeplink.setWebChromeClient(new WebChromeClient());

        if (getIntent() == null){
            finish();
        }

xssdeeplink.loadData(getIntent().getData().getQueryParameter("name"),"text/html", "UTF-8");
    }
}

Nous pouvons voir qu’une WebView est déclarée dans le code et que le JavaScript est actif au sein de cette WebView. Lorsqu’un deep link est ouvert, nous cherchons la valeur du paramètre name dans le deep link et nous l’utilisons dans la WebView. La WebView se contente simplement d’afficher la valeur du paramètre name.

Un lien normal ressemble au suivant :

xss://test/?name=John

Si l’utilisateur ouvre ce lien avec l’application, il obtiendra le résultat suivant :

Maintenant, un attaquant peut forger le lien suivant et le transmettre à ses victimes via du social engineering (le payload affiche simplement une alerte)  :

xss://test/?name=John%3cscript%3ealert%28123%29%3c%2fscript%3e

Si la victime clique, le code injecté sera exécuté :

L’exemple est peu réaliste, mais permet d’illustrer le principe de l’attaque. Dans tous les cas, l’attaquant doit réussir à faire ouvrir un deep link malveillant à sa victime.

Comment se protéger des attaques exploitant les deep links ?

Pour éviter toute exploitation liée au premier scénario d’attaque, il est conseillé de ne pas utiliser les deep links mais plutôt un autre mécanisme existant : les app links.

Le fonctionnement des app links assez similaires aux deep links. En fait, les app links sont un type spécifique de deep links. Ils sont définis de la même manière dans le fichier AndroidManifest.xml à l’aide d’un intent filter à quelques différences près :

  • Un champ android:autoVerify= »true » doit être ajouté à l’intent filter
  • Il est obligatoire d’utiliser les protocoles HTTP et HTTPS. Il n’est donc plus possible d’utiliser des schémas personnalisés comme vaadata:// ou xss://.
  • Un fichier assetlinks.json doit être ajouté sur le serveur web correspondant au domaine du app link. Le domaine doit donc exister et vous appartenir.

Voilà une configuration valide avec les app links :


<activity
    android:name=".DeepLinkExample1"
    android:exported="true">
    <intent-filter android:label="my deeplink app"
        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="https" />
        <data android:host="vaadata.com" />
    </intent-filter>
</activity>

Ici, nous indiquons que notre activité peut ouvrir les URI de type :

https://vaadata.com/<anyhing>

Le android:autoverify="true" permet d’indiquer que votre application est la seule sur le téléphone à pouvoir ouvrir ce type de lien si elle est installée. L’application s’ouvre donc directement. Cela permet d’éviter la fenêtre de dialogue permettant d’ouvrir une autre application.

Il convient aussi d’ajouter un fichier /.well-know/assetlinks.json accessible sans authentification en HTTPS à l’URL :

https://vaadata.com/.well-known/assetlinks.json

Ce fichier est un fichier « Digital Asset Links JSON ». Il doit notamment contenir les informations suivantes :

  • Namespace : le nom donné à l’application
  • Package_name : l’identifiant de l’application mobile comme définie dans le fichier « build.gradle »
  • Sha256_cert_fingerprints : l’empreinte SHA256 du certificat ayant permis de signer votre application

En voici un exemple pour une application :

[{
  "relation": ["delegate_permission/common.handle_all_urls"],
  "target": {
    "namespace": "DeepLink_app",
    "package_name": "com.vaadata.deeplink.app",
    "sha256_cert_fingerprints":
    ["14:6D:E9:83:C5:73:06:50:D8:EE:B9:95:2F:34:FC:64:16:A0:83:42:E6:1D:BE:A8:8A:04:96:B2:3F:CF:44:E5"]
  }
}]

Les app links fonctionnent de la façon suivante : à chaque fois que vous allez ouvrir un lien sur votre téléphone correspondant à un app link d’une de vos applications, Android va contacter votre serveur web et récupérer le fichier assetlinks.json. Il va vérifier que le nom du package et le hash du certificat de signature correspondent à celui de l’application sur votre téléphone. Si c’est le cas, le lien sera ouvert. Tant que le serveur web n’est pas compromis, une seule application sur votre téléphone sera en mesure d’ouvrir un app link spécifique. Pour tous les détails sur l’implémentation, veuillez vous référer à la documentation officielle des app links.

Cette recommandation fonctionne pour la partie fuite de données et pour éviter que plusieurs applications puissent ouvrir des deep links similaires. Dans les autres cas, la recommandation est à appliquer localement dans le code en fonction de l’utilisation des données transférées dans les app links.

De façon générale, les deep links ne doivent jamais être utilisés pour faire transiter des informations sensibles. Il faut aussi garder à l’esprit que les données contrôlées par les utilisateurs (et donc les URI) ne doivent pas être utilisées pour des actions sensibles sans être au préalable vérifiées et nettoyées. 

Auteur : Yoan MONTOYA – Pentester @Vaadata