Analyse et détection des maliciels
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 (AndroidMa-nifest.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 :
— le nom de l’application et sa version ;
— la description de certaines composantes (Activities, Intent Filters, Broadcast Receivers, Services et Content Providers) ;
— les permissions demandées par l’application pour communiquer avec des composantes protégées de l’API ;
— le point d’entrée pour l’exécution de l’application ;
— la version minimum de l’API que requiert l’application pour fonctionner correctement et
— l’ensemble des bibliothèques logicielles externes auxquelles l’application est liée.
Lorsque l’application est construite en vue d’être déployée, le code compilé (sous la forme d’un ou plusieurs fichiers classes[x].dex où x est la numérotation du fichier si le code est réparti en plusieurs fichiers), le manifeste et les différentes ressources (bibliothèques logicielles, images, base de données, etc.) sont consolidés dans une archive compressée en format Zip nommée Android Package (APK) [48]. Les fichiers inclus dans l’archive sont signés à l’aide d’une clef privée incluse dans un certificat généré par le développeur ou automatiquement si l’application est en mode déverminage. La clef publique du certificat et ses informations sont incluses dans l’APK de même que les valeurs des signatures des fichiers validant leur intégrité. Cette clef publique est utilisée pour valider la signature des fichiers et ainsi garantir que les fichiers présents dans l’APK sont ceux d’origine.
Sécurité du SE
Au cours de son existence, Android a subi plusieurs modifications visant à améliorer la sécurité de la plateforme. L’équipe de l’Android Open Source Project (AOSP) publie des correctifs de sécurité pour limiter le risque d’actions malicieuses visant l’exploitation d’Android (vol de donnée, logiciel espion, élévation de privilèges, etc.). Ces correctifs visent également à corriger les vulnérabilités identifiées dans le SE. Bien que cette approche permette de corriger les menaces rapportées, elle n’est pas efficace contre celles qui ne sont pas rapportées ou dont le correctif n’est pas encore disponible. De plus, avant d’être déployés sur les appareils, ces correctifs sont révisés par les manufacturiers respectifs puis acceptés par les utilisateurs individuellement. Les détails de ce processus sont décrits par Wyatt [129]. Cela introduit d’importants délais dans le déploiement de correctifs et peut même l’empêcher si ceux-ci s’adressent à des appareils que le manufacturier ne souhaite plus supporter.Pour ces raisons, des efforts ont été investis dans l’ajout de mécanismes proactifs de sécurité pour contenir les dommages potentiels que l’exploitation d’une faille de sécurité peut avoir sur Android. Le bac à sable applicatif, SELinux et la distribution aléatoire de l’espace d’adressage (Address Space Layout Randomization, ASLR) sont des éléments importants de cette sécurité et sont détaillés ci-après 1.
Bac à sable applicatif
L’une des principales défenses d’Android est son bac à sable applicatif, c’est-à-dire un envi-ronnement où les interactions avec les autres ressources ou applications de l’appareil sont impossibles ou fortement contrôlées. Ce contrôle s’exerce à l’aide de mesures de contrôle d’accès et des restrictions de privilèges. Le bac à sable s’applique à toutes les applications Android exécutées sur un appareil et vise tant la portion Java que la partie native du code puisqu’il fait appel à plusieurs mécanismes de sécurité présents dans le noyau Linux [51]. Les identifiants d’utilisateur (User Id, UID), les identifiants de groupe (Group Id, GID) et le mécanisme de permissions sont la pierre angulaire du bac à sable applicatif d’Android.
Identifiant unique d’utilisateur (UID)
Le contrôle des accès sur la base des rôles est implanté dans Linux sous la forme d’identifiant d’utilisateur (User Id, UID). En attribuant un UID par application Android au moment de l’installation et en les forçant à s’exécuter uniquement via ce UID, chaque application est confinée dans un espace de fichiers isolé des autres applications du système. La seule exception est s’il s’agit d’un utilisateur privilégié (c.-à-d. root) qui peut outrepasser ces restrictions. Cependant, l’utilisateur root sur Android n’est pas disponible en dehors des processus du système, à moins de modification du SE ou par l’exploitation d’une faille de celui-ci. Si une application nécessite plus d’accès pour s’exécuter (par exemple, communiquer avec un serveur Web ou si elle souhaite écrire sur l’espace de stockage), elle doit obtenir des permissions [52].
Permissions
Les permissions sont une mesure de contrôle d’accès à l’API pour limiter l’accès à certaines fonctionnalités qui peuvent porter préjudice à l’utilisateur si elles sont octroyées à une appli-cation illégitime ou malicieuse. Pour cette raison, les permissions doivent être entérinées par l’utilisateur avant d’être données au demandeur. En plus des permissions faisant partie de l’API d’Android, il est possible de créer de nouvelles permissions afin de protéger certains accès sensibles aux données ou services qu’une application peut souhaiter exposer de façon restreinte.
Chaque permission correspond à un GID du noyau Linux. Chaque GID possède les accès aux ressources du SE requises pour l’exécution des comportements rattachés à cette permission. Pour chaque permission octroyée, Android ajoute le UID de l’application au groupe qui y correspond. C’est de cette façon que l’application obtient les privilèges lui permettant d’agir en accord avec la permission demandée.
Avant la version 6.0 d’Android, toutes les permissions demandées par une application doivent être déclarées dans son manifeste. Lors de l’installation, l’utilisateur doit accepter la totalité des permissions demandées ou refuser l’installation. La version 6.0 introduit la possibilité de demander les permissions à l’exécution et de révoquer les permissions à la pièce si l’utilisateur le désire. Cela permet un contrôle plus granulaire sur le type de comportement qu’un utilisateur autorise à une application et permet de limiter les demandes de permissions jugées exagérées. Par exemple, un utilisateur pourrait souhaiter refuser l’accès à ses contacts et à la messagerie texte de son téléphone pour une application de réveil-matin.
SELinux
Tel qu’expliqué par Shabtai [105], Security Enhanced Linux (SELinux) est un module de sécurité Linux introduisant le concept de contrôle d’accès obligatoire par l’utilisation d’étiquettes de sécurité pour établir des règles d’accès visant à renforcer la sécurité d’Android et protéger les accès aux composantes du noyau. À l’origine présenté par Loscocco et Smalley [79] pour PC, SELinux fait officiellement partie d’Android depuis la version 4.3 parue en 2010. Il permet de surveiller et bloquer l’accès à différents objets accédés par un processus. Un objet correspond à une ressource du SE tel un fichier, un socket ou un périphérique. Ils sont classés par type de ressources requises selon le niveau d’accès requis (lecture, écriture, etc.) et ont une étiquette de sécurité qui leur est assignée. Le tableau 1.5 décrit les principales composantes d’une étiquette de sécurité. Ces différentes caractéristiques servent à décrire quel utilisateur peut utiliser quel processus pour poser quelle action lors de l’accès à une ressource.
L’étiquette de sécurité peut être exprimée à l’aide d’un contexte de sécurité, ce qui correspond à une chaîne de caractère prenant la forme présentée à la figure 1.1 décrivant l’étiquette de sécurité sous une forme lisible.
Utilisateur:Rôle:Type d’accès:Niveau de sécurité
Figure 1.1 – Contexte de sécurité.
L’approche de SELinux est le refus par défaut. Cela implique que tous les accès aux ressources désirés sur le système doivent avoir été préalablement autorisés par le biais de règles d’accès se trouvant dans un fichier de politiques de sécurité faute de quoi l’accès est automatiquement refusé et journalisé par le gestionnaire d’accès de SELinux. Puisque SELinux est une composante indépendante du noyau Linux, il est en mesure de contrôler les accès de tous les utilisateurs du système, incluant ceux de l’utilisateur root [79]. Cette technique permet de limiter les actions qui pourraient provenir d’une élévation de privilège, c’est-à-dire d’une attaque qui obtient un accès root à partir d’un contexte sans privilège en exploitant une faille de sécurité. En effet, si le changement d’utilisateur se produit à partir d’un processus qui n’a pas explicitement la permission de le faire en vertu des politiques de sécurité, les actions subséquentes seront bloquées par le gestionnaire d’accès de SELinux.
ASLR
La distribution aléatoire de l’espace d’adressage (Address Space Layout Randomization, ASLR) vise à rendre la topologie du contenu de la mémoire vive imprévisible pour un attaquant. Ce mécanisme de sécurité complique l’exploitation d’une faille de sécurité reposant sur un débordement de tampon (buffer overflow). En effet, il devient difficile pour un attaquant d’in-jecter des appels à des ressources (p.ex. fonction exploitable, bibliothèque logicielle, exécutable, position de la pile ou du tas, etc.) en mémoire vive si leur position est inconnue. La première apparition de l’ASLR sur Android a été appliquée à partir de la version 4.0. Il est possible de désactiver l’ASLR à la compilation du SE.
Résumé
Android est un système qui évolue rapidement afin d’offrir nouvelles fonctionnalités et sécurité accrue à ses utilisateurs. Toutefois, les concepteurs de maliciels s’adaptent continuellement aux nouvelles techniques déployées et il est essentiel de recourir à des techniques d’analyse de maliciels afin de découvrir les nouvelles menaces visant Android. Une revue du fonctionnement des maliciels sur Android ainsi que les différentes techniques existantes pour en faire l’analyse sur Android sont l’objet du chapitre 2.
Analyse et détection des maliciels
L’étude de l’analyse des maliciels sur Android nécessite de comprendre le fonctionnement de la plateforme (comme décrit au chapitre précédent) et des maliciels qui y résident. La section 2.1 vise à décrire les tendances recensées chez les maliciels Android. C’est avec cette connaissance qu’il est possible d’établir les cibles que doivent atteindre les techniques d’analyse de maliciels sur la plateforme afin d’être efficaces. La section 2.2 recense l’état de l’art des méthodes d’analyse statique ou dynamique des comportements malicieux sur Android. Elle décrit également les principales limitations des approches actuelles et, ce faisant, légitime l’exploration d’autres techniques d’analyse (p. ex. l’analyse de la mémoire vive) complémentaires à celles existantes.
Maliciels Android
Chaque SE présente des particularités qui régissent le modus operandi des maliciels qui y sont destinés. Android n’échappe pas à cette logique. Il est important pour comprendre les défis que tentent de résoudre les approches d’analyse de maliciels d’expliquer les principaux vecteurs d’attaques utilisés par ceux-ci. Cette section détaille les principales techniques utilisées par les maliciels Android pour compromettre en partie ou en totalité l’appareil d’un utilisateur. Dans un premier temps, les principales approches utilisées par les maliciels pour infecter la plateforme sont présentées. Dans un second temps, les techniques relatives à l’évasion de la détection sont détaillées.
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.
Exploitation d’une application légitime Ce type d’attaque a pour but d’exploiter la plateforme par le biais de vulnérabilités présentes dans une application légitime. Il nécessite une bonne connaissance du fonctionnement interne de l’application ciblée et des interfaces qu’elle offre. Cette connaissance est habituellement acquise par la rétro-ingénierie de cette dernière afin d’y trouver des erreurs logiques ou des communications intercomposantes (CIC) non protégées. L’impact dépend surtout des permissions détenues par l’application exploitée et du niveau de sensibilité des opérations dont elle est responsable. Les conséquences peuvent être une attaque par déni de service, l’exfiltration de données sensibles et même la prise de contrôle de l’application. Les impacts de ces maliciels peuvent être mineurs sur les capacités de l’appareil, mais être majeurs pour l’utilisateur. Par exemple, une attaque prenant le contrôle d’une application possédant uniquement la permission d’accès à Internet serait dangereuse s’il s’agit d’une application de gestion bancaire dont l’exploitation pourrait engendrer des pertes financières pour l’utilisateur. En revanche, dans cet exemple, la performance de l’appareil serait peu ou pas affectée. La principale limitation de ce type d’attaque est que le comportement malicieux est confiné au même environnement d’exécution et de permissions que l’application compromise, à moins d’être combinée à une élévation de privilèges. L’ajout du module de sécurité SELinux à Android contribue à limiter les répercussions de l’élévation de privilèges. En effet, tous processus Linux découlant d’une application Android sont confinés à un utilisateur qui n’est pas autorisé par SELinux à élever ses privilèges selon les politiques de sécurité.
Applications malicieuses Les applications malicieuses sont des applications Android à part entière qui peuvent être installées par l’utilisateur (p. ex. par hameçonnage) ou à son insu sur l’appareil (p. ex. prédéployées à l’achat [32]). Leur principale raison d’être est de porter atteinte à l’intégrité du SE et de son contenu comme les applications et les données privées de l’utilisateur. Selon l’étude de Zhou et Jiang [138] portant sur 1260 maliciels récoltés entre 2010 et 2011, près de 86% de cet échantillon découlent d’applications légitimes ayant été modifiées pour y inclure du contenu malicieux. Ces maliciels sont habituellement redistribués sur différents marchés d’applications Android et sont des calques de leur version légitime. De plus, près de 36,7% de cet échantillon utilise des mécanismes d’élévation de privilège. Leurs comportements varient selon l’intention du concepteur et peuvent inclure l’envoi de textos à des services payants, le chiffrement de données personnelles dans un but d’extorsion et le vol de données personnelles[68].
Techniques d’évasion de la détection
Les vecteurs d’attaques présentés ci-dessus permettent à un agent malicieux d’infecter la plateforme et d’y avoir un impact indésirable, voire même dommageable pour l’usager. Or, il n’est pas suffisant de pouvoir infecter un appareil pour les concepteurs de maliciel. En effet, pour être rentable ou utilisable pour des activités illicites, un maliciel doit avoir la possibilité d’y rester suffisamment longtemps pour y exercer le comportement malicieux désiré et, potentiellement, rentabiliser l’infection. De plus, le maliciel doit également être capable de se faire passer pour une application légitime au moment d’être soumis sur les marchés d’applications Android, faute de quoi il ne sera pas publié. Pour cette raison, les concepteurs de maliciels ont développé des techniques permettant d’éviter la détection et de masquer l’activité illicite des techniques d’analyse. Le rapport de Symantec [115] rapportent que les maliciels Android sont de plus en plus sophistiqués sur ce point. Plusieurs recherches ont étudié les capacités d’évasion de la détection des maliciels existants ou en ont proposé de nouvelles. Il est essentiel de les relever afin de mieux comprendre les limitations des techniques d’analyse aux différentes stratégies d’évasion. Les principales techniques d’évasion retrouvée sur Android sont présentées ci-dessous.
Obfuscation Wroblewski [127] définit l’obfuscation comme étant les transformations appli-quées au contenu d’une application visant à rendre la compréhension ou l’analyse de celle-ci plus complexe tout en préservant le comportement de l’application transformée équivalente à celle d’origine (c.-à-d. produisant le même résultat). L’obfuscation vise généralement à limiter les capacités de rétro-ingénierie d’une application en augmentant le temps, l’effort et le niveau de connaissances requis pour y parvenir. Elle est employée pour protéger la propriété intellec-tuelle d’une application ou encore pour limiter l’efficacité d’analyse des engins de détection de maliciels. Sur Android, des études se sont intéressés à l’injection d’instructions Dalvik permettant de masquer le flux d’exécution réelle d’une application aux décompilateurs tout en maintenant le comportement final de l’application. Les travaux de Kovacheva [71], Rastogi et al. [99], Schulz [104] démontrent qu’il est possible de limiter les capacités d’analyse d’une application tout en conservant le comportement désiré à l’exécution. Pour ce faire, ils utilisent l’injection d’instructions Dalvik nuisibles à la décompilation ou l’interprétation du code par des outils d’analyse automatisés .
Une autre approche utilisée sur Android consiste à appliquer l’obfuscation directement sur le code source de l’application, avant la compilation. Maiorca et al. [81] ont appliqué des techniques d’obfuscation sur des échantillons de maliciels connus. Leur étude vise à démontrer la robustesse des outils de détection de maliciels disponibles sur le marché. Ces résultats sont présentés au tableau 2.1. L’application de ces techniques a été démontrée efficace pour éviter la détection sur la plupart des outils d’analyses commerciaux retenus dans l’étude. Ces résultats soutiennent la pertinence de l’obfuscation en tant que technique d’évasion de la détection.
Peu d’études se sont intéressées à appliquer des techniques d’obfuscation pour les fonctions incluses dans les bibliothèques logicielles natives en C/C++ pouvant être utilisées par une application Android. En contrepartie, peu d’outils d’analyse s’y sont intéressés également.
Détection et évitement d’engin d’analyses Cette technique permet à un maliciel d’ex-primer ou d’inhiber son comportement malicieux en fonction de caractéristiques de son environnement d’exécution. Pour ce faire, il est possible de sonder différentes variables présentes sur Android pour identifier si certaines portent des valeurs typiques d’un environnement d’analyse. Petsas et al. [95], Vidas et Christin [122] font état d’un ensemble de propriétés pouvant être utilisées par les applications pour détecter si le système est émulé ou correspond à un appareil réel. Afin d’effectuer cette détection, les auteurs interrogent les propriétés du système, comparent la performance du système à des mesures étalons récoltées a priori et font l’inventaire du matériel et des logiciels présents sur la plateforme. Le tableau 2.2 rapporte des techniques employées par les auteurs pour effectuer cette détection.
Une autre approche, documentée dans l’étude de Fratantonio et al. [39], permet d’éviter la détection en ajoutant un segment de code appelé bombe logique. Ce code sert de déclencheur à du code malicieux lorsqu’une condition donnée est atteinte. Pour être efficace, ce déclencheur doit être un événement permettant de discriminer si le maliciel est en interaction avec une victime potentielle ou avec un système d’analyse automatisé. Ainsi, il peut être une tâche trop complexe pour qu’un système automatisé puisse la compléter (p. ex. compléter le premier niveau d’un jeu). Il peut également s’agir de l’introduction d’un délai avant l’exécution du comportement malicieux.
La détection de l’utilisateur peut aussi se faire par la détection d’un patron d’interactions correspondant à ce qui est attendu d’un appareil qui est dans les mains d’un utilisateur typique. Par exemple, Petsas et al. [95] ont démontré qu’il est possible de conclure qu’un environnement est simulé si les valeurs retournées par les capteurs de l’appareil sont prévisibles et constantes.
|
Table des matières
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