Analyse et détection des maliciels
Structure interne du SE
Android est construit en suivant une structure en couche telle que représentée au tableau 1.1 (tirée de [50]), et est construit sur la base d’un noyau Linux. Ce type d’architecture permet d’abstraire les actions élémentaires (accès mémoire, gestion de l’alimentation, etc.) pour les développeurs d’applications et d’isoler les composantes les unes des autres pour des questions de sécurité. Chaque couche est responsable d’exécuter un ensemble spécifique de tâches et communique aux autres couches via des interfaces clairement définies. Elles sont présentées ci-après. Couche applicative La couche applicative est le domaine d’exécution de l’ensemble des applications Android déployées sur un appareil. Le SE Android expose un kit de développement logiciel (Software Development Kit, SDK) en Java permettant la création d’applications capables d’interagir avec le système via l’Application Programming Interface (API) d’Android. Le SDK et l’API sont mis à jour régulièrement par Google tant pour corriger des problèmes de sécurité que pour l’ajout de nouvelles fonctionnalités. Pour qu’une application s’exécute sur un appareil Android, celui-ci doit avoir une version du SE suffisamment à jour pour supporter les fonctionnalités de la version de l’API visée. Pour cette raison, un développeur peut volontairement choisir de créer une application visant une version de l’API antérieure à la dernière disponible. De cette façon, l’application peut être utilisable sur des appareils plus anciens. Une fois installées sur un appareil, les applications utilisent le cadriciel (ou framework) d’Android pour communiquer avec les différentes composantes du SE. Cadriciel Android Cette couche est responsable d’offrir aux applications les services applicatifs qu’elles nécessitent pour accomplir leurs tâches. Le tableau 1.2 reprend la description de John [67] et présente les principales composantes de cette couche. Les différentes abstractions offertes par cette couche sont ensuite redirigées vers les couches subséquentes pour le traitement.
Engin d’exécution Android Cette couche est responsable de l’exécution des applications en tant que telles. En plus de contenir le nécessaire à l’exécution des applications Android, cette couche est responsable de la conversion du bytecode Dalvik en instructions-machines exécutables par le processeur de la plateforme. Jusqu’à la version 4.4.4, cette conversion est réalisée par la compilation à la volée (Just-In-Time Compilation, JIT) par un interpréteur contenu dans la machine virtuelle Dalvik (Dalvik Virtual Machine, MVD). L’interpréteur est conçu pour conserver en mémoire vive le code compilé des sections de bytecode les plus fréquemment parcourues pendant l’exécution de l’application. Cela permet de minimiser l’impact sur la performance de la compilation à la volée, tel que décrit par Cheng et Buzbee [24]. Depuis la version 5.0 d’Android, Android Runtime (ART) remplace la MVD officiellement. Ce nouvel environnement d’exécution délaisse la compilation à la volée et utilise plutôt la compilation anticipée (Ahead-Of-Time Compilation, AOT). Tel qu’il est expliqué par Ghuloum et al. [42], ce changement de paradigme améliore les performances et réduit la consommation énergétique. Ces optimisations se font au coût d’un temps d’installation plus long et de plus d’espace de stockage utilisé par installation.
Applications Android
L’interaction d’une application avec le SE s’effectue via un API permettant d’utiliser les ressources disponible sur Android comme la géolocalisation, état de l’appareil ou l’envoi de messages textes. Le langage de programmation utilisé pour la création des applications est majoritairement le Java. Il est également possible d’intégrer du code en C/C++ à l’aide de l’interface Java de programmation native ou Java Native Interface (JNI). Lors de la compilation, le code Java est traduit dans un langage intermédiaire, appelé bytecode Dalvik. Cette forme intermédiaire permet de décrire les comportements de l’application dans avec une fine granularité (c.-à-d. de façon détaillée). Les instructions sous cette forme demeurent agnostiques à une architecture du microprocesseur et ne peuvent être directement exécutées par celui-ci. La conversion vers les instructions-machine appropriées pour un microprocesseur donné s’effectue par la plateforme elle-même lors de l’installation ou de l’exécution de l’application, selon l’engin d’exécution utilisé (ART ou MVD). Les sections de code en C/C++ sont, quant à elles, directement compilées en instructions-machines. Cela requiert que les bibliothèques logicielles C/C++ soient recompilées à l’aide des outils de compilation adaptés à chaque architecture de microprocesseur viser par le développeur. Les applications Android sont un assemblage de différentes composantes. Ces composantes sont résumées au tableau 1.4.
La description complète de ces composantes est présentée par Google Inc [53]. Lorsqu’une composante échange des informations avec une autre composante, il est question de communication intercomposante (CIC). Les CIC peuvent être réservées à des composantes détenant des permissions explicitement attribuées ou encore en étant directement sollicitées par l’émetteur. Afin d’être valide, le code de l’application est accompagné de son manifeste (AndroidManifest. xml). Il s’agit d’un fichier en format eXtensible Markup Language (XML) décrivant les paramètres d’exécutions nécessaires pour faire fonctionner correctement l’application. Il contient :
Vecteurs d’attaques
Les vecteurs d’attaques sont les techniques utilisées par les maliciels afin d’infecter un appareil Android. La compréhension des points d’entrées potentiels des maliciels permet d’identifier les points d’inflexion que doivent être capables de documenter les analyses de maliciels pour accroître les chances de les détecter. Ces vecteurs d’attaques sont détaillés ci-après. Exploitation du noyau Linux Ce type d’attaque vise à exploiter directement le noyau Linux (p. ex. Lineberry [78]) ou les bibliothèques logicielles chargées dans Android (p. ex. ZLabs [139]). Elle peut permettre de poser des actions demandant plus de privilèges qu’autorisés pour un processus. Dans certains cas, elle permet de compromettre l’ensemble du système. Ces techniques d’exploitation peuvent aussi être volontairement utilisées afin d’obtenir les privilèges de l’utilisateur root pour étendre les fonctionnalités du SE. Un exemple de ce type d’utilisation est l’exploit TowelRoot de Hotz [63] qui peut servir à élever les privilèges de l’utilisateur pour lui permettre de modifier les configurations du noyau Linux ou encore de remplacer la version d’Android sur son appareil même si celui-ci est verrouillé par le manufacturier. La découverte d’une faille pouvant mener à l’exploitation du noyau Linux et la création de l’exploit qui l’utilise demandent une expertise poussée sur le fonctionnement du SE ainsi que des connaissances techniques avancées (lecture d’instructions-machine, désassemblage, décompilations, examen de la pile ou du tas, lecture de rapports d’erreur, etc.). En revanche, une fois découverte et exploitée, l’exploit conçu peut être rendu public et utilisable avec un minimum d’effort sous la forme d’un outil libre ou de code réutilisable. Dans ce cas, la complexité pour abuser d’un système par cette faille de sécurité est grandement réduite. Des plateformes, comme Metasploit[97] et Drozer[87], sont d’ailleurs spécialisées dans la distribution de ce type d’exploits prêts à l’emploi.
Pour opérer, un maliciel de ce type utilise une ou plusieurs vulnérabilités du noyau, connues ou non, mettant en place les conditions favorables pour déclencher le comportement malicieux désiré. Certains exploits procèdent en altérant des structures internes du noyau contenues en mémoire vive. Celles qui sont particulièrement d’intérêt sont habituellement impliquées dans des fonctions critiques de l’exécution du SE comme la gestion du contrôle d’accès des utilisateurs. L’un des objectifs de ce type d’attaque est d’augmenter leurs accès ou privilèges sur la plateforme. Par exemple, l’exploit TowelRoot[63] utilise une vulnérabilité dans la gestion des futex (ou fast userspace mutex) permettant de suspendre un fil d’exécution si une condition donnée est remplie afin de protéger l’accès à une ressource déjà utilisée[134–136]. L’exploitation de cette vulnérabilité permet la modification de l’espace mémoire du noyau contenant la valeur de l’UID auquel appartient un fil d’exécution du noyau. Cela permet notamment d’attribuer l’UID « 0 » de l’utilisateur root à n’importe quel fil d’exécution, comme celui d’une invite de commande contrôlée par l’utilisateur. La présence de techniques de mitigation, comme l’ASLR, rend plus difficile l’exploitation du noyau par ce genre d’attaques. En effet, elles rendent l’exploitation du noyau Linux complexe et dépendante de la représentation de la mémoire vive au moment de leur exécution. De ce fait, un maliciel utilisant une même vulnérabilité sur des environnements différents (p. ex. architecture du processeur, version des bibliothèques logicielles ou du SE, etc.) peut échouer même si celui-ci est vulnérable.
|
Table des matières
Liste des tableaux
Table des figures
Liste d’abréviations
Remerciements
Introduction
1 Android
1.1 Structure interne du SE
1.2 Applications Android
1.3 Sécurité du SE
2 Analyse et détection des maliciels
2.1 Maliciels Android
2.2 Analyse des maliciels
3 Analyse de la mémoire vive
3.1 Acquisition
3.2 Analyse
4 Application de l’analyse de la mémoire vive
4.1 Analyse différentielle de la mémoire vive
4.2 Étude de cas
4.3 Méthodologie
4.4 Résultats
4.5 Discussion
4.6 Limites
4.7 Travaux futurs
Conclusion
A Décompilation de l’application HelloWorld
B Code de l’Application A
C Réflexion
D Compilation du noyau Linux
E Compilation de LiME
F Relation Root et PPID sur Android
Bibliographie
Télécharger le rapport complet