Android Applications Basics
Last updated
Last updated
Groupe de Sécurité Try Hard
Il y a deux couches :
Le système d'exploitation (OS), qui maintient les applications installées isolées les unes des autres.
L'application elle-même, qui permet aux développeurs de exposer certaines fonctionnalités et de configurer les capacités de l'application.
Chaque application se voit attribuer un ID utilisateur spécifique. Cela est fait lors de l'installation de l'application afin que l'application puisse interagir uniquement avec les fichiers appartenant à son ID utilisateur ou des fichiers partagés. Par conséquent, seule l'application elle-même, certains composants du système d'exploitation et l'utilisateur root peuvent accéder aux données des applications.
Deux applications peuvent être configurées pour utiliser le même UID. Cela peut être utile pour partager des informations, mais si l'une d'elles est compromise, les données des deux applications seront compromises. C'est pourquoi ce comportement est déconseillé.
Pour partager le même UID, les applications doivent définir la même valeur android:sharedUserId
dans leurs manifestes.
Le bac à sable des applications Android permet d'exécuter chaque application en tant que processus séparé sous un ID utilisateur distinct. Chaque processus a sa propre machine virtuelle, de sorte que le code d'une application s'exécute de manière isolée par rapport aux autres applications. À partir d'Android 5.0 (L), SELinux est appliqué. Fondamentalement, SELinux a refusé toutes les interactions entre les processus, puis a créé des politiques pour autoriser uniquement les interactions attendues entre eux.
Lorsque vous installez une application et qu'elle demande des autorisations, l'application demande les autorisations configurées dans les éléments uses-permission
dans le fichier AndroidManifest.xml. L'élément uses-permission indique le nom de l'autorisation demandée dans l'attribut name. Il a également l'attribut maxSdkVersion qui arrête de demander des autorisations sur des versions supérieures à celle spécifiée.
Notez que les applications Android n'ont pas besoin de demander toutes les autorisations au début, elles peuvent également demander des autorisations dynamiquement mais toutes les autorisations doivent être déclarées dans le manifeste.
Lorsqu'une application expose des fonctionnalités, elle peut limiter l'accès uniquement aux applications ayant une autorisation spécifiée. Un élément d'autorisation a trois attributs :
Le nom de l'autorisation
L'attribut permission-group, qui permet de regrouper les autorisations connexes.
Le niveau de protection qui indique comment les autorisations sont accordées. Il existe quatre types :
Normal : Utilisé lorsqu'il n'y a aucune menace connue pour l'application. L'utilisateur n'est pas tenu d'approuver.
Dangereux : Indique que l'autorisation accorde à l'application demandant un accès élevé. Les utilisateurs sont invités à les approuver.
Signature : Seules les applications signées par le même certificat que celui exportant le composant peuvent se voir accorder l'autorisation. Il s'agit du type de protection le plus fort.
SignatureOrSystem : Seules les applications signées par le même certificat que celui exportant le composant ou les applications fonctionnant avec un accès de niveau système peuvent se voir accorder des autorisations.
Ces applications se trouvent généralement dans les répertoires /system/app
ou /system/priv-app
et certaines d'entre elles sont optimisées (vous ne trouverez peut-être même pas le fichier classes.dex
). Ces applications valent la peine d'être vérifiées car parfois elles fonctionnent avec trop de permissions (en tant que root).
Celles livrées avec le ROM AOSP (Android OpenSource Project)
Ajoutées par le fabricant de l'appareil
Ajoutées par le fournisseur de téléphonie mobile (si acheté chez eux)
Pour obtenir un accès root sur un appareil Android physique, vous devez généralement exploiter 1 ou 2 vulnérabilités qui sont généralement spécifiques au dispositif et à la version.
Une fois l'exploit réussi, généralement le binaire Linux su
est copié dans un emplacement spécifié dans la variable d'environnement PATH de l'utilisateur comme /system/xbin
.
Une fois le binaire su configuré, une autre application Android est utilisée pour interagir avec le binaire su
et traiter les demandes d'accès root comme Superuser et SuperSU (disponibles dans le Google Play Store).
Notez que le processus de rootage est très dangereux et peut endommager gravement l'appareil
Il est possible de remplacer le système d'exploitation en installant un firmware personnalisé. En faisant cela, il est possible d'étendre l'utilité d'un ancien appareil, contourner les restrictions logicielles ou accéder au dernier code Android. OmniROM et LineageOS sont deux des firmwares les plus populaires à utiliser.
Notez que il n'est pas toujours nécessaire de rooter l'appareil pour installer un firmware personnalisé. Certains fabricants permettent le déverrouillage de leurs chargeurs d'amorçage de manière bien documentée et sécurisée.
Une fois qu'un appareil est rooté, toute application pourrait demander un accès en tant que root. Si une application malveillante l'obtient, elle pourra accéder à presque tout et endommager le téléphone.
Le format des applications Android est appelé format de fichier APK. Il s'agit essentiellement d'un fichier ZIP (en renommant l'extension de fichier en .zip, le contenu peut être extrait et visualisé).
Contenu de l'APK (non exhaustif)
AndroidManifest.xml
resources.arsc/strings.xml
resources.arsc : contient des ressources précompilées, comme du XML binaire.
res/xml/files_paths.xml
META-INF/
C'est là que se trouve le Certificat !
classes.dex
Contient le bytecode Dalvik, représentant le code Java (ou Kotlin) compilé que l'application exécute par défaut.
lib/
Contient des bibliothèques natives, séparées par architecture CPU dans des sous-répertoires.
armeabi
: code pour les processeurs basés sur ARM
armeabi-v7a
: code pour les processeurs basés sur ARMv7 et supérieurs
x86
: code pour les processeurs X86
mips
: code pour les processeurs MIPS uniquement
assets/
Stocke des fichiers divers nécessaires à l'application, potentiellement y compris des bibliothèques natives supplémentaires ou des fichiers DEX, parfois utilisés par les auteurs de logiciels malveillants pour dissimuler du code supplémentaire.
res/
Contient des ressources qui ne sont pas compilées dans resources.arsc
En développement Android, Java ou Kotlin est utilisé pour créer des applications. Au lieu d'utiliser le JVM comme dans les applications de bureau, Android compile ce code en code bytecode Dalvik exécutable (DEX). Auparavant, la machine virtuelle Dalvik gérait ce bytecode, mais maintenant, l'Android Runtime (ART) prend le relais dans les nouvelles versions d'Android.
Pour l'ingénierie inverse, Smali devient crucial. C'est la version lisible par l'homme du bytecode DEX, agissant comme un langage d'assemblage en traduisant le code source en instructions bytecode. Smali et baksmali font référence aux outils d'assemblage et de désassemblage dans ce contexte.
Les Intentions sont le principal moyen par lequel les applications Android communiquent entre leurs composants ou avec d'autres applications. Ces objets de message peuvent également transporter des données entre les applications ou les composants, de manière similaire à l'utilisation des requêtes GET/POST dans les communications HTTP.
Ainsi, une Intention est essentiellement un message qui est transmis entre les composants. Les Intentions peuvent être dirigées vers des composants ou des applications spécifiques, ou peuvent être envoyées sans destinataire spécifique. Pour simplifier, une Intention peut être utilisée pour :
Démarrer une Activité, ouvrir généralement une interface utilisateur pour une application
Comme diffusions pour informer le système et les applications des changements
Pour démarrer, arrêter et communiquer avec un service en arrière-plan
Accéder aux données via des ContentProviders
Comme rappels pour gérer des événements
Si elles sont vulnérables, les Intentions peuvent être utilisées pour effectuer diverses attaques.
Les Filtres d'Intention définissent comment une activité, un service ou un récepteur de diffusion peuvent interagir avec différents types d'Intentions. Essentiellement, ils décrivent les capacités de ces composants, comme les actions qu'ils peuvent effectuer ou les types de diffusions qu'ils peuvent traiter. Le principal endroit pour déclarer ces filtres est dans le fichier AndroidManifest.xml, bien que pour les récepteurs de diffusion, les coder soit également une option.
Les Filtres d'Intention sont composés de catégories, d'actions et de filtres de données, avec la possibilité d'inclure des métadonnées supplémentaires. Cette configuration permet aux composants de gérer des Intentions spécifiques qui correspondent aux critères déclarés.
Un aspect critique des composants Android (activités/services/fournisseurs de contenu/récepteurs de diffusion) est leur visibilité ou leur statut public. Un composant est considéré comme public et peut interagir avec d'autres applications s'il est exporté
avec une valeur de true
ou si un Filtre d'Intention est déclaré pour lui dans le manifeste. Cependant, les développeurs ont la possibilité de garder explicitement ces composants privés, garantissant qu'ils n'interagissent pas avec d'autres applications de manière involontaire. Cela est réalisé en définissant l'attribut exported
sur false
dans leurs définitions de manifeste.
De plus, les développeurs ont la possibilité de sécuriser davantage l'accès à ces composants en exigeant des autorisations spécifiques. L'attribut permission
peut être défini pour imposer que seules les applications disposant de l'autorisation désignée puissent accéder au composant, ajoutant une couche supplémentaire de sécurité et de contrôle sur qui peut interagir avec lui.
Les intentions sont créées de manière programmatique en utilisant un constructeur Intent :
L'Action de l'intent précédemment déclaré est ACTION_SEND et l'Extra est un Uri mailto (l'Extra est l'information supplémentaire que l'intent attend).
Cet intent doit être déclaré dans le manifeste comme dans l'exemple suivant:
Un intent-filter doit correspondre à l'action, aux données et à la catégorie pour recevoir un message.
Le processus de "résolution de l'intent" détermine quelle application doit recevoir chaque message. Ce processus prend en compte l'attribut de priorité, qui peut être défini dans la déclaration de l'intent-filter, et celui avec la priorité la plus élevée sera sélectionné. Cette priorité peut être définie entre -1000 et 1000 et les applications peuvent utiliser la valeur SYSTEM_HIGH_PRIORITY
. En cas de conflit, une fenêtre "choisir" apparaît pour que l'utilisateur puisse décider.
Un intent explicite spécifie le nom de la classe qu'il cible:
Dans d'autres applications, pour accéder à l'intention précédemment déclarée, vous pouvez utiliser :
Ces intentions permettent à d'autres applications de prendre des actions au nom de votre application, en utilisant l'identité et les autorisations de votre application. Pour construire une Intention en attente, il faut spécifier une intention et l'action à effectuer. Si l'intention déclarée n'est pas explicite (ne déclare pas quelle intention peut l'appeler), une application malveillante pourrait effectuer l'action déclarée au nom de l'application victime. De plus, si une action n'est pas spécifiée, l'application malveillante pourra effectuer n'importe quelle action au nom de la victime.
Contrairement aux intentions précédentes, qui ne sont reçues que par une seule application, les intentions de diffusion peuvent être reçues par plusieurs applications. Cependant, à partir de la version API 14, il est possible de spécifier l'application qui doit recevoir le message en utilisant Intent.setPackage.
Il est également possible de spécifier une autorisation lors de l'envoi de la diffusion. L'application réceptrice devra avoir cette autorisation.
Il existe deux types de diffusions : Normale (asynchrone) et Ordonnée (synchrone). L'ordre est basé sur la priorité configurée dans l'élément récepteur. Chaque application peut traiter, relayer ou abandonner la diffusion.
Il est possible d'envoyer une diffusion en utilisant la fonction sendBroadcast(intent, receiverPermission)
de la classe Context
.
Vous pouvez également utiliser la fonction sendBroadcast
du LocalBroadcastManager
pour garantir que le message ne quitte jamais l'application. En utilisant cela, vous n'aurez même pas besoin d'exporter un composant récepteur.
Ce type de diffusions peut être accédé longtemps après leur envoi. Ils ont été obsolètes à partir du niveau API 21 et il est recommandé de ne pas les utiliser. Ils permettent à n'importe quelle application de renifler les données, mais aussi de les modifier.
Si vous trouvez des fonctions contenant le mot "persistant" comme sendStickyBroadcast
ou sendStickyBroadcastAsUser
, vérifiez l'impact et essayez de les supprimer.
Dans les applications Android, les liens profonds sont utilisés pour initier une action (Intention) directement via une URL. Cela se fait en déclarant un schéma d'URL spécifique au sein d'une activité. Lorsqu'un appareil Android tente d'accéder à une URL avec ce schéma, l'activité spécifiée dans l'application est lancée.
Le schéma doit être déclaré dans le fichier AndroidManifest.xml
:
Le schéma de l'exemple précédent est exampleapp://
(notez également la catégorie BROWSABLE
)
Ensuite, dans le champ des données, vous pouvez spécifier l'hôte et le chemin :
Pour y accéder depuis un site web, il est possible de définir un lien comme suit :
Pour trouver le code qui sera exécuté dans l'application, allez à l'activité appelée par le lien profond et recherchez la fonction onNewIntent
.
Apprenez comment appeler des liens profonds sans utiliser de pages HTML.
Le Langage de Définition d'Interface Android (AIDL) est conçu pour faciliter la communication entre le client et le service dans les applications Android via la communication interprocessus (IPC). Étant donné qu'il n'est pas permis d'accéder directement à la mémoire d'un autre processus sur Android, AIDL simplifie le processus en marshalling des objets dans un format compris par le système d'exploitation, facilitant ainsi la communication entre différents processus.
Services liés: Ces services utilisent AIDL pour l'IPC, permettant aux activités ou composants de se lier à un service, de faire des requêtes et de recevoir des réponses. La méthode onBind
dans la classe du service est essentielle pour initier l'interaction, en en faisant une zone vitale pour l'examen de la sécurité à la recherche de vulnérabilités.
Messager: Agissant en tant que service lié, Messenger facilite l'IPC en se concentrant sur le traitement des données via la méthode onBind
. Il est essentiel d'inspecter cette méthode de près pour tout traitement de données non sécurisé ou l'exécution de fonctions sensibles.
Binder: Bien que l'utilisation directe de la classe Binder soit moins courante en raison de l'abstraction d'AIDL, il est bénéfique de comprendre que Binder agit comme un pilote au niveau du noyau facilitant le transfert de données entre les espaces mémoire de différents processus. Pour une meilleure compréhension, une ressource est disponible sur https://www.youtube.com/watch?v=O-UHvFjxwZ8.
Ceux-ci incluent: Activités, Services, Récepteurs de diffusion et Fournisseurs.
Dans les applications Android, les activités sont comme des écrans, affichant différentes parties de l'interface utilisateur de l'application. Une application peut avoir de nombreuses activités, chacune présentant un écran unique à l'utilisateur.
L'activité de lancement est la principale passerelle vers une application, lancée lorsque vous appuyez sur l'icône de l'application. Elle est définie dans le fichier manifeste de l'application avec des intentions MAIN et LAUNCHER spécifiques:
Pas toutes les applications ont besoin d'une activité de lancement, en particulier celles sans interface utilisateur, comme les services en arrière-plan.
Les activités peuvent être rendues disponibles à d'autres applications ou processus en les marquant comme "exportées" dans le manifeste. Ce paramètre permet à d'autres applications de démarrer cette activité :
Cependant, accéder à une activité depuis une autre application n'est pas toujours un risque pour la sécurité. Le problème survient si des données sensibles sont partagées de manière inappropriée, ce qui pourrait entraîner des fuites d'informations.
Le cycle de vie d'une activité commence avec la méthode onCreate, configurant l'interface utilisateur et préparant l'activité pour l'interaction avec l'utilisateur.
Dans le développement Android, une application a la possibilité de créer une sous-classe de la classe Application, bien que cela ne soit pas obligatoire. Lorsqu'une telle sous-classe est définie, elle devient la première classe à être instanciée dans l'application. La méthode attachBaseContext
, si elle est implémentée dans cette sous-classe, est exécutée avant la méthode onCreate
. Cette configuration permet une initialisation précoce avant le démarrage du reste de l'application.
Les services sont des opérations en arrière-plan capables d'exécuter des tâches sans interface utilisateur. Ces tâches peuvent continuer à s'exécuter même lorsque les utilisateurs passent à différentes applications, ce qui rend les services cruciaux pour les opérations de longue durée.
Les services sont polyvalents ; ils peuvent être initiés de différentes manières, les Intents étant la méthode principale pour les lancer en tant que point d'entrée d'une application. Une fois qu'un service est démarré en utilisant la méthode startService
, sa méthode onStart
entre en action et continue à s'exécuter jusqu'à ce que la méthode stopService
soit explicitement appelée. Alternativement, si le rôle d'un service dépend d'une connexion client active, la méthode bindService
est utilisée pour lier le client au service, en engageant la méthode onBind
pour le passage de données.
Une application intéressante des services comprend la lecture de musique en arrière-plan ou la récupération de données réseau sans entraver l'interaction de l'utilisateur avec une application. De plus, les services peuvent être rendus accessibles à d'autres processus sur le même appareil grâce à l'exportation. Ce n'est pas le comportement par défaut et nécessite une configuration explicite dans le fichier Android Manifest :
Les récepteurs de diffusion agissent comme des écouteurs dans un système de messagerie, permettant à plusieurs applications de répondre aux mêmes messages du système. Une application peut enregistrer un récepteur de deux manières principales : via le Manifest de l'application ou dynamiquement dans le code de l'application via l'API registerReceiver
. Dans le Manifest, les diffusions sont filtrées avec des autorisations, tandis que les récepteurs enregistrés dynamiquement peuvent également spécifier des autorisations lors de l'enregistrement.
Les filtres d'intention sont cruciaux dans les deux méthodes d'enregistrement, déterminant quelles diffusions déclenchent le récepteur. Une fois qu'une diffusion correspondante est envoyée, la méthode onReceive
du récepteur est invoquée, permettant à l'application de réagir en conséquence, comme ajuster le comportement en réponse à une alerte de batterie faible.
Les diffusions peuvent être soit asynchrones, atteignant tous les récepteurs sans ordre, soit synchrones, où les récepteurs reçoivent la diffusion en fonction de priorités définies. Cependant, il est important de noter le risque potentiel pour la sécurité, car toute application peut se prioriser pour intercepter une diffusion.
Pour comprendre la fonctionnalité d'un récepteur, recherchez la méthode onReceive
dans sa classe. Le code de cette méthode peut manipuler l'Intention reçue, soulignant la nécessité de la validation des données par les récepteurs, en particulier dans les Diffusions ordonnées, qui peuvent modifier ou supprimer l'Intention.
Les fournisseurs de contenu sont essentiels pour partager des données structurées entre les applications, mettant en avant l'importance de mettre en œuvre des autorisations pour garantir la sécurité des données. Ils permettent aux applications d'accéder à des données provenant de différentes sources, y compris des bases de données, des systèmes de fichiers ou le web. Des autorisations spécifiques, telles que readPermission
et writePermission
, sont cruciales pour contrôler l'accès. De plus, un accès temporaire peut être accordé via les paramètres grantUriPermission
dans le manifest de l'application, en exploitant des attributs tels que path
, pathPrefix
et pathPattern
pour un contrôle d'accès détaillé.
La validation des entrées est primordiale pour prévenir les vulnérabilités, telles que les injections SQL. Les fournisseurs de contenu prennent en charge des opérations de base : insert()
, update()
, delete()
et query()
, facilitant la manipulation et le partage de données entre les applications.
FileProvider, un Fournisseur de contenu spécialisé, se concentre sur le partage sécurisé de fichiers. Il est défini dans le manifest de l'application avec des attributs spécifiques pour contrôler l'accès aux dossiers, indiqués par android:exported
et android:resource
pointant vers des configurations de dossiers. Il est conseillé de faire preuve de prudence lors du partage de répertoires pour éviter d'exposer involontairement des données sensibles.
Déclaration de manifeste exemple pour FileProvider:
Et un exemple de spécification des dossiers partagés dans filepaths.xml
:
Pour plus d'informations, consultez :
Les WebViews sont comme des mini navigateurs web à l'intérieur des applications Android, récupérant du contenu soit depuis le web, soit depuis des fichiers locaux. Ils sont confrontés à des risques similaires à ceux des navigateurs classiques, mais il existe des moyens de réduire ces risques grâce à des paramètres spécifiques.
Android propose deux principaux types de WebViews :
WebViewClient est idéal pour le HTML de base mais ne prend pas en charge la fonction d'alerte JavaScript, ce qui affecte la manière dont les attaques XSS peuvent être testées.
WebChromeClient se comporte davantage comme l'expérience complète du navigateur Chrome.
Un point clé est que les navigateurs WebView ne partagent pas les cookies avec le navigateur principal de l'appareil.
Pour le chargement du contenu, des méthodes telles que loadUrl
, loadData
, et loadDataWithBaseURL
sont disponibles. Il est crucial de s'assurer que ces URL ou fichiers sont sûrs à utiliser. Les paramètres de sécurité peuvent être gérés via la classe WebSettings
. Par exemple, désactiver JavaScript avec setJavaScriptEnabled(false)
peut prévenir les attaques XSS.
Le "Bridge" JavaScript permet aux objets Java d'interagir avec JavaScript, exigeant que les méthodes soient marquées avec @JavascriptInterface
pour la sécurité à partir d'Android 4.2.
Autoriser l'accès au contenu (setAllowContentAccess(true)
) permet aux WebViews d'accéder aux Fournisseurs de contenu, ce qui pourrait représenter un risque à moins que les URL du contenu ne soient vérifiées comme sécurisées.
Pour contrôler l'accès aux fichiers :
Désactiver l'accès aux fichiers (setAllowFileAccess(false)
) limite l'accès au système de fichiers, avec des exceptions pour certains éléments, garantissant qu'ils ne sont utilisés que pour du contenu non sensible.
La signature numérique est indispensable pour les applications Android, garantissant qu'elles sont authentiquement authentifiées avant l'installation. Ce processus utilise un certificat pour l'identification de l'application et doit être vérifié par le gestionnaire de packages de l'appareil lors de l'installation. Les applications peuvent être auto-signées ou certifiées par une AC externe, se protégeant contre les accès non autorisés et assurant que l'application reste intacte lors de sa livraison à l'appareil.
À partir d'Android 4.2, une fonctionnalité appelée Vérifier les applications permet aux utilisateurs de vérifier la sécurité des applications avant l'installation. Ce processus de vérification peut avertir les utilisateurs contre les applications potentiellement dangereuses, voire empêcher l'installation de celles particulièrement malveillantes, renforçant la sécurité des utilisateurs.
Les solutions MDM fournissent une surveillance et une sécurité pour les appareils mobiles via l'API d'administration des appareils. Elles nécessitent l'installation d'une application Android pour gérer et sécuriser efficacement les appareils mobiles. Les fonctions clés incluent l'application de politiques de mot de passe, l'obligation de chiffrement du stockage, et l'autorisation de l'effacement des données à distance, assurant un contrôle et une sécurité complets sur les appareils mobiles.
Groupe de sécurité Try Hard