Objectif
Exfiltrez à distance le jeton d’authentification utilisé dans l’application pour prendre le contrôle d’un compte en un seul clic.
Outil
- Jadx – https://github.com/skylot/jadx
- ADB – https://developer.android.com/tools/adb?hl=fr
- Ngrok – https://ngrok.com/
- Burp Collaborator – https://portswigger.net/burp
Installation
Pour commencer, nous allons installer l’application sur un périphérique virtuel (bien que cela fonctionne également avec les périphériques physiques), le tout grâce à ADB :
$ adb install mhl-lab-tokenbleed.apk
Performing Streamed Install
Success
L’application est installée avec succès et se lance parfaitement :

Commençons l’analyse !
Fichier AndroidManifest.xml
Voici le fichier AndroidManifest.xml de l’application TokenBleed :

Il est possible d’identifier une activity exportée, c’est-à-dire accessible par d’autres applications du système. Il s’agit de l’activity com.mobilehackinglab.exchange.SplashActivity qui sera lancée par défaut quand l’utilisateur clique sur l’icône de l’application.
Il est possible de déclencher cette activity via des URI de type mhlcrypto:// aussi appelées deep links.
Pour rappel, les deep links (Android >= 6.0) sont des URI conçues pour s’ouvrir directement dans une application mobile, guidant les utilisateurs vers un contenu ou une action spécifique sans passer par l’écran d’accueil de l’application.
Audit du code source
Pas grand chose de plus dans le fichier AndroidManifest.xml, allons voir au sein du code source de SplashActivity :

Si un utilisateur est déjà connecté (ligne 21), alors on prépare un Intent vers MainActivity (ligne 22) en conservant les données (ligne 23) et l’action (ligne 24) qui ont servi à lancer SplashActivity, afin de ne pas perdre le contexte initial. Sinon, il y a une redirection vers LoginActivity.
Au sein de MainActivity, la fonction handleIntent (ligne 84) est un cas classique de validation insuffisante des deep links :

En effet, le deep link réalise les actions suivantes :
- Récupère l’intent courant et vérifie si l’action est android.intent.action.VIEW
- Vérifie que le schéma de données de l’intent est mhlcrypto://
- Vérifie que l’hôte des données de l’intent est showPage
- Récupère le paramètre url et le transmet directement à DwebViewActivity
En effet, aucune vérification n’est faite pouvant permettre d’ouvrir n’importe quelle URL grâce à la commande : adb shell am start -a android.intent.action.VIEW -d « mhlcrypto://showPage?url=URL »

Voici le code source de DwebViewActivity :

Au sein de ce dernier, lignes 38 à 44, de nombreuses fonctions potentiellement dangereuse sont désactivées.
L’ajout de l’objet JsApi via la fonction addJavascriptObject (ligne 56) expose une interface JavaScript qui permet à toute page chargée d’interagir avec le code natif Android. Cette « fonctionnalité » couplée au fait qu’il n’y a pas de contrôle strict de l’URL (hormis l’utilisation du https en ligne 57) peut permettre l’injection de JavaScript malveillant invoquant des méthodes sensibles de l’application.
Note :
addJavascriptInterface (méthode native Android) et addJavascriptObject (variante de bibliothèques tierces comme dwebview) reposent sur le même principe : exposer un objet Java au moteur JavaScript d’un WebView. La différence est purement d’implémentation, mais le risque de sécurité reste identique.
On parle de addJavascriptInterface dans le write-up Guess Me.
Compréhension des WebViews
Au sein de l’application, la page Summer Staking Bonus attire notre attention ! En effet, celle-ci correspond à une page externe (https://mhl-cex-auth-worker.arnotstacc.workers.dev/promo/0) chargée via une WebView.

Néanmoins, chose intéressante, des données de personnalisation sont ajoutés lorsque la page est consultée depuis l’application mobile alors que cette personnalisation n’est pas présente lors d’un accès standard :

Il en est de même pour l’URL https://mhl-cex-auth-worker.arnotstacc.workers.dev/promo/1 :

Voici le code source de la page en question :

Au sein de la fonction authenticate(), le mécanisme de sécurité repose entièrement sur la confiance accordée à dsBridge.call(« getUserAuth », …).
Comme expliqué précédemment, un attaquant pouvant contrôler l’URL chargée dans le WebView, il est alors possible d’injecter du JavaScript malveillant permettant d’invoquer cette méthode et ainsi dérober le token JWT.
Assemblons le tout et exploitons
Nous avons donc un deep link ne mettant pas suffisament de contrôle en place permettant ainsi de charger des URL abritraires, addJavascriptObject exposant une interface JavaScript qui permet à toute page chargée d’interagir avec le code natif Android et la fonction getUserAuth accessible en JavaScript permettant de voler des tokens JWT.
En modifiant légèrement le code source de la page, il est alors possible d’exfiltrer le token vers un site sous notre contrôle :

Il ne reste plus qu’à monter un serveur local (python ici), le rendre accessible sur Internet (ngrok ici) et de faire appel à la Webview :

Il est alors possible de voir l’exfiltration du token JWT :

Il ne reste plus qu’à décoder la valeur 🙂

