Abus des services d’accessibilité 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

Aperçu

AccessibilityService a été créé pour aider les personnes en situation de handicap à interagir avec les appareils Android. Malheureusement, les mêmes API d’automatisation puissantes (navigation globale, saisie de texte, envoi de gestes, fenêtres d’overlay…) peuvent être détournées par des malwares pour obtenir un contrôle à distance complet du terminal sans root privileges.

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 permission BIND_ACCESSIBILITY_SERVICE est considérée comme « à haut risque » et requiert une action explicite de l’utilisateur).
  2. Exploiter le service pour
  • capturer chaque événement UI et chaque texte qui apparaît à l’écran,
  • injecter des gestes synthétiques (dispatchGesture) et des actions globales (performGlobalAction) pour automatiser n’importe quelle tâche souhaitée par l’opérateur,
  • dessiner des overlays plein écran par-dessus des apps légitimes en utilisant le type de fenêtre TYPE_ACCESSIBILITY_OVERLAY (pas d’invite SYSTEM_ALERT_WINDOW !),
  • accorder silencieusement des autorisations runtime supplémentaires en cliquant sur les dialogues système au nom de la victime.
  1. Exfiltrer des données ou réaliser de la On-Device-Fraud (ODF) en temps réel pendant que l’utilisateur regarde un écran parfaitement normal.

Droppeurs d’accessibilité packagés

ClayRat v3.0.8 couple son Accessibility RAT avec une charge utile en plusieurs étapes cachée sous assets/. À l’exécution, l’APK hôte :

  1. Récupère en streaming le blob chiffré depuis assets/*.dat.
  2. Le décrypte avec une clé AES/CBC codée en dur + IV intégrés dans le loader Java/Kotlin.
  3. Écrit le DEX en clair dans le répertoire privé de l’app et le charge via DexClassLoader, exposant les classes réelles de spyware uniquement en mémoire.
byte[] blob = readAsset("payload.enc");
Cipher c = Cipher.getInstance("AES/CBC/PKCS5Padding");
SecretKeySpec key = new SecretKeySpec(hex("A1..."), "AES");
c.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(iv));
byte[] dex = c.doFinal(blob);
DexClassLoader cl = new DexClassLoader(writeTemp(dex), getCodeCacheDir().getPath(), null, getClassLoader());
cl.loadClass("com.clayrat.Core").newInstance();

Ce schéma d’empaquetage (ATT&CK T1406.002) maintient le module Accessibility hors du disque jusqu’à l’exécution du dropper, contournant les scans de signatures statiques et Play Protect tant que l’utilisateur n’a pas encore accordé les permissions dangereuses.


Demander la permission

<!-- AndroidManifest.xml -->
<service
android:name="com.evil.rat.EvilService"
android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE"
android:exported="false">

<intent-filter>
<action android:name="android.accessibilityservice.AccessibilityService" />
</intent-filter>

<meta-data android:name="android.accessibilityservice"
android:resource="@xml/evil_accessibility_config"/>
</service>

Le XML compagnon définit l’apparence de la fausse boîte de dialogue :

<?xml version="1.0" encoding="utf-8"?>
<accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"
android:description="@string/service_description"
android:accessibilityEventTypes="typeAllMask"
android:accessibilityFeedbackType="feedbackGeneric"
android:notificationTimeout="200"
android:canPerformGestures="true"
android:canRetrieveWindowContent="true"/>

Primitives d’automatisation d’interface utilisateur à distance

Squelette d'automatisation du service d'accessibilité ```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); } }

</details>

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 d'autorisation qui apparaissent.
* Installer/mettre à jour des APK supplémentaires via l'intent Play Store.

---

## Modèles d'abus

### 1. Overlay Phishing (Credential Harvesting)
Un `WebView` transparent ou opaque est ajouté au gestionnaire de fenêtres:
```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 faux formulaire pendant que l’application 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 de la page Tapjacking.

ClayRat expose cette capacité via les commandes show_block_screen / hide_block_screen qui téléchargent des templates d’overlay depuis le C2. Les opérateurs peuvent changer les layouts à la volée pour :

  • Noircir le panneau afin que la victime suppose que le mobile est éteint ou gelé pendant que des gestes automatisés désactivent Play Protect ou accordent plus d’autorisations.
  • Afficher de faux panneaux system update / battery optimization qui justifient pourquoi l’appareil est « occupé » pendant que l’automatisation en arrière-plan continue.
  • Afficher un overlay de interactive PIN pad qui reproduit l’écran de verrouillage système — le malware capture chaque chiffre et le transmet à l’opérateur dès qu’un code à 4 chiffres est saisi.

Parce que les fenêtres TYPE_ACCESSIBILITY_OVERLAY ne déclenchent jamais l’invite de permission SYSTEM_ALERT_WINDOW, la victime ne voit que l’interface leurre pendant que le RAT continue d’interagir avec les vraies apps en dessous.

2. Automatisation de fraude sur l’appareil

Des familles de malware comme 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 l’authentification multi‑facteurs liée à cet appareil.

3. Streaming et surveillance d’écran

ClayRat améliore la ruse MediaProjection classique en une pile de bureau à distance :

  1. turbo_screen déclenche la boîte de consentement MediaProjection ; le service Accessibility clique sur « Start now » pour que la victime n’ait jamais à intervenir.
  2. Avec le token MediaProjection obtenu, il crée un VirtualDisplay soutenu par un ImageReader, maintient un ForegroundService actif et récupère les frames sur des threads workers.
  3. Les frames sont encodées en JPEG/PNG selon le paramètre set_quality fourni par l’opérateur (valeur par défaut 60 si absent) et envoyées via un upgrade HTTP→WebSocket annonçant le user-agent personnalisé ClayRemoteDesktop.
  4. start_desktop / stop_desktop gèrent les threads de capture tandis que screen_tap, screen_swipe, input_text, press_home, press_back et press_recents rejouent des gestes sur le framebuffer en direct.

Le résultat est un flux de type VNC fourni entièrement via des APIs autorisées — aucun exploit root ou kernel — tout en offrant à l’attaquant une conscience situationnelle en temps réel avec une latence de l’ordre de la milliseconde.

4. Vol d’identifiants sur l’écran de verrouillage et déverrouillage automatique

ClayRat s’abonne aux événements TYPE_WINDOW_CONTENT_CHANGED / TYPE_VIEW_TEXT_CHANGED émis par com.android.systemui (Keyguard). Il reconstruit le mécanisme de verrouillage actif :

  • PIN – surveille les pressions sur le pavé numérique jusqu’à ce que le locker signale la fin.
  • Password – concatène les chaînes vues dans le champ de mot de passe focalisé pour chaque AccessibilityEvent.
  • Pattern – enregistre les indices de noeuds ordonnés déduits des coordonnées des gestes sur la grille 3×3.

Les secrets plus les métadonnées (type de verrou + horodatage) sont sérialisés dans SharedPreferences sous lock_password_storage. Lorsque l’opérateur envoie auto_unlock, le service réveille l’appareil avec unlock_device / screen_on, rejoue les chiffres ou gestes stockés via dispatchGesture, et contourne silencieusement le keyguard afin que les flux ODF suivants puissent se poursuivre.

5. Phishing et récolte via notifications

Un Notification Listener compagnon transforme le panneau de notifications en surface de phishing :

  • get_push_notifications exporte toutes les notifications actuellement visibles, y compris les messages OTP / MFA.
  • La commande notifications bascule un flag notifications_enabled afin que chaque futur payload onNotificationPosted() soit streamé vers le C2 en temps réel.
  • send_push_notification permet aux opérateurs de créer de fausses notifications interactives se faisant passer pour des apps bancaires ou de messagerie ; tout texte saisi par la victime est parsé comme des identifiants et exfiltré immédiatement.

Parce que Accessibility peut ouvrir/fermer le panneau de notifications de façon programmatique, cette méthode récolte des secrets sans toucher aux apps ciblées.

6. Canal de commandes Téléphonie & SMS

Après avoir contraint l’utilisateur à définir le RAT comme application SMS par défaut, les commandes suivantes fournissent un contrôle complet du modem :

  • send_sms et retransmishion envoient des messages arbitraires ou rejoués vers des numéros contrôlés par l’attaquant.
  • messsms itère sur l’ensemble de la base de contacts pour spammer des liens de phishing en propagation de type ver.
  • make_call initie des appels vocaux qui supportent des scénarios d’ingénierie sociale.
  • get_sms_list / get_sms et get_call_log / get_calls exportent les boîtes de réception et l’historique d’appels afin que les codes MFA ou les métadonnées d’appel puissent être exploités instantanément.

Combiné avec la navigation UI pilotée par Accessibility, ClayRat peut recevoir un OTP via notification/SMS et le saisir immédiatement dans l’app bancaire ou d’entreprise ciblée.

7. Découverte, collecte & proxying

Des commandes ClayRat supplémentaires cartographient l’environnement et maintiennent la résilience du C2 :

  • get_apps / get_apps_list énumèrent les packages installés (ATT&CK T1418).
  • get_device_info rapporte le modèle, la version de l’OS et l’état de la batterie (T1426).
  • get_cam / get_camera capture des images de la caméra frontale, tandis que get_keylogger_data sérialise les PINs de verrouillage ainsi que les mots de passe, descriptions de vues et indices extraits des champs sensibles.
  • get_proxy_data récupère une URL WebSocket de proxy, y ajoute l’ID unique de l’appareil et lance un job qui tunnelise HTTP/HTTPS sur le même canal bidirectionnel (T1481.002 / T1646).

PlayPraetor – workflow de command & control

  1. HTTP(S) heartbeat – itérer 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 – pousser de nouvelles conf/APKs
  • alert_arr – configurer des templates d’overlay
  • report_list – envoyer la liste des noms de packages ciblés
  • heartbeat_web – keep-alive
  1. RTMP (port 1935) – streaming d’écran/vidéo en direct.
  2. REST exfiltration
  • /app/saveDevice (fingerprint)
  • /app/saveContacts | /app/saveSms | /app/uploadImageBase64
  • /app/saveCardPwd (identifiants bancaires)

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
  • Paramètres → Accessibility → Downloaded services – recherchez des apps qui ne proviennent pas du Google Play.
  • MDM / EMM solutions can enforce ACCESSIBILITY_ENFORCEMENT_DEFAULT_DENY (Android 13+) to block sideloaded services.
  • Analyser les services en cours :
adb shell dumpsys accessibility | grep "Accessibility Service"

Recommandations de durcissement pour les développeurs d’apps

  • Marquez les vues sensibles avec android:accessibilityDataSensitive="accessibilityDataPrivateYes" (API 34+).
  • Combinez setFilterTouchesWhenObscured(true) avec FLAG_SECURE pour empêcher le détournement de tap/overlay.
  • Détectez les overlays en interrogeant WindowManager.getDefaultDisplay().getFlags() ou l’API ViewRootImpl.
  • Refusez de fonctionner lorsque Settings.canDrawOverlays() ou un service Accessibility non fiable est actif.

Cheat-sheet d’automatisation ATS (pilotée par Accessibility)

Un malware peut entièrement automatiser une app bancaire en n’utilisant que les APIs Accessibility. Primitives génériques :

Méthodes utilitaires pour l'automatisation ATS ```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); } ```

Flux d’exemple (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 domestique) → mettre le focus sur 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 à cause de widgets personnalisés.

Observé également : des pré-étapes vers check_limit et limit en naviguant vers l’UI des limites et en augmentant les limites quotidiennes avant le transfert.

Streaming pseudo-écran basé sur du texte

Pour un contrôle à faible latence, plutôt que du streaming vidéo complet, extraire une représentation textuelle de l’arbre UI actuel et l’envoyer de manière répétée au C2.

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<n.getChildCount();i++) dumpTree(n.getChild(i), indent+"  ", sb);
}

Ceci est la base pour des commandes comme txt_screen (one-shot) et screen_live (continuous).

Primitives de contrainte pour Device Admin

Une fois qu’un Device Admin receiver est activé, ces appels augmentent les opportunités de capturer des identifiants et de maintenir le contrôle :

DevicePolicyManager dpm = (DevicePolicyManager) getSystemService(DEVICE_POLICY_SERVICE);
ComponentName admin = new ComponentName(this, AdminReceiver.class);

// 1) Immediate lock
dpm.lockNow();

// 2) Force credential change (expire current PIN/password)
dpm.setPasswordExpirationTimeout(admin, 1L); // may require owner/profile-owner on recent Android

// 3) Disable biometric unlock to force PIN/pattern entry
int flags = DevicePolicyManager.KEYGUARD_DISABLE_FINGERPRINT |
DevicePolicyManager.KEYGUARD_DISABLE_TRUST_AGENTS;
dpm.setKeyguardDisabledFeatures(admin, flags);

Remarque : la disponibilité exacte de ces politiques varie selon la version d’Android et l’OEM ; validez le rôle de la policy de l’appareil (admin vs owner) pendant les tests.

Crypto wallet seed-phrase extraction patterns

Flux observés pour MetaMask, Trust Wallet, Blockchain.com et Phantom :

  • Déverrouiller avec un PIN volé (capturé via overlay/Accessibility) ou le mot de passe du wallet fourni.
  • Naviguer : Settings → Security/Recovery → Reveal/Show recovery phrase.
  • Récupérer la phrase via keylogging des text nodes, contournement de secure-screen, ou OCR de screenshot quand le texte est masqué.
  • Prendre en charge plusieurs locales (EN/RU/CZ/SK) pour stabiliser les selectors – préférer viewIdResourceName quand disponible, fallback sur la correspondance de texte multilingue.

NFC-relay orchestration

Les modules Accessibility/RAT peuvent installer et lancer une app NFC-relay dédiée (p.ex., NFSkate) comme troisième étape et même injecter un overlay guide pour accompagner la victime à travers les étapes de relay avec carte présente.

Background and TTPs: https://www.threatfabric.com/blogs/ghost-tap-new-cash-out-tactic-with-nfc-relay


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