Frida, l’outil dédié à la sécurité des applications mobiles

Introduction

Les applications mobiles occupent aujourd’hui une place centrale. Usage personnel ou professionnel, elles sont omniprésentes et souvent indispensables dans notre quotidien.

Cependant, cette omniprésence expose également les utilisateurs et les entreprises à des risques accrus en matière de sécurité. Identifier les vulnérabilités avant qu’elles ne soient exploitées est devenu une priorité, et des outils open source comme Frida permettent aux pentesters de tester la robustesse des applications mobiles face aux attaques.

Cet article a pour objectif de présenter de manière générale l’outil Frida, ses principaux usages, ainsi que quelques exemples concrets auxquels un pentester pourrait être confronté lors d’un test d’intrusion mobile.

Étant donné la richesse et la puissance de Frida, nous ne pourrons aborder qu’une partie de ses fonctionnalités dans cet article. Nous espérons néanmoins que cette introduction vous incitera à explorer par vous-même Frida et ses nombreux cas d’utilisation.

Guide complet sur l’outil Frida

Frida : principes et fonctionnement

Frida est une boîte à outils qui permet d’interagir dynamiquement avec des programmes en cours d’exécution.

Son usage ne se limite pas aux applications mobiles. En plus des apps Android et iOS, Frida peut être utilisé avec des logiciels natifs sur Windows, macOS ou Linux, entre autres.

Le principe de Frida est qu’il permet d’interagir avec un processus ciblé en cours d’exécution en y injectant des scripts JavaScript arbitraires. Cela permet de modifier dynamiquement le comportement du logiciel. Concrètement, ces scripts peuvent « s’accrocher » aux méthodes des classes présentes dans le code et altérer divers éléments, comme la valeur retournée ou les arguments en entrée. Ainsi, la logique interne du programme peut être modifiée sans toucher au binaire. Cette technique est appelée le « hooking » de méthode.

Frida offre une grande variété d’usages. Un développeur peut s’en servir pour mieux comprendre un logiciel et le débugger de manière simple et rapide. Lors d’un test d’intrusion d’application mobile, Frida est principalement utilisé pour contourner des mécanismes de sécurité qui pourraient gêner le déroulement de l’audit ; ou pour exploiter des vulnérabilités, sans avoir à modifier le code de l’application.

Concernant le développement de scripts, un des points clés de Frida est son API en plus de différents outils qui facilitent l’interaction avec les processus ciblés. Nous verrons des exemples de scripts plus loin dans cet article. Notez par ailleurs que Frida peut également être utilisé avec différents langages, comme Python ou NodeJS, en complément du code JavaScript.

Dans un contexte mobile, Frida est installé à la fois sur la machine de l’utilisateur et sur le téléphone où l’application à tester est installée. Nous ne détaillerons pas ici l’installation de Frida, car sa documentation est déjà très complète.

Toutefois, nous nous attarderons sur les deux principaux types d’installation, intéressants en fonction des situations rencontrées lors d’un pentest mobile.

Notez que dans les deux cas, Frida est installé de la même manière sur la machine du pentester. La différence réside dans la façon dont Frida est installé sur le téléphone où est installée l’application mobile à tester.

Injected installation

Le premier type d’installation est appelé « Injected ». Pour ce mode, le téléphone sur lequel le pentester effectue ses tests doit être rooté et le composant « frida-server » y sera installé.

C’est le cas le plus courant en pentest mobile. Ce composant, comme son nom l’indique, est un serveur Frida qui écoute par défaut en TCP et localhost sur le port 27042.

Le principal avantage de ce mode est est qu’il suffit d’installer le composant une seule fois. De plus, il prend en charge toutes les applications installées sur le téléphone, ce qui permet au pentester de s’attacher à n’importe quelle application à tout moment.

Frida Injected installation
Injected installation

Embedded installation

Le deuxième type d’installation est appelé « Embedded ». Il est pratique lorsque l’on souhaite utiliser Frida sur une application installée sur un téléphone non rooté.

Dans ce cas, il faut intégrer le composant « frida-gadget » directement dans l’application cible. Cela nécessite de « patcher » l’application, soit automatiquement, soit manuellement, en décompilant l’application, en y intégrant « frida-gadget », puis en la recompilant.

L’avantage principal de cette méthode est qu’elle permet d’utiliser Frida sur des téléphones non rootés. Cependant, ce processus peut être fastidieux s’il doit être répété pour plusieurs applications. De plus, en fonction des applications, les étapes d’installation peuvent différer voire ne pas fonctionner.

Frida Embedded Installation
Embedded installation

Ressources pour installer Frida

Outils et fonctionnalités de Frida

Lorsque Frida est installé, il intègre divers outils qui peuvent être utilisés en ligne de commande sur la machine utilisateur, en fonction du besoin.

Dans cette section, nous verrons les principaux outils Frida utilisés lors de nos pentests mobile et les commandes associées.

L’outil frida-ls-devices permet de lister les appareils avec lesquels Frida peut interagir. Pour ce faire, il suffit d’utiliser la commande suivante :

frida-ls-devices

Résultat de la commande :

Resultat de la commande frida-ls-devices

Ici, on peut voir que Frida est capable d’interagir avec deux appareils, la machine hôte qui est l’ordinateur de l’utilisateur, et un téléphone portable, branché en USB.

L’outil frida-ps permet de lister les processus d’un appareil et notamment les applications installées. Ci-dessous, les commandes courantes utilisées lors d’un pentest mobile.

Pour lister les processus d’un appareil en fonction de son ID (trouvé avec frida-ls-devices) :

frida-ps -D <device_ID>

Pour lister les processus de l’appareil connecté en USB :

frida-ps -U

Résultat des deux commandes précédentes :

Pour lister les applications en exécution de l’appareil connecté en USB :

frida-ps -Ua

Résultat de la commande :

Pour lister toutes les applications installées sur l’appareil connecté en USB :

frida-ps -Uai

Résultat de la commande :

La commande frida offre de nombreuses options, mais elle est principalement utilisée pour injecter des scripts JavaScript ; permettant ainsi de modifier le comportement de l’application à volonté.

Frida permet d’injecter un script soit dès le début du processus (mode spawn) soit sur un processus déjà existant (mode attach).

Dans certains cas, il est utile d’exécuter le script dès le démarrage de l’application, par exemple pour contourner des mécanismes de sécurité qui s’activent dès le lancement, comme le SSL pinning ou les protections anti-root.

Pour injecter un script JS dès le début d’un processus cible, sur un appareil connecté en USB :

frida -U -f owasp.mstg.uncrackable1 -l .\scripts-frida\bypass-root.js

Ici, l’option -f est utilisée pour s’injecter au lancement du processus. Si l’application est déjà lancée, la commande la redémarrera.

Injection d’un script JS sur un processus cible déjà lancé, en utilisant son PID, sur un appareil connecté en USB :

frida -U -p 11316 -l .\scripts-frida\bypass-root.js

Ici, nous avons utilisé l’option -p pour s’attacher au processus en cours en utilisant son PID, mais il est possible de le faire avec son Nom, avec l’option -n, ou avec son Identifiant avec l’option -N. Ces options sont d’ailleurs les mêmes pour l’outil frida-trace que nous verrons par la suite.

Dans les exemples précédents, l’application cible est owasp.mstg.uncrackable1 et son PID est 11316. Le script utilisé, bypass-root.js, a été créé par l’attaquant pour contourner les contrôles qui bloquent l’application si elle est installée sur un appareil rooté

Nous verrons dans la suite de cet article un exemple concret nécessitant l’exécution de ce type de commande.

frida-trace est un outil qui permet de tracer dynamiquement les appels de fonction. Par exemple, il arrive parfois qu’une fonctionnalité intéressante soit identifiée sur l’application ciblée. Dans ce cas, il est intéressant de trouver la méthode responsable de cette fonctionnalité dans le code.

Cela permet de tester si la modification des valeurs d’entrée ou de sortie peut altérer le comportement de la fonctionnalité. frida-trace facilite grandement ce processus tout en offrant une grande flexibilité.

Prenons l’exemple d’un bouton qui valide ou non une entrée utilisateur en fonction de sa valeur. Si le bouton affiche « SUBMIT », nous pouvons essayer de tracer la méthode appelée lors de son utilisation en exécutant la commande suivante :

frida-trace -U -N b3nac.injuredandroid -j "*!*submit*"

Dans cette commande, nous nous attachons à l’application cible b3nac.injuredandroid, déjà en cours d’exécution, en utilisant son identifiant avec l’option -N.

Avec l’option -j, nous recherchons, dans toutes les classes Java, les méthodes dont le nom contient « submit ».

La syntaxe pour cette option est « classe_java ! nom_méthode ». D’autres options permettent, par exemple, d’exclure certaines méthodes de la recherche ou de rechercher des méthodes dans d’autres langages ou librairies.

En l’occurrence, le résultat de la commande précédente est le suivant :

frida-trace a trouvé 20 méthodes contenant « submit » dans leur nom. En parrallèle, il a automatiquement généré un script JavaScript pour chaque méthode identifiée.

Ces scripts peuvent ensuite être modifiés pour changer les valeurs en entrée et en sortie de chaque méthode.

Lorsque nous cliquons sur le bouton sur l’application, la sortie suivante nous est retournée :

Nous avons désormais le nom de la classe, FlagOneLoginActivity, et celui de la méthode, submitFlag, qui sont responsables du bouton.

Pour modifier les variables d’entrée ou de sortie de cette méthode, il suffit d’éditer le fichier généré par frida-trace ou de créer un script JS pour se hooker à la méthode. Nous verrons comment procéder dans la section suivante.

Exploitations de vulnérabilités et scripting avec Frida

Imaginons une application dans laquelle l’utilisateur doit fournir un flag pour valider une action en cliquant sur un bouton, comme illustré dans l’image ci-dessous :

Comme nous l’avons déjà vu précédemment dans cet article, grâce à frida-trace, nous avons identifié la méthode submitFlag dans le code décompilé, qui est appelée lorsque le bouton est cliqué :

Code décompilé

Ici, nous pouvons voir que si la méthode a de la classe d.s.d.g retourne true, alors la validation du flag est contournée et le challenge sera considéré par l’application comme réussi. Pour forcer la méthode à envoyer true, il est possible de relancer frida-trace, mais cette fois sur la méthode a.

Ainsi frida-trace nous générera automatiquement un script JS, permettant de modifier dynamiquement la variable retournée par la méthode.

Nous exécutons donc la commande suivante :

frida-trace -U -N b3nac.injuredandroid -j "d.s.d.g!a"

Ensuite, nous nous rendons dans le fichier généré par frida-trace que nous pouvons trouver au chemin « …/ __handlers__/d.s.d.g/a.js » :

Nous remarquons les fonctions onEnter et onLeave de l’API JavaScript de Frida. Ces dernières permettent de modifier les variables d’entrée et de sortie de la méthode a.

Pour forcer la méthode à retourner toujours true, nous pouvons écrire return true dans la fonction onLeave :

Si la valeur retournée devait être « 1 » à la place de « 0 », nous aurions pu écrire retval.replace(1).

Suite à la sauvegarde du script et au clic sur le bouton avec une valeur de flag invalide, l’application considère que nous avons quand même réussi le challenge :

Il est important de noter qu’à l’origine, l’objectif de cet exercice n’est pas de contourner la validation du flag, mais plutôt de découvrir la valeur du flag. Toutefois, nous avons contourné la validation du flag dans cet exemple pour illustrer une méthode d’exploitation utilisant frida-trace.

Cette même approche peut également être utilisée pour contourner des fonctionnalités critiques, comme la validation d’un code PIN dans une application bancaire, par exemple.

Référence de l’application.

Une des principales composantes de Frida est son API JavaScript. Cette dernière permet de créer des scripts personnalisés pour interagir dynamiquement avec les méthodes d’une application cible.

Dans cette section, nous explorerons quelques exemples d’exploitations dans un environnement Android.

Changement de la valeur retournée

Lorsqu’une méthode est identifiée, il est possible de l’appeler directement avec la fonction de l’API, Java.use et la propriété implementation.

Pour illustrer ce cas de figure, nous pouvons reprendre notre scénario précédent exploité avec les scripts générés par frida-trace.

Pour rappel, avec le code suivant, si la méthode a de la classe d.s.d.g retourne true, alors la validation du flag est contournée et le challenge sera considéré par l’application comme réussi :

Cette fois-ci, nous écrivons nous même notre script JS pour forcer la méthode a de la classe d.s.d.g à retourner true, en utilisant Java.use et implementation :

Avec Java.use, on sélectionne la classe pour ensuite réécrire dynamiquement la méthode avec implementation.

Ensuite, avec la commande suivante, on peut s’injecter dans l’application avec le script que nous venons de créer :

frida -U -N b3nac.injuredandroid -l .\scripts-frida\bypass.js

Lorsque l’on clique sur le bouton dans l’application, nous arrivons à contourner la validation du flag et l’application considère que nous avons réussi le challenge.

Dans la console Frida, on peut voir que nous avons bien hook la méthode a.

Récupération du résultat

Maintenant que nous avons vu comment contourner la validation, essayons de récupérer la valeur du flag.

Pour rappel, si la condition des exemples précédents est vraie alors le challenge est vrai. Lorsqu’on regarde de plus près cette même condition, on peut voir que la condition est vraie si l’entrée utilisateur est égale au résultat retourné par la méthode a de la classe k avec comme argument la chaîne de caractère « k3FElEG9lnoWbOateGhj5pX6QsXRNJKh///8Jxi8KXW7iDpk2xRxhQ== » :

Voici le code décompilé de cette méthode a :

En la parcourant rapidement, on comprend que c’est une méthode qui déchiffre la chaîne de caractères passée en argument.

Pour récupérer son résultat, au lieu d’essayer de récupérer la clé privée, qui peut être obfusquée, et de déchiffrer la chaîne de caractères manuellement, utilisons simplement quelques lignes de JavaScript et laissons travailler Frida !

Voici ci-dessous le script JS que nous injecterons avec Frida :

Comme précédemment, nous ciblons la classe k avec la fonction Java.use.

Puis nous réécrivons dynamiquement la fonction de déchiffrement a en l’utilisant et en passant en argument la chaîne de caractères à déchiffrer.

Ensuite, nous affichons dans la console la chaîne de caractères chiffrée dans un premier temps, et déchiffrée dans un second temps.

Enfin, nous retournons le résultat déchiffré pour ne pas casser la logique du code original qui attend en retour le résultat déchiffré.

La commande suivante est utilisée pour injecter notre script :

frida -U -N b3nac.injuredandroid -l .\scripts-frida\injuredAndroidFlag6.js

Lorsque nous cliquons sur le bouton, nous avons le flag chiffré et déchiffré dans la console :

Communauté Frida

Si vous ne souhaitez pas écrire vos propres scripts, comme vu précédemment, vous pouvez profiter des nombreux scripts déjà développés par la communauté Frida. Ils sont disponibles sur Frida CodeShare ou sur divers dépôts.

Ces scripts sont souvent conçus pour résoudre des problèmes récurrents rencontrés de pentests mobile. Par exemple, il arrive fréquemment qu’une application utilise un système de protection contre les attaques de type « Man in the Middle », empêchant ainsi un attaquant d’intercepter le trafic HTTP entre l’application et ses serveurs. Ce mécanisme est appelé « SSL Pinning ».

Cependant, lors d’un pentest mobile, il est crucial d’intercepter et d’analyser le trafic. Si l’application testée utilise du SSL Pinning, il est donc nécessaire de contourner cette protection. Bien que cela puisse être complexe dans certains cas rares, la plupart du temps, il est possible de le faire en utilisant des scripts communautaires simples pour désactiver cette protection.

Un autre exemple de test à effectuer lors d’un audit consiste à vérifier si les mots de passe et autres informations sensibles sont stockés de manière sécurisée, comme dans des Shared Preferences chiffrées. Pour cela, il est utile de suivre dynamiquement le chemin emprunté par le mot de passe au moment où il est saisi dans l’application.

Pour faciliter cette tâche, la communauté a développé des scripts permettant d’analyser en temps réel les fichiers système appelés par l’application. Par exemple, si, lors de l’exécution de ce type de script, nous voyons que le fichier des Shared Preferences chiffrées est ouvert lorsque nous saisissons un mot de passe, cela signifie que le mot de passe est stocké de manière sécurisée.

La communauté Frida est donc une excellente ressource pour gagner du temps et de l’efficacité. Cependant, soyez toujours vigilant et vérifiez soigneusement le code avant de l’exécuter !

Conclusion

Frida est un outil puissant pouvant être utilisé aussi bien sur les plateformes mobiles que sur d’autres systèmes. Sa flexibilité et sa modularité en font un allié incontournable pour les audits de sécurité mobile.

Sa puissance est renforcée par une communauté active, qui développe une grande variété de scripts pour répondre aux innombrables cas d’usage rencontrés.

Nous avons exploré quelques exemples concrets d’utilisation de Frida, et nous espérons que cela vous incitera à approfondir votre maîtrise de cet outil.

Auteur : Lorenzo CARTE – Pentester @Vaadata