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
- Du matériel permettant de connecter un ordinateur à l'objet via USB, JTAG, UART, JPIO, I2C ou SPI à voir selon le matériel déjà disponible
Pour la partie logicielle, de nombreux outils pourraient être utiles à ce projet et seront à investiguer :
- Ghidra
- IDA
- Radare2
- Binwalk
- Apktool
- dex2jar
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 :
Malheureusement, cela n'a pas abouti puisque le mécanisme de "Read Out Protection" est activé sur la puce, empêchant la lecture des données.
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é.