IMA5 2019/2020 P20
Présentation générale
Description
Les objets connectés commencent à se démocratiser dans les habitations (thermostats connectés, caméras connectés, serrures connectées, ...) et dans l'industrie (remontée de données, supervision, ...). Le marché de l'IOT (Internet Of Things) est très concurrentiel et les produits doivent être mis sur le marché rapidement à bas coût. Les problématiques de sécurités sont donc très souvent délaissées. De plus, les faibles performances et les tailles de mémoires limitées empêchent d’implémenter les méthodes classiques du monde Internet (anti-virus, pare-feux, mise à jour, certificats ,...).
Ce projet propose de concevoir et réaliser les briques de base d'une plateforme d'évaluation de la sécurité d'objets connectés :
- Une partie logicielle permettant la récupération de code embarqué, la décompilation, la compilation, ...
- Une partie matérielle permettant d'accéder aux données d'un objet (récupération du firmware) et l'injection de signaux ;
- Une éventuelle base de données permettant de sauvegarder les résultats et les failles trouvées.
Objectifs
Dans un premier temps, l'objectif sera d'être capable de récupérer les données utilisateur d'un objet connecté puis son firmware dans le but d'y trouver de potentielles failles de sécurité. Le code source de l'application mobile associée à l'objet pouvant lui aussi être source de failles de sécurité, sera de même à analyser.
Dans un second temps, le but ultime de ce projet serait d'automatiser une partie de ce processus et de le rendre utilisable pour un grand nombre d'objets connectés divers et variés.
Préparation du projet
Cahier des charges
Choix techniques : matériel et logiciel
Pour la partie matérielle, ce projet nécessite :
- Un objet connecté à exploiter à définir avec les encadrants, dont le nom et les résultats obtenus sur ce dernier ne seront pas explicités ici
- Convertisseur série -> USB pour l'exploitation de pins de communication série
- Debugger pour l'exploitation de pins de debug
Pour la partie logicielle, de nombreux outils pourraient être utiles à ce projet et seront à investiguer :
- Ghidra
- IDA
- Radare2
- Binwalk
- Firmwalker
- Apktool
- dex2jar
- JDGUI
- OWASP ZAP
Liste des tâches à effectuer
Ce projet se découpe en 4 tâches principales :
- Récupération des données utilisateur de l'objet
- Récupération du firmware de l'objet et analyse dans le but de trouver des failles de sécurité
- Analyse du code source de l'application mobile associée à l'objet dans le but de trouver des failles de sécurité
- Automatisation d'une partie des trois premières tâches
Calendrier prévisionnel
Réalisation du Projet
Récupération du code d'une carte Arduino Uno
Afin de pouvoir me familiariser avec le sujet, les différents logiciels disponibles et en attendant d'avoir l'objet sur lequel travailler, j'ai utilisé une carte Arduino Uno. Après y avoir téléversé un des programmes proposés en exemple sur l'IDE Arduino qui allume puis éteint la LED chaque seconde, j'ai tenté de le récupérer via le port série. Pour cela j'ai utilisé l'utilitaire avrdude qui permet de réaliser des actions sur la mémoire d'un microcontroleur AVR comme présent sur l'arduino.
J'ai alors pu extraire de la mémoire flash de l’Arduino un fichier Intel Hex avec la commande suivante :
avrdude -c arduino -P /dev/ttyACM0 -p m328p -b 115200 -U flash:r:blink.hex:i
Une fois en possession de ce fichier, j'ai utilisé l'outil Ghidra, outil de reverse engineering développé par la NSA, afin de pouvoir analyser le contenu du fichier dans une forme plus compréhensible :
L’outil nous permet alors, de visualiser le contenu du fichier sous forme de code assembleur mais aussi sa transposition en code C.
Sur la partie gauche, l’outil nous présente toutes les fonctions qu’il a pu reconstruire à partir du fichier. Il est cependant impossible de retrouver les noms de fonctions ou variables personnalisées utilisées dans le code qui a été téléversé sur l’Arduino. Les fonctions sont alors nommées FUN_code_xxxxxx. Le code C obtenu est très bas niveau avec de la manipulation d’adresses mémoire, de registres etc. Sachant ici à quoi le code d’origine ressemble, on retrouve très vite que le void loop() du programme d’origine correspond ici au do { … } while (true) de la fonction FUN_code_000160 que l’on peut voir sur la capture d’écran ci-dessus. La fonction FUN_code_000070 correspond à la fonction digitalWrite() du programme d’origine et la fonction FUN_code_0000de correspond à la fonction delay.
Etant donné que Ghidra n’est pas capable de retrouver les librairies C utilisées à partir du fichier Intel Hex donné (contrairement à lorsqu’on lui donne un fichier ELF), il devient très vite compliqué de s’y retrouver lorsque le code d’origine utilise de nombreuses fonctions importées.
J’ai aussi pu tester le framework Radare2 qui a la même finalité que Ghidra. Il s’avère plus compliqué à prendre en main et moins ergonomique. Lors de l’analyse du fichier obtenu précédemment, Radare2 donne en résultat qu’une unique fonction en langage assembleur et ne permet pas de la transposer en code C. L’utilisation de Ghidra sera donc très certainement préférée pour ce projet.
Récupération de données utilisateur et firmware d'un objet connecté
En ouvrant l'objet connecté étudié, nous nous sommes rendus compte de la présence de pins de debug RX, TX, CLK, IO, VCC, GND. Avec l'aide de Thierry Flamen, nous avons soudé des fils de connexion sur chacun de ces pins afin de pouvoir les exploiter.
Dans un premier temps, j'ai utilisé un convertisseur série -> USB afin d'exploiter les pins TX et RX. Je n'ai eu aucun résultat avec minicom.
Dans un second temps, j'ai utilisé un JLink SEGGER debugger, dispositif permettant de debugger des micro-controleurs dont celui présent dans l'objet étudié. Le constructeur du micro-controleur en question met à disposition un outil en ligne de commande permettant entre autres de programmer la puce ou de lire son contenu par l'intermédiaire du JLink. J'ai alors connecté ce dernier à l'objet avec les pins de Serial Wire Debug CLK et IO présents sur le circuit puis tenté de récupérer le firmware :
J'ai alors été confronté à une protection empêchant la lecture des données. Après quelques recherches, le seul moyen d'enlever cette protection serait d'effacer le contenu du micro-contrôleur et de le reprogrammer. Cela n'est pas envisageable puisque ceux sont les données actuellement présentes dans l'objet que je souhaite récupérer.
Suite à ces deux impasses, j'ai du me tourner vers la dernière piste pour la récupération des données de l'objet, à savoir, exploiter le fait que l'objet puisse être mis à jour à partir de l'application mobile associée.
Afin de mettre à jour l'objet connecté, après appairage, l'application mobile réalise des appels API sur un serveur distant afin de vérifier si l'objet est à jour ou non et récupérer la dernière version du firmware. J'ai donc décidé d'observer ces communications afin de voir si il y avait quelque chose à exploiter à ce niveau. Pour cela, j'ai utilisé le logiciel OWASP ZAP qui est un proxy présentant de nombreuses fonctionnalités de pentest. Après avoir installé ce proxy sur mon ordinateur. J'ai configuré un smartphone android pour utiliser ce dernier. Dès lors, toutes les requêtes HTTP passent par l'OWASP ZAP avant d'atteindre leur cible. Le logiciel permet alors de visualiser toutes les requêtes réalisées à partir du smartphone avec les différents paramètres envoyées ainsi que les réponses reçues. Une fois le proxy mis en place, j'ai pu visualiser les différents échanges réalisées entre l'application mobile associée à l'objet connecté à son lancement et le serveur distant. Un premier appel de login est réalisé au lancement de l'application. J'ai pu retrouver en clair sur le logiciel servant de proxy, les paramètres associées à cet appel, à savoir, mon adresse email servant de login ainsi que mon mot de passe. Les identifiants étant corrects, le serveur distant envoie en réponse un token nécessaire à tous les autres appels API. Ce token est aussi lisible sur le logiciel. Après avoir récupéré mon token, j'ai tenté de réaliser un appel API via Postman. Cela a fonctionné et nous pouvons donc de cette façon, récupérer les données stockées sur le serveur distant.
Après ce premier appel de login, un appel de vérification de la version du firmware est réalisé. L'objet en ma possession n'étant pas à jour, il s'en est suivi un autre appel afin de récupérer la mise à jour à appliquer sur l'objet. La mise à jour a alors été téléchargé sur le smartphone. Après avoir installé un explorateur de fichiers sur ce dernier, j'ai rapidement retrouvé le fichier de mise à jour téléchargé. Malheureusement, ce fichier d'extension .des semble être un fichier encrypté.
Des captures d'écran illustrant les résultats sont visibles sur le wiki du projet Gitlab.
Reverse engineering sur l'application mobile associée à l'objet
Les objets connectés ne pouvant pas embarquer une grande quantité de mémoire du fait de leur petite taille, les données mesurées sont la plupart du temps envoyés et stockés sur un serveur distant. Généralement, il est alors proposé une application mobile associée à l'objet permettant de synchroniser les nouvelles données mesurées et les visualiser.
C'est le cas pour l'objet connecté étudié qui est proposé avec une application mobile associée. Cette application permet d'analyser les données récupérées par l'objet après synchronisation. Pour cela, l'application doit interagir avec l'objet mais aussi le serveur distant sur lequel vont être stockées les données. Le code de l'application peut donc potentiellement contenir des informations pouvant compromettre la solution.
Après avoir téléchargé le fichier APK de l'application, j'ai utilisé différents outils pour décompiler le code afin de pouvoir l'analyser.
Dans un premier temps, j'ai utilisé l'outil dex2jar afin de convertir le fichier APK en un JAR :
d2j-dex2jar.sh application.apk
J'ai ensuite utilisé JD-GUI pour décompiler le JAR et visualiser le code source. Il m'a fallu ajouter des droits d’exécution et de lecture sur le JAR afin d'éviter une erreur peu explicite lors de l'import du JAR sur l'outil. Le code récupéré n'étant pas offusqué, j'ai pu l'analyser.
Je suis assez vite tombé sur un fichier de constantes contenant l'adresse de l'API du constructeur ainsi que ses différents endpoints utilisées afin d'agir sur les données utilisateurs. Cette API permet par exemple de récupérer les informations de profil utilisateur, de les éditer, de récupérer les données mesurées par le capteur de différentes façons ou encore de télécharger des mises à jour de firmware.
L'application utilise une base de données fichier SQLite stockée sur le téléphone de l'utilisateur composée d'une table regroupant les différentes mesures pouvant être prises avec l'objet associé mais aussi avec deux autres, l'application étant commune à trois différents proposés par le constructeur.
Après avoir découvert cela, j'ai creusé un peu plus afin de savoir comment était synchronisé l'ensemble des données entre l'objet, la base de données sur le téléphone ainsi que l'API.
A son inscription, l'utilisateur fournit des données sur lui même l'identifiant et aidant à analyser les données récupérées par l'objet. Ces données utilisateur sont envoyées directement sur le serveur distant grâce à un certain endpoint de l'API. L'utilisateur peut alors ensuite se connecter sur l'application avec le login et le mot de passe entrés lors de son inscription. La vérification de son identité est faite par un appel API qui, si l'utilisateur est reconnu par le serveur, fournit un token qui permettra d'identifier l'utilisateur pour les appels API concernant les actions sur les données récupérées par l'objet.
Une fois connecté, l'utilisateur peut appairer son objet à l'application via bluetooth puis synchroniser les données mesurées par l'objet. Toutes les données présentes sur l'objet sont alors récupérées sur l'application via le bluetooth et celles qui ne sont pas présentes en base de données (la vérification est faite en comparant la date des mesures) sont stockées en base de données sur le téléphone avec un certain champ indiquant que ces données n'ont pas encore été téléchargé sur l'API. Si le téléphone mobile est connecté à internet, toutes les données de la base dont le champ en question est dans l'état "non téléchargé" sont envoyées sur le serveur distant à travers l'API grâce au token d'identification de l'utilisateur récupéré à sa connexion. L'état des données uploadées est alors modifié pour ne pas qu'elles soient envoyées une deuxième fois sur le serveur distant.
Lorsque l'utilisateur consulte la page d'analyse des données sur son application, les données à analyser sont récupérées dans la base de données si présentes sinon, sur le serveur distant par appel API. Elles peuvent être récupérées sur différentes périodes selon la page consultée sur l'application. Lorsque les données sont récupérées pour les trente derniers jours, les données de la base sur le téléphone sont effacées et remplacées par les données récupérées grâce à l'API. La base de données ne sert donc qu'à stocker temporairement les données afin de pouvoir répondre aux demandes de l'utilisateur dans le cas ou son téléphone n'est pas connecté à internet. Elle joue aussi un rôle de cache.
J'ai aussi pu me rendre compte que lorsque l'on quitte l'application en y étant connecté, on l'est toujours en relançant l'application. Le login et le mot de passe doivent donc être stockés quelque part. L'analyse du code m'a permis de découvrir que ces données sensibles sont bien stockées sur le téléphone grâce au système de préférences d'application Android. Les données sont stockées en clair au format clé-valeur dans un fichier qui n'est supposé être accessible que par l'application. Avec un peu de recherche, on se rend compte que ce fichier peut être accessible via un simple explorateur de fichiers sur un téléphone rooté.
En dehors de cet aspect stockage de données, j'ai pu voir dans le code qu'il y avait une fonctionnalité de mise à jour du firmware de l'objet connecté via l'application avec notamment de nombreuses mentions à la norme de transmission OTA, "over the air". Cela constitue une piste pour la partie récupération de firmware de l'objet connecté.