Bases des applications Android

Tip

Apprenez et pratiquez le hacking AWS :HackTricks Training AWS Red Team Expert (ARTE)
Apprenez et pratiquez le hacking GCP : HackTricks Training GCP Red Team Expert (GRTE) Apprenez et pratiquez le hacking Azure : HackTricks Training Azure Red Team Expert (AzRTE)

Soutenir HackTricks

Modèle de sécurité Android

Il y a deux couches :

  • L’OS, qui maintient les applications installées isolées les unes des autres.
  • L’application elle‑même, qui permet aux développeurs d’exposer certaines fonctionnalités et configure les capacités de l’application.

Séparation des UID

Chaque application reçoit un User ID spécifique. Cela se fait lors de l’installation de l’app afin que l’application ne puisse interagir qu’avec les fichiers appartenant à son User ID ou aux fichiers partagés. Par conséquent, seules l’application elle‑même, certains composants de l’OS et l’utilisateur root peuvent accéder aux données de l’application.

Partage d’UID

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 manifests.

Sandboxing

Le Android Application Sandbox permet d’exécuter chaque application en tant que processus séparé sous un User ID distinct. Chaque processus dispose de sa propre machine virtuelle, donc le code d’une app s’exécute en isolation par rapport aux autres apps.
Depuis Android 5.0 (L) SELinux est appliqué. En pratique, SELinux a d’abord refusé toutes les interactions entre processus, puis des policies ont été créées pour autoriser uniquement les interactions attendues entre eux.

Permissions

Lorsque vous installez une app et qu’elle demande des permissions, l’app sollicite les permissions configurées dans les éléments uses-permission du fichier AndroidManifest.xml. L’élément uses-permission indique le nom de la permission demandée dans l’attribut name. Il possède aussi l’attribut maxSdkVersion qui empêche de demander la permission sur les versions supérieures à celle spécifiée.
Notez que les applications Android n’ont pas besoin de demander toutes les permissions au démarrage ; elles peuvent aussi demander des permissions dynamiquement, mais toutes les permissions doivent être déclarées dans le manifest.

Lorsqu’une app expose une fonctionnalité, elle peut limiter l’accès uniquement aux apps qui possèdent une permission spécifiée.
Un élément permission a trois attributs :

  • Le name de la permission
  • L’attribut permission-group, qui permet de regrouper les permissions liées.
  • Le protection-level qui indique comment les permissions sont accordées. Il existe quatre types :
    • Normal : Utilisée lorsqu’il n’y a aucune menace connue pour l’app. L’utilisateur n’est pas obligé de l’approuver.
    • Dangerous : Indique que la permission accorde à l’application requérante un accès élevé. Les utilisateurs doivent les approuver.
    • Signature : Seules les apps signées par le même certificat que celle exportant le composant peuvent se voir accorder la permission. C’est le type de protection le plus strict.
    • SignatureOrSystem : Seules les apps signées par le même certificat que celle exportant le composant ou les apps s’exécutant avec un accès de niveau système peuvent se voir accorder ces permissions.

Applications préinstallées

Ces apps 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 méritent d’être vérifiées car parfois elles s’exécutent avec trop de permissions (en tant que root).

  • Celles livrées avec le AOSP (Android OpenSource Project) ROM
  • Ajoutées par le fabricant de l’appareil
  • Ajoutées par l’opérateur (si l’appareil a été acheté chez eux)

Rooting

Pour obtenir l’accès root sur un appareil Android physique, vous devez généralement exploiter une ou deux vulnerabilities qui sont souvent spécifiques à l’appareil et à la version.
Une fois l’exploit réussi, en général le binaire Linux su est copié dans un emplacement présent dans la variable PATH de l’utilisateur, comme /system/xbin.

Une fois le binaire su configuré, une autre app Android est utilisée pour interagir avec le binaire su et traiter les demandes d’accès root comme Superuser et SuperSU (disponibles sur le Google Play store).

Caution

Notez que le processus de rooting est très dangereux et peut gravement endommager l’appareil

ROMs

Il est possible de remplacer l’OS en installant un firmware custom. Ainsi, il est possible d’étendre l’utilité d’un vieux device, de contourner des restrictions logicielles ou d’accéder au dernier code Android.
OmniROM et LineageOS sont deux des firmwares les plus populaires.

Notez que il n’est pas toujours nécessaire de root l’appareil pour installer un firmware custom. Certains fabricants permettent le déverrouillage de leur bootloader de manière bien documentée et sûre.

Implications

Une fois qu’un appareil est rooted, toute app peut demander un accès root. Si une application malveillante l’obtient, elle aura accès à presque tout et pourra endommager gravement le téléphone.

Fondamentaux des applications Android

  • Le format des applications Android est appelé APK file format. C’est essentiellement un ZIP file (en renommant l’extension en .zip, le contenu peut être extrait et consulté).
  • 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 compilé Java (ou Kotlin) que l’application exécute par défaut.
  • lib/
  • Contient les bibliothèques natives, séparées par architecture CPU dans des sous‑répertoires.
    • armeabi: code pour processeurs ARM
    • armeabi-v7a: code pour processeurs ARMv7 et supérieurs
    • x86: code pour processeurs X86
    • mips: code pour processeurs MIPS uniquement
  • assets/
  • Stocke des fichiers divers nécessaires à l’app, incluant potentiellement des bibliothèques natives ou des fichiers DEX supplémentaires, parfois utilisés par des auteurs de malware pour dissimuler du code additionnel.
  • res/
  • Contient des ressources qui ne sont pas compilées dans resources.arsc

Dalvik & Smali

En développement Android, Java ou Kotlin sont utilisés pour créer des apps. Plutôt que d’utiliser la JVM comme pour les apps desktop, Android compile ce code en Dalvik Executable (DEX) bytecode. Auparavant, la virtual machine Dalvik gérait ce bytecode, mais désormais, l’Android Runtime (ART) prend le relais sur les versions récentes d’Android.

Pour le reverse engineering, Smali devient crucial. C’est la version lisible par l’humain du bytecode DEX, agissant comme un langage assembleur en traduisant le code source en instructions bytecode. Smali et baksmali désignent respectivement les outils d’assemblage et de désassemblage dans ce contexte.

Intents

Les Intents sont le principal moyen par lequel les apps Android communiquent entre leurs composants ou avec d’autres apps. Ces objets message peuvent aussi transporter des données entre apps ou composants, de façon similaire aux requêtes GET/POST en HTTP.

Donc un Intent est essentiellement un message qui est passé entre des composants. Les Intents peuvent être dirigés vers des composants ou apps spécifiques, ou peuvent être envoyés sans destinataire spécifique.
Pour faire simple, un Intent peut être utilisé :

  • Pour démarrer une Activity, ouvrant typiquement une interface utilisateur pour une app
  • Comme broadcasts pour informer le système et les apps de changements
  • Pour démarrer, arrêter et communiquer avec un service en background
  • Pour accéder à des données via des ContentProviders
  • Comme callbacks pour gérer des événements

Si vulnérables, les Intents peuvent être utilisés pour effectuer une variété d’attaques.

Intent-Filter

Les Intent Filters définissent comment une activity, un service ou un Broadcast Receiver peut interagir avec différents types d’Intents. Essentiellement, ils décrivent les capacités de ces composants, comme les actions qu’ils peuvent effectuer ou les types de broadcasts qu’ils peuvent traiter. Le lieu principal pour déclarer ces filters est le fichier AndroidManifest.xml, bien que pour les Broadcast Receivers, il soit aussi possible de les coder.

Les Intent Filters sont composés de catégories, d’actions et de filtres de données, avec la possibilité d’inclure des metadata supplémentaires. Cette configuration permet aux composants de traiter des Intents spécifiques qui correspondent aux critères déclarés.

Un aspect critique des composants Android (activities/services/content providers/broadcast receivers) est leur visibilité ou statut public. Un composant est considéré comme public et peut interagir avec d’autres apps s’il est exported avec une valeur true ou si un Intent Filter est déclaré pour lui dans le manifest. Cependant, il existe un moyen pour les développeurs de garder explicitement ces composants privés, en veillant à ce qu’ils n’interagissent pas involontairement avec d’autres apps. Cela se fait en définissant l’attribut exported sur false dans leurs définitions de manifest.

De plus, les développeurs ont la possibilité de renforcer l’accès à ces composants en exigeant des permissions spécifiques. L’attribut permission peut être défini pour imposer que seules les apps disposant de la permission 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.

<activity android:name=".MyActivity" android:exported="false">
<!-- Intent filters go here -->
</activity>

Intents implicites

Les Intents sont créés de manière programmatique en utilisant un constructeur Intent :

Intent email = new Intent(Intent.ACTION_SEND, Uri.parse("mailto:"));

L’Action de l’intent précédemment déclaré est ACTION_SEND et l’Extra est une Uri mailto (l’Extra est l’information supplémentaire que l’intent attend).

Cet intent doit être déclaré dans le manifest comme dans l’exemple suivant :

<activity android:name="ShareActivity">
<intent-filter>
<action android:name="android.intent.action.SEND" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>

Un intent-filter doit correspondre à l’action, aux données et à la catégorie pour recevoir un message.

Le processus “Intent resolution” détermine quelle application doit recevoir chaque message. Ce processus prend en compte l’attribut priority, qui peut être défini dans la déclaration intent-filter, et celle qui a la priorité la plus élevée sera sélectionnée. Cette priorité peut être réglée entre -1000 et 1000 et les applications peuvent utiliser la valeur SYSTEM_HIGH_PRIORITY. Si un conflit survient, une fenêtre “chooser” apparaît afin que l’utilisateur puisse décider.

Intents explicites

Un intent explicite spécifie le nom de la classe qu’il cible :

Intent downloadIntent = new (this, DownloadService.class):

Dans d’autres applications, pour accéder à l’intent déclaré précédemment, vous pouvez utiliser :

Intent intent = new Intent();
intent.setClassName("com.other.app", "com.other.app.ServiceName");
context.startService(intent);

Pending Intents

These allow other applications to take actions on behalf of your application, using your app’s identity and permissions. Constructing a Pending Intent it should be specified an intent and the action to perform. If the declared intent isn’t Explicit (doesn’t declare which intent can call it) a malicious application could perform the declared action on behalf of the victim app. Moreover, if an action isn’t specified, the malicious app will be able to do any action on behalf the victim.

Broadcast Intents

Unlike the previous intents, which are only received by one app, broadcast intents can be received by multiple apps. However, from API version 14, it’s possible to specify the app that should receive the message using Intent.set Package.

Alternatively it’s also possible to specify a permission when sending the broadcast. The receiver app will need to have that permission.

There are two types of Broadcasts: Normal (asynchronous) and Ordered (synchronous). The order is base on the configured priority within the receiver element. Each app can process, relay or drop the Broadcast.

It’s possible to send a broadcast using the function sendBroadcast(intent, receiverPermission) from the Context class.
You could also use the function sendBroadcast from the LocalBroadCastManager ensures the message never leaves the app. Using this you won’t even need to export a receiver component.

Sticky Broadcasts

This kind of Broadcasts can be accessed long after they were sent.
These were deprecated in API level 21 and it’s recommended to not use them.
They allow any application to sniff the data, but also to modify it.

If you find functions containing the word “sticky” like sendStickyBroadcast or sendStickyBroadcastAsUser, check the impact and try to remove them.

In Android applications, deep links are used to initiate an action (Intent) directly through a URL. This is done by declaring a specific URL scheme within an activity. When an Android device tries to access a URL with this scheme, the specified activity within the application is launched.

The scheme must be declarated in the AndroidManifest.xml file:

[...]
<activity android:name=".MyActivity">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="examplescheme" />
</intent-filter>
[...]

Le schéma de l’exemple précédent est examplescheme:// (notez aussi la category BROWSABLE)

Ensuite, dans le champ data, vous pouvez spécifier le host et le path:

<data android:scheme="examplescheme"
android:host="example"
/>

Pour y accéder depuis le web, il est possible de définir un lien comme :

<a href="examplescheme://example/something">click here</a>
<a href="examplescheme://example/javascript://%250dalert(1)">click here</a>

Pour trouver le code qui sera exécuté dans l’App, allez à l’activity appelée par le deeplink et cherchez la fonction onNewIntent.

Apprenez comment appeler les deep links sans utiliser de pages HTML.

  • Découverte des points d’entrée : Activities exportées qui déclarent <action android:name="android.intent.action.VIEW" /> + <category android:name="android.intent.category.BROWSABLE" /> sont accessibles à distance via des URI craftées (custom schemes ou http/https App Links). Priorisez les chemins contenant les mots-clés login/reset/payment/wallet/admin.
  • Heuristiques de contournement de validation : des vérifications d’hôte faibles comme endsWith(), contains(), des regex permissifs, ou des listes d’autorisation basées sur des sous-chaînes peuvent généralement être contournées avec des sous-domaines contrôlés par l’attaquant, des astuces de préfixe/suffixe, et un double-encodage URL/UTF‑8.
  • WebView sinks : si le handler transmet l’URI entrant ou les query params à WebView.loadUrl(...), vous pouvez forcer l’app à afficher un contenu arbitraire contrôlé par l’attaquant. Si la validation du scheme est faible, essayez des payloads javascript: ainsi que des URL externes https://.
  • adb PoC templates (implicit vs explicit):
# Generic implicit VIEW (custom scheme or App Link)
adb shell am start -a android.intent.action.VIEW \
-d "myscheme://com.example.app/web?url=https://attacker.tld/payload.html"

# Explicitly target a specific Activity
adb shell am start -n com.example/.MainActivity -a android.intent.action.VIEW \
-d "myapp://host/path?redirect=https://attacker.tld"

# Try javascript: when scheme filters are lax
adb shell am start -a android.intent.action.VIEW \
-d "myapp://host/web?url=javascript:alert(1)"
  • Conseils opérationnels : capturez plusieurs variantes de payload (external URL vs javascript:) et rejouez-les rapidement sur un device/emulator pour distinguer les vrais problèmes (open-redirect/auth-bypass/WebView URL injection) du bruit de static-analysis.
  • Automation: Deep-C automatise deeplink hunting en décompilant l’APK (apktool + dex2jar + jadx), en énumérant les exported + browsable activities, en corrélant les validations faibles et les flux WebView.loadUrl, et en émettant des adb PoCs prêts à l’exécution (exécutables automatiquement optionnellement avec --exec).

AIDL - Android Interface Definition Language

Le Android Interface Definition Language (AIDL) est conçu pour faciliter la communication entre client et service dans les applications Android via la communication inter-processus (IPC). Comme l’accès direct à la mémoire d’un autre process n’est pas autorisé sur Android, AIDL simplifie le processus en marshallant les objets dans un format compris par le système d’exploitation, facilitant ainsi la communication entre différents process.

Concepts clés

  • Bound Services : ces services utilisent AIDL pour l’IPC, permettant aux activities ou components de se lier à un service, d’émettre des requêtes et de recevoir des réponses. La méthode onBind dans la classe du service est critique pour initier l’interaction, ce qui en fait une zone vitale à auditer pour détecter des vulnérabilités.

  • Messenger : fonctionnant comme un bound service, 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 détecter tout traitement de données non sûr 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 fournie par AIDL, il est utile de comprendre que Binder agit comme un driver au niveau kernel facilitant le transfert de données entre les espaces mémoire de différents process. Pour approfondir, une ressource est disponible à https://www.youtube.com/watch?v=O-UHvFjxwZ8.

Composants

Ils incluent : Activities, Services, Broadcast Receivers and Providers.

Launcher Activity et autres activities

Dans les applications Android, les activities sont comme des écrans, affichant différentes parties de l’interface utilisateur de l’application. Une application peut avoir de nombreuses activities, chacune présentant un écran différent à l’utilisateur.

La launcher activity est la porte d’entrée principale d’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 intents MAIN et LAUNCHER spécifiques :

<activity android:name=".LauncherActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>

Toutes les apps n’ont pas besoin d’une launcher activity, surtout celles sans interface utilisateur, comme les services en arrière-plan.

Les Activities peuvent être rendues accessibles à d’autres apps ou processus en les marquant comme “exported” dans le manifest. Ce paramètre permet à d’autres apps de démarrer cette activity :

<service android:name=".ExampleExportedService" android:exported="true"/>

Cependant, accéder à une activity depuis une autre application n’est pas toujours un risque de sécurité. Le problème survient si des données sensibles sont partagées de manière inappropriée, ce qui pourrait conduire à des information leaks.

Le cycle de vie d’une activity commence avec la méthode onCreate, en configurant l’UI et en préparant l’activity pour l’interaction avec l’utilisateur.

Sous-classe d’Application

Dans le développement Android, une application a la possibilité de créer une sous-classe de la Application classe, 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 que le reste de l’application ne démarre.

public class MyApp extends Application {
@Override
protected void attachBaseContext(Context base) {
super.attachBaseContext(base);
// Initialization code here
}

@Override
public void onCreate() {
super.onCreate();
// More initialization code
}
}

Services

Services are opérateurs 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 à d’autres 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 comme point d’entrée d’une application. Une fois qu’un service est démarré à l’aide de la méthode startService, sa méthode onStart se met en action et continue de s’exécuter jusqu’à ce que la méthode stopService soit appelée explicitement. 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, engageant la méthode onBind pour le passage de données.

Une application intéressante des services inclut la lecture de musique en arrière-plan ou la récupération de données réseau sans gêner l’interaction de l’utilisateur avec une application. De plus, les services peuvent être rendus accessibles à d’autres processus sur le même appareil via l’exportation. Ce n’est pas le comportement par défaut et nécessite une configuration explicite dans le Android Manifest file:

<service android:name=".ExampleExportedService" android:exported="true"/>

Récepteurs de broadcast

Broadcast receivers agissent comme des écouteurs dans un système de messagerie, permettant à plusieurs applications de répondre aux mêmes messages émis par le système. Une app peut register a receiver de deux manières principales : via le Manifest de l’app ou dynamiquement dans le code de l’app via l’API registerReceiver. Dans le Manifest, les broadcasts sont filtrés par des permissions, tandis que les receivers enregistrés dynamiquement peuvent également préciser des permissions lors de l’enregistrement.

Les Intent filters sont cruciaux dans les deux méthodes d’enregistrement : ils déterminent quels broadcasts déclenchent le receiver. Lorsqu’un broadcast correspondant est envoyé, la méthode onReceive du receiver est invoquée, permettant à l’app de réagir en conséquence, par exemple en adaptant son comportement face à une alerte de batterie faible.

Les broadcasts peuvent être soit asynchrones, atteignant tous les receivers sans ordre particulier, soit synchrones, où les receivers reçoivent le broadcast selon des priorités définies. Attention toutefois au risque de sécurité : n’importe quelle app peut se donner une priorité pour intercepter un broadcast.

Pour comprendre le fonctionnement d’un receiver, recherchez la méthode onReceive dans sa classe. Le code de cette méthode peut manipuler l’Intent reçu, ce qui souligne la nécessité d’une validation des données par les receivers, en particulier pour les Ordered Broadcasts, qui peuvent modifier ou abandonner l’Intent.

Content Provider

Content Providers sont essentiels pour le partage de données structurées entre applications, ce qui souligne l’importance d’implémenter des permissions pour garantir la sécurité des données. Ils permettent aux apps d’accéder à des données provenant de différentes sources : bases de données, systèmes de fichiers ou le web. Des permissions spécifiques, comme 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’app, en utilisant 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 éviter des vulnérabilités comme les injections SQL. Les Content Providers supportent des opérations de base : insert(), update(), delete() et query(), facilitant la manipulation et le partage de données entre applications.

Permission semantics and pitfalls (Content Providers)

  • Si un provider est exporté, vous devez déclarer explicitement à la fois readPermission et writePermission. Si writePermission est omis la valeur par défaut est null, ce qui signifie que n’importe quelle app peut tenter insert()/update()/delete() si ces méthodes sont implémentées par le provider.
  • Ne concaténez jamais des projection, selection, selectionArgs ou sortOrder non fiables dans du SQL brut. Utilisez des listes blanches et le binding de paramètres (par ex. SQLiteQueryBuilder avec une projection map) et des templates WHERE fixes.
  • Préférez android:exported="false" sauf si le provider doit être public. Pour un partage sélectif, utilisez grantUriPermissions avec path/pathPrefix/pathPattern.

FileProvider, un Content Provider spécialisé, se concentre sur le partage sécurisé de fichiers. Il est défini dans le manifest de l’app avec des attributs spécifiques pour contrôler l’accès aux dossiers, notamment android:exported et android:resource pointant vers des configurations de dossiers. Il convient d’être prudent lors du partage de répertoires afin d’éviter d’exposer involontairement des données sensibles.

Exemple de déclaration dans le manifest pour FileProvider:

<provider android:name="androidx.core.content.FileProvider"
android:authorities="com.example.myapp.fileprovider"
android:grantUriPermissions="true"
android:exported="false">
<meta-data android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/filepaths" />
</provider>

Et un exemple de spécification des dossiers partagés dans filepaths.xml :

<paths>
<files-path path="images/" name="myimages" />
</paths>

Pour plus d’informations, consultez :

WebViews

WebViews sont comme des mini navigateurs web intégrés aux apps Android, récupérant du contenu soit depuis le web soit depuis des fichiers locaux. Ils présentent des risques similaires aux navigateurs classiques, mais il existe des moyens de réduire ces risques via des paramètres spécifiques.

Android propose deux types principaux de WebView :

  • WebViewClient convient pour du HTML basique mais ne prend pas en charge la fonction JavaScript alert, ce qui affecte la façon dont les attaques XSS peuvent être testées.
  • WebChromeClient se comporte davantage comme l’expérience complète du navigateur Chrome.

Un point important est que les navigateurs WebView ne partagent pas les cookies avec le navigateur principal de l’appareil.

Pour charger du contenu, des méthodes telles que loadUrl, loadData et loadDataWithBaseURL sont disponibles. Il est crucial de s’assurer que ces URLs 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 annotées avec @JavascriptInterface pour des raisons de sécurité à partir d’Android 4.2.

Autoriser l’accès au contenu (setAllowContentAccess(true)) permet aux WebViews d’accéder aux Content Providers, ce qui peut représenter un risque à moins que les URLs de contenu ne soient vérifiées comme sûres.

Pour contrôler l’accès aux fichiers :

  • La désactivation de l’accès aux fichiers (setAllowFileAccess(false)) limite l’accès au système de fichiers, avec des exceptions pour certains assets, en s’assurant qu’ils ne servent qu’à du contenu non sensible.

Autres composants d’application et Mobile Device Management

Signature numérique des applications

  • La signature numérique est indispensable pour les apps Android, garantissant qu’elles sont authentiquement signées avant l’installation. Ce processus utilise un certificat pour l’identification de l’app et doit être vérifié par le gestionnaire de paquets de l’appareil lors de l’installation. Les apps peuvent être auto-signées ou certifiées par une CA externe, protégeant contre les accès non autorisés et garantissant que l’app n’a pas été altérée pendant sa distribution vers l’appareil.

Vérification des apps pour une sécurité renforcée

  • À partir d’Android 4.2, une fonctionnalité appelée Verify Apps permet aux utilisateurs de faire vérifier les apps avant installation. Ce processus de vérification peut avertir les utilisateurs contre des apps potentiellement dangereuses, ou même empêcher l’installation d’apps particulièrement malveillantes, renforçant la sécurité des utilisateurs.

Gestion des appareils mobiles (MDM)

  • Les solutions MDM offrent une supervision et une sécurité des appareils mobiles via le Device Administration API. Elles nécessitent l’installation d’une app 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 chiffrer le stockage, et la possibilité d’effacer des données à distance, garantissant un contrôle et une sécurité complets sur les appareils mobiles.
// Example of enforcing a password policy with MDM
DevicePolicyManager dpm = (DevicePolicyManager) getSystemService(Context.DEVICE_POLICY_SERVICE);
ComponentName adminComponent = new ComponentName(context, AdminReceiver.class);

if (dpm.isAdminActive(adminComponent)) {
// Set minimum password length
dpm.setPasswordMinimumLength(adminComponent, 8);
}

Énumération et exploitation des services AIDL / Binder

Android Binder IPC expose de nombreux services système et fournis par le vendor. Ces services deviennent une surface d’attaque lorsqu’ils sont exportés sans une vérification de permission adéquate (la couche AIDL elle-même n’effectue aucun contrôle d’accès).

1. Découvrir les services en cours d’exécution

# from an adb shell (USB or wireless)
service list               # simple one-liner
am list services           # identical output, ActivityManager wrapper

Je n’ai reçu aucun contenu à traduire. Veuillez coller le texte (ou préciser le fichier) que vous souhaitez traduire en français.
La traduction conservera la syntaxe markdown/HTML, les tags et chemins non traduits, et la sortie sera fournie sous forme de liste numérotée.

145  mtkconnmetrics: [com.mediatek.net.connectivity.IMtkIpConnectivityMetrics]
146  wifi             : [android.net.wifi.IWifiManager]
  • Le index (première colonne) est assigné à l’exécution – ne comptez pas dessus entre deux redémarrages.
  • Le Binder name (par ex. mtkconnmetrics) est ce qui sera passé à service call.
  • La valeur entre crochets est l’AIDL interface entièrement qualifiée dont le stub a été généré.

2. Obtenir le descripteur d’interface (PING)

Chaque stub Binder implémente automatiquement le transaction code 0x5f4e5446 (1598968902 décimal, ASCII “_NTF”).

# "ping" the service
service call mtkconnmetrics 1    # 1 == decimal 1598968902 mod 2^32

Une réponse valide renvoie le nom de l’interface encodé en UTF-16 à l’intérieur d’un Parcel.

3. Appel d’une transaction

Syntaxe: service call <name> <code> [type value ...]

Spécificateurs d’arguments courants:

  • i32 <int> – valeur signée 32 bits
  • i64 <long> – valeur signée 64 bits
  • s16 <string> – chaîne UTF-16 (Android 13+ utilise utf16)

Exemple – démarrer la surveillance réseau avec l’uid 1 sur un appareil MediaTek:

service call mtkconnmetrics 8 i32 1

4. Brute-forcing de méthodes inconnues

Lorsque les fichiers d’en-tête ne sont pas disponibles, vous pouvez itérer le code jusqu’à ce que l’erreur passe de :

Result: Parcel(00000000 00000000)  # "Not a data message"

à une réponse Parcel normale ou une SecurityException.

for i in $(seq 1 50); do
printf "[+] %2d -> " $i
service call mtkconnmetrics $i 2>/dev/null | head -1
done

Si le service a été compilé avec proguard la correspondance doit être devinée – voir l’étape suivante.

5. Mise en correspondance codes ↔ méthodes via onTransact()

Décompilez le jar/odex qui implémente l’interface (pour les stubs AOSP vérifiez /system/framework; les OEMs utilisent souvent /system_ext ou /vendor). Recherchez Stub.onTransact() – il contient un énorme switch(transactionCode):

case TRANSACTION_updateCtaAppStatus:      // 5
data.enforceInterface(DESCRIPTOR);
int appId  = data.readInt();
boolean ok = data.readInt() != 0;
updateCtaAppStatus(appId, ok);
reply.writeNoException();
return true;

Maintenant le prototype et les types de paramètres sont parfaitement clairs.

6. Repérer les contrôles d’autorisation manquants

L’implémentation (souvent une classe interne Impl) est responsable de la vérification des autorisations :

private void updateCtaAppStatus(int uid, boolean status) {
if (!isPermissionAllowed()) {
throw new SecurityException("uid " + uid + " rejected");
}
/* privileged code */
}

L’absence d’une telle logique ou d’une whitelist d’UID privilégiés (par ex. uid == 1000 /*system*/) est un indicateur de vulnérabilité.

Étude de cas – MediaTek startMonitorProcessWithUid() (transaction 8) exécute complètement un message Netlink sans aucun contrôle d’autorisation, permettant à une application non privilégiée d’interagir avec le module Netfilter du noyau et d’inonder le journal système.

7. Automatisation de l’évaluation

Outils / scripts qui accélèrent la reconnaissance Binder :

  • binderfs – exposes /dev/binderfs with per-service nodes
  • binder-scanner.py – walks the binder table and prints ACLs
  • Frida shortcut: Java.perform(()=>console.log(android.os.ServiceManager.listServices().toArray()))

Références

Tip

Apprenez et pratiquez le hacking AWS :HackTricks Training AWS Red Team Expert (ARTE)
Apprenez et pratiquez le hacking GCP : HackTricks Training GCP Red Team Expert (GRTE) Apprenez et pratiquez le hacking Azure : HackTricks Training Azure Red Team Expert (AzRTE)

Soutenir HackTricks