Un EDR sous Android ?

Magazine
Marque
MISC
Numéro
116
Mois de parution
juillet 2021
Spécialité(s)


Résumé

Les particularités du système Android ainsi que son modèle de sécurité peuvent limiter les fonctionnalités d'une solution de type EDR. Dans cet article, nous montrons ce qu'il serait possible de faire et les limites imposées par l'environnement Android.


Body

L’utilisation toujours croissante des environnements mobiles force les éditeurs de solutions d'EDR (Endpoint Detection and Response) à se positionner sur ce marché. Tout comme Linux a ses spécificités par rapport à Windows et macOS, les systèmes mobiles disposent également de leurs caractéristiques et le développement d'un EDR sous Android ne se fait pas de la même façon que sous Linux (bien que les deux systèmes partagent une base commune). Dans cet article, nous aborderons la question des EDR sous Android du point de vue d’un développeur : « Comment s’y prendrait-on pour développer un EDR sous Android ? ». Pour répondre à cette question, nous aborderons dans une première partie la structure de l'environnement Android et de ses applications, nous verrons ensuite les éléments du système qu’il peut être intéressant de surveiller et nous terminerons par les mécanismes qui permettent de cacher ou de rendre difficile la détection d'un comportement malveillant.

1. L’environnement Android

Afin de mieux comprendre les possibilités de développement d’un EDR sous Android, revenons sur la structure du système Android et ses particularités de manière à mieux comprendre les limites par rapport à un système d’exploitation plus classique.

Android est développé sur la base d’un Kernel Linux, sur laquelle vient s’ajouter une couche système (userland) propre à Android. Dans cette couche, on y trouve une libc propre à Android (Bionic), le runtime pour exécuter les applications (ART) et l'ensemble du framework Android disponible aux développeurs [1].

android stack-s

Fig. 1 : Les différentes couches du système Android – https://developer.android.com/.

Au-dessus de cette couche système viennent se placer les applications Android qui sont la partie visible et modulaire du système. Les applications sont principalement développées en Java ou en Kotlin avec la possibilité d’intégrer du code natif via la JNI (Java Natif Interface).

Par rapport à un système d’exploitation comme Linux, il est important de mentionner qu’Android ne permet pas à une application de s’exécuter en tant que root. On va retrouver des notions d’applications « admin » avec des privilèges étendus, mais il n’est pas possible, par exemple, d’avoir un accès complet au système de fichiers. De même, et contrairement à Windows, il n’est pas possible d’installer des drivers Kernel.

Outre les applications, l'utilisateur n'a pas la possibilité de supprimer ou d'installer les composants du système.Les constructeurs et les fournisseurs de firmwares Android ont en revanche la possibilité d'ajouter ces composants bas niveaux.

Les capacités d'un EDR sous Android se limitent donc aux capacités applicatives et à l’API exposée par le framework Android.

2. Les éléments caractéristiques du système à surveiller

Pour rappel, un EDR a pour objectif de collecter un certain nombre d'informations de manière à pouvoir détecter un comportement anormal. Se pose donc la question de la nature des données qui peuvent être collectées.

Comme nous l'avons expliqué dans la partie précédente, un EDR sous Android est principalement limité par l'API exposée par le système Android et n'a donc pas une visibilité complète de ce qui est fait dessus.

À titre de comparaison, un EDR pour un système type Windows pourrait effectuer des analyses sur le trafic réseau là où sous Android, et pour des raisons de sécurité, ce type d'analyse n'est pas possible sauf en mettant en place un VPN sur le téléphone.

Étant donné les limites de l'API Android et de ce qui peut être rajouté par l'utilisateur, la principale source de comportements malveillants reste les applications installées sur le téléphone. Un EDR sous Android se rapprocherait donc plus d'un antivirus classique que d'une réelle solution capable de surveiller plusieurs éléments du système.

Au niveau applicatif, on peut distinguer deux catégories d'applications : celles installées par l'utilisateur via un store et celles préinstallées sur le système. Les applications préinstallées ont pour particularité de ne pas pouvoir être supprimées sauf via une mise à jour complète du système. Elles peuvent également demander des permissions plus critiques et ont accès à certaines API privilégiées. Comme le montrent certains articles parus récemment [2], ces applications peuvent contenir du code malveillant qui peut être plus compliqué à supprimer par rapport à la désinstallation d'une application classique.

Jusqu’à Android 11, les développeurs peuvent accéder à la liste des applications installées et au fichier '.apk' de l’application assez facilement en utilisant l'interface PackageManager [3][4]. À titre de comparaison, sur iOS, le code des applications est chiffré et il n’est pas possible pour une application tierce d'accéder au fichier d'installation des autres applications.

Dans le cas d'Android, un EDR peut donc analyser l'ensemble des applications qui sont présentes sur le téléphone.

3. Analyse des applications Android

Puisque la principale source d'analyse pour un EDR reste les applications Android installées sur le système, regardons leur structure et les éléments qui permettraient de détecter un comportement malveillant ou non désiré.

Tout d'abord, une application Android se matérialise par un fichier APK (Android Package Kit) qui est l'équivalent d'un setup.exe sous Windows ou d'un IPA sous iOS. Ces fichiers APK sont en réalité des fichiers zip dans lesquels on y trouve le code de l'application, ses dépendances (bibliothèques natives) ainsi que ses ressources (images, icônes, etc.) et des metadatas (nom du package, permissions, etc.).

Nous allons donc aborder les différents points sur lesquels un EDR doit porter son attention lors de l'analyse de l'APK.

3.1 La signature

Les paquets Android contiennent une signature qui garantit l’authenticité de la source (l’éditeur de l’application) ainsi que l’intégrité du contenu de l’archive. Ce point doit être passé en revue lors de l’analyse faite par un EDR afin de vérifier que l’application provient d’une source légitime et que le schéma de signature utilisé garantit l’intégrité de la totalité des fichiers présents dans l’archive (afin de prévenir toutes modifications ultérieures). Depuis la création d’Android, le mécanisme de signature a évolué et il existe aujourd’hui quatre versions de ce mécanisme. La première évolution avait pour objectif de résoudre une vulnérabilité critique [5] tandis que les autres versions sont des améliorations fonctionnelles.

Il est donc important de vérifier que les applications ne sont pas uniquement signées avec la version 1 dumécanisme de signature.

Un EDR doit également porter une attention particulière aux champs CN : Common Name et O : Organization du certificat utilisé pour générer la signature de manière à pouvoir identifier et vérifier le propriétaire ainsi que l'autorité de délivrance.

Certaines applications malveillantes peuvent utiliser les noms de sociétés légitimes dans les champs Common Name et Organization pour ne pas attirer l'attention.

3.2 Le manifeste Android

Les APK contiennent un fichier nommé AndroidManifest.xml stocké à la racine de l’archive. Contrairement à ce que l’extension pourrait suggérer, il ne s’agit pas d’un fichier au format XML, mais d’un format spécifique aux applications Android : Android Binary XML. Le manifeste Android d’une application est en quelque sorte sa carte d'identité et contient des informations très intéressantes pour l’analyse. On y trouve notamment :

  • les permissions requises par l’application ;
  • une liste de certains composants applicatifs présents dans l’application.

Pour un EDR, le manifeste Android permet de dresser rapidement le profil d'une application.

Le premier point d'analyse qui peut révéler la présence potentielle d'une application malveillante est la liste des permissions requises par cette dernière [6]. Cette liste permet de faire un état des lieux rapide des capacités d'une application :

  • Est-ce que l’application peut accéder à l'interface Bluetooth ?
  • Est-ce qu’elle peut lire le carnet de contacts de l'utilisateur ? etc.

Un trop grand nombre de permissions demandées par une application peut être révélateur d’un comportement suspect.

De plus, en rapprochant les permissions et la catégorie de l’application, il est possible de révéler des incohérences qui peuvent laisser présager un comportement malveillant. Par exemple, pourquoi une application de type calculatrice scientifique demanderait la permission d’envoyer des SMS ?

L'analyse du manifeste devrait ensuite s'orienter sur les différents composants applicatifs déclarés par l'application. Dans le cadre d'une analyse effectuée par un EDR, deux types de composants peuvent faire l'objet d'une attention particulière :

  • les services [7] ;
  • les broadcast receivers [8].

Les services sont des composants qui s'exécutent en tâche de fond et qui peuvent être totalement transparents du point de vue de l’utilisateur. Comme l’exécution de ce type de composant ne requiert pas l'affichage d'une interface graphique, ils sont donc particulièrement adaptés à l'exécution silencieuse (du point de vue de l'utilisateur) de code malveillant.

Les broadcast receivers sont quant à eux des composants à l’écoute de certains messages délivrés par différents acteurs du système :

  • un autre composant de la même application ;
  • une application différente ;
  • le système lui-même.

Contrairement aux autres composants applicatifs, les broadcasts receiver n’apparaissent pas forcément dans le manifeste de l’application, car ces derniers ont la possibilité d’être enregistrés dynamiquement (à l’exécution). À la réception d’un message, le receiver exécute un traitement particulier.

Il est par exemple possible de créer un broadcast receiver qui exécutera sa fonctionnalité lors de la réception d'un SMS en écoutant les messages de type android.provider.Telephony.SMS_RECEIVED ou encore lors d'un changement de connectivité Wi-Fi via les messages android.net.wifi.WifiManager.NETWORK_STATE_CHANGED_ACTION, etc.

Les broadcast receivers peuvent donc être propices à l'exécution de code malveillant, car ils constituent des points d’entrée alternatifs qui échappent à une analyse naïve du manifeste.

3.3 Le bytecode Dalvik

Au moment de la génération de l'application, le code Java ou Kotlin d'une application est transformé dans un bytecode propre à Android appelé Dalvik.

L'ensemble des fonctionnalités Java/Kotlin de l'application d'origine est donc représenté sous la forme de ce bytecode qui utilise un jeu d'instruction spécifique. Au regard de notre EDR, ce bytecode est particulièrement intéressant puisqu’il sert de base d'analyse pour détecter du code malveillant ou non désiré dans le code source d'origine.

Au niveau de l'APK, ce bytecode Dalvik est enveloppé dans un ou plusieurs fichiers DEX (classes.dex, classes2.dex, etc.) qui se trouvent à la racine du fichier APK.

La structure du bytecode et le format DEX dans lequel les instructions Dalvik sont enveloppées ont des propriétés intéressantes pour effectuer des analyses statiques.

Pour commencer, le bytecode Dalvik utilise 256 instructions [9] dont la sémantique est assez explicite pour faire de la décompilation. Sans aller jusqu’à l'étape de décompilation, prenons l'exemple d'une instruction qui appelle une méthode Java :

invoke-virtual {v0, v1}, #12

La première partie {v0, v1} contient la liste des registres de la JVM (Java Virtual Machine) associés aux paramètres de la méthode. La deuxième partie, #12, représente un index dans un tableau qui liste toutes les méthodes de l'application. Ce tableau fait partie du fichier DEX [10] et contient un certain nombre d'informations sur les méthodes de l'application. En particulier, on peut accéder au prototype de la méthode ainsi qu'à sa classe d'origine.

À l'image du tableau de méthodes référencé par les instructions, l'ensemble des chaînes de caractères de l'application est également représenté sous la forme d'un tableau. On peut donc rapidement analyser ses valeurs sans avoir à désassembler le code. Dans les analyses possibles au sein de notre EDR, nous pourrions vérifier la liste des chaînes de caractères qui commencent par http(s):// ou bien détecter la présence de chaînes caractéristiques d'un malware déjà identifié (IOC).

bytecode dalvik-s

Fig. 2 : Structure d’une instruction Dalvik.

De manière générale, la structure du format DEX et du bytecode conserve un certain nombre de relations et de propriétés du code d’origine.

Ces structures, et en particulier l'indexation de tous les composants du code d’origine (classes, méthodes, attributs de classes, etc.), permettent de facilement générer (par rapport à du code assembleur) un graphe d’appel ou de déterminer l'ensemble des méthodes qui font appel à un certain package Java, une certaine classe ou à une certaine méthode (cross-referencing).

Un EDR pourrait donc précisément et efficacement déterminer si le code d'une application fait appel à des fonctions sensibles et si oui, dans quelles parties du code.

3.4 SDK externes

Il existe une multitude de SDK (Software Development Kit) sur le marché du développement d'applications Android. Ces derniers permettent aux développeurs d'embarquer diverses fonctionnalités au sein d'une application Android.

Cela va du SDK proposant d'intégrer facilement des encarts publicitaires dans une application (AdMob par exemple), au SDK permettant de mesurer l'audience d'une application (comme AppsFlyer) en passant par le SDK d'authentification (Facebook Login par exemple).

Les SDK sont intégrés dans l'application au moment de la génération de l'APK. Les développeurs ont accès à une certaine interface, mais jamais (ou en tout cas très rarement) au code source du SDK.

Il n'existe pas de différences strictes entre le code principal de l'application et ses dépendances. Le code d'une dépendance se situe au même niveau, d'un point de vue fonctionnel, que celui de l'éditeur. Si l'on compare cette intégration par rapport aux exécutables classiques, c'est comme si l'on compilait statiquement toutes les bibliothèques externes. Les SDK tiers peuvent donc profiter de l'ensemble des privilèges de l'application pour effectuer des actions malveillantes [11].

L'étude des SDK externes embarqués par une application a donc son importance du point de vue de l'EDR, car elle peut révéler la présence de composants susceptibles de récolter les données de l'utilisateur ou du système [12].

L'analyse des SDK externes est relativement peu coûteuse, car il s'agit la plupart du temps de vérifier simplement la présence d'une classe ou d'un package dans le bytecode. De plus et contrairement au code principal de l'application, les packages et les noms des classes des SDK externes sont très rarement obfusqués, ce qui rend cette identification possible.

Finalement, la détection des SDK peut permettre à l'EDR d'établir rapidement certaines capacités d'une application en complément de ce qui a été obtenu à partir du manifeste.

3.5 Bibliothèques natives & JNI

Pour diverses raisons, la principale étant le besoin de performances, une partie de la logique applicative peut être déportée dans des bibliothèques natives. Ces bibliothèques sont compilées dans le langage machine correspondant à l’architecture matérielle sur laquelle l’application s'exécute (généralement ARM ou AArch64). Un APK peut embarquer des bibliothèques natives compilées pour différentes architectures afin d’assurer la portabilité de l’application. Le code présent dans ces bibliothèques est appelé par la partie bytecode via la JNI (Java Native Interface).

L’analyse statique de binaires natifs peut se révéler plus délicate que celle du bytecode. Concrètement, une bibliothèque compilée en natif ne contient pas de structures équivalentes à celles du bytecode Dalvik telles que l'index des chaînes de caractères ou l'index identifiant clairement toutes les fonctions présentes dans la bibliothèque.

Par exemple, pour établir un graphe d'appels, il faut s'appuyer sur les adresses des fonctions dans le binaire, adresses qui ne sont pas forcément identifiables lors de l'analyse statique d'un appel de fonction.

De plus, là où le bytecode Dalvik fournit les noms de méthodes, de classes et d'attributs permettant d'identifier clairement un appel à une API, le binaire natif ne fournit pas d'équivalent si ce n'est les symboles qui ne sont pas toujours présents.

Cependant, une analyse par signature des bibliothèques natives est une piste intéressante pour identifier la présence d’un code malveillant.

3.6 Les ressources

Une archive APK embarque une arborescence dédiée aux ressources de toutes sortes :

  • images utilisées dans l’application ;
  • fichiers de chaînes de caractères (pour l’internationalisation d’une application par exemple) ;
  • polices de caractères.

L’analyse des ressources n’est pas à négliger, car elle peut révéler certaines chaînes de caractères significatives, ou encore la présence de fichiers contenant du code malveillant qui pourraient être chargés dynamiquement.

4. Protections & limites

Comme l'ont montré les précédentes parties, la principale source de comportements malveillants se situe dans les applications installées sur le téléphone. Pour contourner les analyses et les heuristiques des EDR, les applications malveillantes vont avoir tendance à se protéger là où c'est possible. En effet, certaines parties du code de l'application peuvent être protégées par des packers ou des obfuscateurs.

Parmi ces techniques, on trouve l'utilisation de la réflexion Java qui consiste à appeler une fonction dynamiquement :

Class<?> cls = Class.forName("com.android.TextUtils");
Method method = cls.getMethod("getText");
method.invoke(obj);
// Équivalent à :
obj.getText();

Couplée avec de l'encodage ou du chiffrement de chaînes de caractères, cette protection peut mettre à mal certaines heuristiques d'analyse statique. Comme mentionné dans la première partie de cet article, le modèle de programmation Java permet de définir des méthodes dont l'implémentation se fait en C ou en C++ via la JNI. Dans la partie précédente, nous avons vu que la structure du bytecode Dalvik rendait l'analyse statique assez fiable et efficace en partie grâce au fait que les éléments du langage (classes, méthodes, attributs, etc.) sont indexés dans des tableaux. En revanche, l'analyse statique du code natif est beaucoup plus complexe et plus gourmande en ressources. Le code assembleur est beaucoup plus compliqué à désassembler que le Bytecode Dalvik sans parler du fait que le mode Thumb/Thumb2 du jeu d'instruction ARM rend la tâche encore plus difficile.

Une application malveillante peut donc assez facilement contourner un certain nombre d'heuristiques d'analyses en développant ses fonctionnalités malveillantes dans la partie native. Enfin, à l'image du dlopen sous Linux et OSX ou du LoadLibrary sous Windows, Android permet le chargement dynamique de fichiers DEX en utilisant l'interface exposée par la classe DexClassLoader.

Cette interface permet donc de déporter une partie des fonctionnalités dans des fichiers autres que ceux présents dans le fichier d'installation. Généralement, le code malveillant va récupérer un fichier sur un serveur qu'il contrôle ou le placer dans les ressources de l'application avec un encodage ou une couche de chiffrement.

Les EDR qui ciblent des environnements mobiles doivent également prendre en compte qu'ils s'exécutent sur un système embarqué avec des ressources limitées. Même si les processeurs des téléphones et la mémoire sont de plus en plus performants et efficaces, les EDR ne peuvent pas utiliser certaines techniques d'analyse telles que l'émulation, le sandboxing ou l'exécution d'algorithmes statiques coûteux en ressources. L'utilisation de telles techniques risquerait de ralentir le téléphone ou de vider la batterie de manière anormale. On pourrait donc se dire, puisqu’on ne peut pas faire ces analyses directement sur le téléphone, faisons-les sur des serveurs avec les données envoyées par le téléphone. Là encore, il faut prendre en compte que les utilisateurs peuvent avoir un réseau instable et ne veulent pas forcément voir leur forfait 4G épuisé à cause d'un EDR un peu trop gourmand.

Comme dans la plupart des problèmes, la solution consiste à trouver un juste milieu qui pourrait être d'envoyer seulement des applications dont les heuristiques statiques trouvent qu'il est pertinent d'envoyer l'application ou la donnée pour une analyse plus poussée au niveau des serveurs.

Conclusion

L'environnement Android permet de développer des solutions d'analyses du type EDR, mais de par l'API exposée par Android et des contraintes d'un système embarqué, ses fonctionnalités se rapprochent plus de celle d'un antivirus. Néanmoins, la principale surface d'attaque sur le système Android reste la partie applicative. La structure de ces applications et les metadatas associées permettent généralement d'effectuer des analyses plus fines et plus précises que sur des exécutables classiques.

Références

[1] Ce framework comprend les classes Java qui permettent d'interagir avec le téléphone et le système Android.

[2] https://blog.malwarebytes.com/android/2021/04/pre-installed-auto-installer-threat-found-on-android-mobile-devices-in-germany

[3] https://developer.android.com/reference/android/content/pm/PackageManager

[4] https://developer.android.com/reference/android/content/pm/ApplicationInfo#sourceDir

[5] https://www.guardsquare.com/blog/new-android-vulnerability-allows-attackers-to-modify-apps-without-affecting-their-signatures-guardsquare

[6] https://developer.android.com/training/permissions/declaring

[7] https://developer.android.com/guide/components/services

[8] https://developer.android.com/guide/components/broadcasts

[9] https://source.android.com/devices/tech/dalvik/dalvik-bytecode#instructions

[10] https://source.android.com/devices/tech/dalvik/dex-format#method-id-item

[11] https://www.bitsight.com/blog/fraudulent-android-advertising-sdk-installed-in-over-15-million-devices

[12] https://reports.exodus-privacy.eu.org/en/trackers/



Article rédigé par

Les derniers articles Premiums

Les derniers articles Premium

Stubby : protection de votre vie privée via le chiffrement des requêtes DNS

Magazine
Marque
Contenu Premium
Spécialité(s)
Résumé

Depuis les révélations d’Edward Snowden sur l’espionnage de masse des communications sur Internet par la NSA, un effort massif a été fait pour protéger la vie en ligne des internautes. Cet effort s’est principalement concentré sur les outils de communication avec la généralisation de l’usage du chiffrement sur le web (désormais, plus de 90 % des échanges se font en HTTPS) et l’adoption en masse des messageries utilisant des protocoles de chiffrement de bout en bout. Cependant, toutes ces communications, bien que chiffrées, utilisent un protocole qui, lui, n’est pas chiffré par défaut, loin de là : le DNS. Voyons ensemble quels sont les risques que cela induit pour les internautes et comment nous pouvons améliorer la situation.

Surveillez la consommation énergétique de votre code

Magazine
Marque
Contenu Premium
Spécialité(s)
Résumé

Être en mesure de surveiller la consommation énergétique de nos applications est une idée attrayante, qui n'est que trop souvent mise à la marge aujourd'hui. C'est d'ailleurs paradoxal, quand on pense que de plus en plus de voitures permettent de connaître la consommation instantanée et la consommation moyenne du véhicule, mais que nos chers ordinateurs, fleurons de la technologie, ne le permettent pas pour nos applications... Mais c'est aussi une tendance qui s'affirme petit à petit et à laquelle à terme, il devrait être difficile d'échapper. Car même si ce n'est qu'un effet de bord, elle nous amène à créer des programmes plus efficaces, qui sont également moins chers à exécuter.

Donnez une autre dimension à vos logs avec Vector

Magazine
Marque
Contenu Premium
Spécialité(s)
Résumé

Avoir des informations précises et détaillées sur ce qu’il se passe dans une infrastructure, et sur les applications qu'elle héberge est un enjeu critique pour votre business. Cependant, ça demande du temps, temps qu'on préfère parfois se réserver pour d'autres tâches jugées plus prioritaires. Mais qu'un système plante, qu'une application perde les pédales ou qu'une faille de sécurité soit découverte et c'est la panique à bord ! Alors je vous le demande, qui voudrait rester aveugle quand l'observabilité a tout à vous offrir ?

Les listes de lecture

11 article(s) - ajoutée le 01/07/2020
Clé de voûte d'une infrastructure Windows, Active Directory est l'une des cibles les plus appréciées des attaquants. Les articles regroupés dans cette liste vous permettront de découvrir l'état de la menace, les attaques et, bien sûr, les contre-mesures.
8 article(s) - ajoutée le 13/10/2020
Découvrez les méthodologies d'analyse de la sécurité des terminaux mobiles au travers d'exemples concrets sur Android et iOS.
10 article(s) - ajoutée le 13/10/2020
Vous retrouverez ici un ensemble d'articles sur les usages contemporains de la cryptographie (whitebox, courbes elliptiques, embarqué, post-quantique), qu'il s'agisse de rechercher des vulnérabilités ou simplement comprendre les fondamentaux du domaine.
Voir les 62 listes de lecture

Abonnez-vous maintenant

et profitez de tous les contenus en illimité

Je découvre les offres

Déjà abonné ? Connectez-vous