Neutron, notre moteur d’IA, a obtenu un score de 96,75 % sur le benchmark CyberGym de l’UC Berkeley. En savoir plus

Sécurité

Sécurité

Risques d'addJavascriptInterface dans les WebView Android

Étude de cas : le moteur de pentest IA d'Ostorlab découvre un pont JavaScript de WebView Android accessible via des deep links et l'enchaîne en manipulation de l'interface native.

Introduction

Les applications mobiles hybrides s'appuient souvent sur un « pont » (bridge) pour permettre au contenu web de communiquer avec la couche native Android. Si cela permet des fonctionnalités puissantes comme les notifications natives ou l'accès au matériel, cela introduit une surface d'attaque importante. Lorsqu'un objet Java ou Kotlin natif est exposé à un contexte JavaScript non fiable, il peut conduire à une manipulation de l'interface, à de l'ingénierie sociale, voire à une exécution de code à distance.

La méthode WebView.addJavascriptInterface(Object object, String name) injecte un objet fourni dans le contexte JavaScript de la WebView, ce qui permet à JavaScript d'appeler des méthodes natives explicitement annotées par @JavascriptInterface. Historiquement, cette API était à l'origine d'une vulnérabilité critique sur les versions d'Android antérieures à la 4.2, où une réflexion sans restriction permettait à JavaScript d'obtenir une exécution de code à distance universelle dans le processus de l'application. Les versions modernes d'Android atténuent cette classe de problèmes en exigeant des annotations explicites et en restreignant l'accès par réflexion. Cependant, si les méthodes exposées effectuent des opérations sensibles ou si du JavaScript non fiable peut s'exécuter dans la WebView, l'interface peut encore être détournée pour franchir les frontières de confiance et manipuler le comportement natif de l'application.

Comprendre l'exposition d'un pont JavaScript

Pour comprendre pourquoi cette vulnérabilité est significative, il faut examiner deux composants Android courants qui, une fois combinés, créent une chaîne d'exploitation puissante : l'interface JavaScript et la gestion des Intents de deep link.

1. L'interface JavaScript

Sous Android, le composant WebView permet aux applications d'afficher du contenu web. Pour que ce contenu web puisse « dialoguer » avec le code Android natif, les développeurs utilisent un « pont » créé avec la méthode addJavascriptInterface.

Lorsqu'un développeur appelle webView.addJavascriptInterface(new MyWebAppInterface(), "Android"), tout JavaScript exécuté dans cette WebView peut appeler les méthodes de la classe Java MyWebAppInterface comme s'il s'agissait de fonctions JS natives (par exemple window.Android.showToast(...)).

Le risque : si la WebView charge un site web non fiable (via une attaque de l'homme du milieu ou un lien malveillant), ce site obtient la possibilité de déclencher du code Java natif.

Les deep links permettent à des sources externes (comme un navigateur ou une autre application) d'ouvrir des écrans précis d'une application à l'aide d'un URI (par exemple myapp://profile).

Lorsque la « porte » d'une application est mal gardée, elle peut accepter un URI javascript: comme deep link. Par exemple : intent://target_activity?url=javascript:alert(window.Android.showToast('Hacked'))

Si l'application charge aveuglément cette URL dans sa WebView, elle ne charge pas simplement une page web : elle exécute du code arbitraire directement dans le contexte sécurisé de l'application.

Lorsque ces deux éléments se combinent, un attaquant peut « atteindre » l'application à distance. En envoyant un lien piégé à un utilisateur, l'attaquant force l'application à s'ouvrir, injecte du JavaScript malveillant dans la WebView interne, puis utilise le pont pour manipuler les fonctionnalités natives.

Présentation du moteur de pentest d'Ostorlab

Le Ostorlab Pentest Engine est un agent de sécurité offensive autonome conçu pour reproduire le raisonnement et la méthodologie d'un pentesteur humain expert grâce à l'intelligence artificielle. Contrairement aux scanners traditionnels qui reposent sur des vérifications statiques fondées sur des règles, le moteur utilise un cadre dynamique « Reason and Act » pour explorer la surface d'attaque d'une application.

Comment il fonctionne : la boucle autonome

Le moteur fonctionne selon un cycle continu et itératif qui lui permet de traiter des vulnérabilités complexes comme l'exposition du pont JavaScript décrite dans cette étude de cas :

  • Modélisation des risques et génération d'hypothèses : à l'aide de méthodologies comme P.A.S.T.A., le moteur construit d'abord un modèle de menace de la cible. Il génère des hypothèses précises (par exemple « Ce deep link peut-il exécuter du JavaScript pour atteindre un pont natif ? ») plutôt que d'envoyer des payloads à tout va.
  • Sous-agents spécialisés (exécuteurs) : le moteur orchestre une flotte de sous-agents spécialisés via la plateforme Ostorlab OXO. Ces exécuteurs utilisent des outils tels que monkey tester, crawlers, fuzzers et moteurs de taint analysis.
  • Instrumentation à l'exécution et interaction dynamique : le moteur opère dans un environnement d'exécution réel et contrôlé, en superposant l'instrumentation sur plusieurs piles technologiques (Java, Swift, C/C++, Flutter). Cela lui permet de « hooker » les comportements natifs, de naviguer dans des parcours d'interface complexes, y compris dans des zones authentifiées avec prise en charge de la 2FA/OTP, et d'inspecter les données au fil de leur circulation entre la couche web et la couche native en temps réel.
  • Validation adverse : pour améliorer la précision et réduire les faux positifs, le moteur inclut une passe de validation adverse dédiée. Il réexamine indépendamment les vulnérabilités trouvées pour confirmer qu'elles sont reproductibles et qu'elles présentent un risque réel.

En conservant une mémoire organisée des artefacts et des observations, le moteur de pentest peut « enchaîner » les résultats. Dans ce cas, il n'a pas seulement trouvé un deep link : il a déduit que le deep link pouvait servir de mécanisme de livraison pour un payload JavaScript, lequel pouvait ensuite interagir avec une interface native énumérée.

Le scénario d'attaque : découvrir l'exposition du pont JavaScript

Lors d'un récent test de benchmark, le Pentest Engine d'Ostorlab a identifié une faille dans l'implémentation WebView d'une application hybride. En sondant systématiquement les gestionnaires de deep links de l'application, il a découvert une interface JavaScript exposée qui permettait à du JavaScript exécuté dans la WebView de déclencher des éléments d'interface natifs sans consentement explicite de l'utilisateur.

Étape 1 : découverte et énumération du pont

Le premier objectif du moteur était de cartographier la surface d'attaque disponible en identifiant les objets natifs injectés dans le contexte JavaScript de la WebView.

Découverte du pont : identifier les objets et méthodes de pont exposés depuis le contexte JavaScript. Objectif : énumérer les méthodes accessibles pour déterminer la surface d'attaque native.

Résumé de l'exécution : Le moteur a utilisé Object.getOwnPropertyNames() pour lister les propriétés de l'objet global window et a identifié une interface personnalisée.

Résultat : Un objet de pont nommé Android a été découvert. Une énumération plus poussée a identifié une seule méthode accessible : showToast(String message).

Conclusion : L'application expose un pont natif. Bien que la liste des méthodes soit minimale, l'absence de restrictions d'origine signifie que n'importe quelle page chargée dans la WebView, y compris celles injectées via des deep links, peut invoquer la logique d'interface native.

Étape 2 : validation du vecteur d'injection

Sachant qu'un pont existait, le moteur a cherché un mécanisme pour interagir avec lui depuis une source externe non fiable. Il a découvert que le gestionnaire de deep links de l'application était configuré pour traiter les Intents entrants sans assainir le schéma de l'URI.

Vérifier si le pont Android est accessible par injection de deep link à l'aide d'un URI javascript:. Objectif : confirmer si la WebView exécute du code arbitraire provenant d'Intents externes.

Résumé de l'exécution : Le moteur a envoyé un Intent malveillant à l'Activity vulnérable (Activity2) à l'aide d'un payload javascript: conçu pour appeler le pont découvert : adb shell am start -W -a android.intent.action.VIEW -n com.target.app/.Activity2 -d "javascript:Android.showToast('BRIDGE_ACCESS_TEST')".

Résultat : La commande a été traitée avec succès par l'application. Un message toast natif est apparu à l'écran de l'appareil avec le texte « BRIDGE_ACCESS_TEST », et des entrées Logcat ont confirmé l'invocation de la méthode depuis le package com.target.app.

Conclusion : Le vecteur d'injection est entièrement validé. Comme l'application ne rejette pas les URI javascript: ou data: transmis via des Intents, n'importe quelle application externe peut forcer la WebView à exécuter du code qui interagit directement avec l'interface Java native.

Étape 3 : recherche d'une escalade (réflexion et injection)

Le pont étant confirmé comme accessible, le moteur a tenté d'aller au-delà de la manipulation de l'interface pour identifier des impacts plus graves, comme l'exécution de code natif ou l'injection de commandes. L'objectif était de déterminer si la méthode showToast ou le pont lui-même pouvait servir de primitive pour contourner la sandbox Android.

Tenter de contourner la sandbox en utilisant la réflexion ou l'injection de commandes via la méthode showToast afin d'obtenir une exécution de code natif.

Résumé de l'exécution : Le moteur a testé systématiquement plusieurs schémas d'exploitation avancés :

  1. Test de réflexion : a tenté d'accéder à la méthode getClass() et d'utiliser forName('java.lang.Runtime') pour exécuter des commandes système.
  2. Injection de commandes : a essayé d'injecter des métacaractères shell et des séparateurs de commandes (par exemple ;, &&, `) dans le paramètre de showToast.
  3. Prototype pollution : a tenté d'injecter des méthodes malveillantes dans le prototype de l'objet de pont.

Résultat : Toutes les tentatives d'escalade ont été bloquées :

  • Réflexion : l'accès à getClass() était restreint, et les tentatives d'enchaîner des méthodes pour atteindre Runtime.exec() ont échoué.
  • Injection de commandes : l'implémentation native de showToast a traité toutes les entrées comme une CharSequence littérale, affichant les chaînes malveillantes en texte brut à l'écran au lieu de les exécuter.
  • Immutabilité : l'objet de pont s'est révélé immuable, ce qui empêche la prototype pollution ou le remplacement de méthodes.

Conclusion : Les contrôles de sécurité au niveau des méthodes sont efficaces. Bien que le pont soit exposé, il est « implémenté de façon sûre », ce qui limite l'impact à des effets d'interface transitoires et empêche l'escalade vers l'exécution de code natif ou l'exfiltration de données.

Étape 4 : confirmation de l'impact concret (la chaîne d'ingénierie sociale)

Alors que le moteur avait confirmé que l'escalade native directe était bloquée, il a pivoté pour valider le risque métier concret : l'ingénierie sociale. En exploitant la capacité du pont à afficher des éléments d'interface native, le moteur a démontré comment un attaquant pourrait manipuler la confiance de l'utilisateur pour faciliter le vol d'identifiants ou la distribution de logiciels malveillants.

Exécuter une chaîne d'attaque en plusieurs phases combinant un contenu de phishing non authentifié avec une notification native « officielle » déclenchée via le pont, afin d'évaluer l'impact de l'ingénierie sociale.

Résumé de l'exécution : Le moteur a orchestré une « attaque chaînée » en deux phases pour simuler un scénario de phishing réaliste :

  1. Redirection de phishing : a utilisé un deep link pour forcer la WebView à charger une URL externe malveillante : adb shell am start -n com.target.app/.Activity2 -d "https://attacker.com/phishing.html"

  2. Injection d'une fausse notification : a enchaîné immédiatement après la redirection en déclenchant un message toast natif urgent via le pont : adb shell am start -n com.target.app/.Activity2 -d "javascript:Android.showToast('🔒 New message: Account verification required')"

Résultat : L'expérience utilisateur a été détournée avec succès. L'application a lancé une page de phishing tout en affichant simultanément une notification native d'apparence officielle. Cela crée une forte illusion que la demande de « vérification du compte » provient de l'application locale de confiance plutôt que du site web malveillant.

Conclusion : Cela confirme un fort potentiel d'impact sur l'activité et sur la confidentialité des utilisateurs. Même sans exfiltration de données ni exécution de code, le pont peut servir à la distribution de « Scareware » ou à la collecte d'identifiants en trompant les utilisateurs sur le véritable état de l'application.

Rapport final

1. Synthèse

Le Ostorlab Pentest Engine a découvert une exposition de pont JavaScript dans le composant BankWebViewKt de l'application cible. L'objet de pont Android est injecté dans la WebView sans aucune validation d'origine ni vérification de la source. Cette vulnérabilité permet à n'importe quel contexte JavaScript, y compris ceux injectés par des deep links non authentifiés, d'invoquer la méthode native showToast. Bien que le moteur ait confirmé que l'exécution de code natif et l'injection de commandes sont bloquées par un cloisonnement efficace au niveau des méthodes, la vulnérabilité reste à fort impact en raison de son potentiel d'ingénierie sociale très crédible et de tromperie de l'utilisateur.

2. Méthodologie

Le moteur de pentest a suivi un processus méthodique en plusieurs étapes pour tester la sécurité des ponts natifs :

  • Découverte du pont : a identifié les interfaces natives personnalisées en énumérant les propriétés de l'objet window dans la WebView.
  • Analyse du vecteur : a découvert qu'Activity2 traite les deep links avec des URI javascript: sans assainissement, ce qui fournit un point d'entrée pour du code externe.
  • Analyse statique : a localisé précisément l'injection vulnérable du pont dans com.target.app/BankWebViewKt.java.
  • Recherche d'escalade : a testé systématiquement la réflexion Java, l'injection de commandes et la prototype pollution afin de déterminer l'exploitabilité maximale.
  • Test de scénario d'impact : a validé le risque d'ingénierie sociale en enchaînant une redirection de phishing avec une fausse notification native.

3. Résultats

  • Exposition sans restriction de l'interface JavaScript : le pont Android est injecté dans la WebView sans validation d'origine, ce qui le rend accessible depuis n'importe quelle page chargée, y compris celles injectées via des deep links. Cela représente un défaut d'application d'une frontière de confiance appropriée entre le contenu web et la couche native.
  • Deep links non protégés : l'application autorise le traitement d'URI javascript: et data: via des Intents sans assainissement. Cela permet à des applications externes d'invoquer directement les méthodes du pont, ce qui offre un moyen direct de manipuler l'interface native.
  • Cloisonnement efficace au niveau du pont : les tentatives d'escalader l'exposition du pont vers une exécution de code au niveau système ont échoué. La méthode showToast traite toute entrée comme des chaînes littérales, et l'objet d'interface JavaScript est immuable, ce qui empêche la réflexion, la prototype pollution ou l'injection de commandes.
  • Absence de limitation de débit : le pont autorise des invocations répétées via des deep links sans restriction. Même si cela ne conduit pas à une exécution de code, cela peut permettre des manipulations rapides de l'interface ou des notifications de « spam », qui peuvent être exploitées dans des scénarios d'ingénierie sociale.

4. Remédiation

Pour traiter ces résultats, les actions suivantes ont été recommandées :

  • Supprimer ou restreindre l'accès : si le pont n'est pas nécessaire, supprimer entièrement le bloc addJavascriptInterface.
  • Validation de l'origine : si le pont est nécessaire, mettre en place une liste d'autorisation stricte de domaines de confiance (par exemple avec SecureWebViewClient) et vérifier l'origine courante avant d'autoriser l'exécution d'une méthode.
  • Assainir les deep links : mettre à jour le gestionnaire de deep links dans Activity2 pour rejeter toute donnée d'Intent commençant par les schémas javascript: ou data:.
  • Appliquer un durcissement de sécurité : désactiver les fonctionnalités WebView inutiles, comme l'accès aux fichiers et le débogage dans les builds de production, afin de réduire la surface d'attaque globale.

5. Conclusion

Cette étude de cas montre que même un pont « implémenté de façon sûre » peut présenter un risque de sécurité important lorsqu'il reste exposé à des entrées externes. En reproduisant la progression logique d'un attaquant, de la découverte de l'interface à la construction d'une chaîne d'ingénierie sociale fonctionnelle, le Pentest Engine d'Ostorlab a fourni la preuve définitive nécessaire pour sécuriser l'interface hybride.