Anatomía de una infección Bigpanzi: mi Android TV trabajaba para una botnet
Hay incidentes que te eligen. Este empezó con un correo raro de mi ISP avisando de que mi IP aparecía en una lista negra, y terminó conmigo sentado frente a un Android TV de 2017 que llevaba 14 meses ejecutando un backdoor como root y alquilando mi conexión a terceros.
Esta es la historia completa, con la metodología, el protocolo C2 que logré recuperar y todo lo que haría distinto. Todo el análisis se hizo sobre mi propia red y mi propio dispositivo, en modo lectura, y sin interactuar con la infraestructura del atacante.
Los datos personales (IP pública, cuenta de Google, MAC) están enmascarados. Los IOC de red sí están completos: son infraestructura del atacante y sirven para que otros puedan detectarla.
Resumen en 30 segundos
| Qué | Detalle |
|---|---|
| Dispositivo | Android TV KII PRO (p20x, Amlogic) |
| Sistema | Android 5.1.1, SDK 22, build userdebug de 2017 |
| Malware | pandoraspearrk — familia Pandora / BADBOX 2.0 |
| Grupo | Bigpanzi (botnet de TV boxes, activa desde ~2015) |
| Persistencia | supervisord como watchdog root, autorestart=true |
| C2 | 45.56.117.139:9999 y otras 3 IPs en el puerto 9999 |
| Efecto | Proxy residencial + shell root + nodo DDoS |
| Consecuencia | IP pública listada en DroneBL (Class 6, fuente CBL) |
Herramientas y recursos usados
| Técnica | Herramienta | Objetivo |
|---|---|---|
| Reputación de IP | DroneBL, Spamhaus | Confirmar el listado |
| Descubrimiento | arp-scan, nmap, mDNS |
Mapear la LAN |
| Acceso al device | adb (root) |
Enumeración autorizada |
| Forense volátil | /proc/net/tcp, netstat, ss |
Socket → proceso |
| Análisis de artefactos | file, strings, aapt, readelf |
Identificar binarios y APKs |
| Sinkhole | iptables DNAT + listener Python |
Capturar el protocolo C2 |
| Ingeniería inversa | Unicorn, Capstone, Ghidra | Entender el empaquetado |
| Captura | tcpdump |
Evidencia de red |
1. La señal: una IP en una lista negra
La primera pista no fue técnica, fue administrativa. Un aviso indicaba que la IP pública del hogar estaba listada en DroneBL, incidente con clasificación Class 6 — Unknown worm or spambot, alimentada por CBL.
Esa clasificación casi nunca es un falso positivo accidental: la CBL detecta patrones de spam, escaneos o proxying saliente. En una casa con tres móviles, una PC y una TV, la pregunta era obvia: ¿quién está generando ese tráfico?
2. Barrido de la red local
Con la máquina de análisis en la misma LAN, lo primero es tener inventario real, no el que uno cree tener.
1
2
sudo arp-scan -I eth0 --localnet
sudo nmap -sn 192.168.18.0/24 -oG hosts
El barrido devolvió router, un Ubiquiti, la PC de análisis, los móviles y 192.168.18.11, un
dispositivo que se anunciaba por mDNS como reproductor AirPlay (ATV_11). Era el Android TV.
El paso siguiente fue mirar qué puertos exponía:
1
sudo nmap -p- -sS -sV -Pn 192.168.18.11 -oN tv_ports
Resultado incómodo: 5555/tcp abierto, el puerto de ADB, sin autenticación. En la LAN.
Cualquier dispositivo de la red podía conectarse con adb connect y obtener shell.
1
2
3
adb connect 192.168.18.11:5555
adb root
adb shell
ADB expuesto en red es una de las puertas traseras más comunes en TV boxes baratas. No es una vulnerabilidad exótica: viene activado de fábrica en muchos firmwares.
3. Dentro del dispositivo
Ya con shell root, el objetivo es responder tres preguntas: qué corre, qué habla con Internet y qué sobrevive a un reinicio.
1
2
3
ps -A -o PID,USER,NAME,ARGS
cat /proc/net/tcp
netstat -antp
Tres procesos destacaron de inmediato:
| Proceso | PID | Rol |
|---|---|---|
/system/bin/pandoraspearrk |
3805 | Backdoor principal, mantiene el C2 |
/data/data/com.waxrain.airplaydmr/files/wdog2 |
4454 | Proxy + ejecución remota root |
/system/bin/supervisord |
3793 | Watchdog root que relanza el backdoor |
Y la conexión que lo confirmaba todo:
1
192.168.18.11:60267 -> 45.56.117.139:9999 ESTABLISHED (/system/bin/pandoraspearrk)
Un binario en /system/bin (partición de solo lectura en teoría), corriendo como root, con una
conexión persistente al puerto 9999 de un VPS en Linode. No había mucho margen para interpretaciones.
3.1 Artefactos encontrados
| Fichero | Función |
|---|---|
/system/bin/pandoraspearrk |
Backdoor (Android.Pandora.2) |
/system/bin/supervisord |
Watchdog empaquetado con UPX |
/data/supervisord.tar.gz |
Kit de infección completo |
/data/data/com.waxrain.airplaydmr/files/wdog2 |
Proxy / exec root |
/data/data/com.waxrain.airplaydmr/lib/libpreloader.so |
Payload auxiliar (timestamp falso: 1979) |
/data/local/tmp/server.apk |
APK com.cetusplay.remoteservice |
/system/etc/hosts |
DNS secuestrado (58 dominios) |
El kit de persistencia era el hallazgo más interesante:
1
2
adb shell tar tzvf /data/supervisord.tar.gz
# pandoraspearrk supervisord s.conf curl arp tar
Y dentro, s.conf:
1
2
3
4
5
6
7
[supervisord]
logfile=/data/sp.log
pidfile=/data/sp.pid
[program:pd]
command = /data/pandoraspearrk
autorestart=true
Ahí está el diseño. supervisord corre como root y relanza pandoraspearrk cada vez que muere.
En sp.log había 2.012 reinicios entre el 2025-07-03 y el 2026-09-27. Matar el proceso a mano no
sirve de nada: vuelve en segundos.
4. El secuestro de DNS
El /system/etc/hosts había sido reescrito con 58 dominios apuntando a 37 IPs de la
infraestructura. No es un detalle menor: con eso el malware se asegura de que, incluso si el usuario
cambia el DNS del router, el dispositivo siga resolviendo los dominios del atacante a sus propias IPs.
1
adb pull /system/etc/hosts hosts.modificado
Un extracto representativo:
1
2
3
4
5
6
7
8
173.255.212.243 apz.pdonno.com
173.255.212.243 ok3.mflve.com
173.255.212.243 pcn.panddna.com
139.144.201.58 pandorabackup-1322908155.us-west-2.elb.amazonaws.com
23.12.198.13 ageniustv1.cc
54.149.89.70 eumk.wak2p.com
71.19.250.244 www.htvmarket.com
127.0.0.1 pcdnbus-bk.a2k3v.com
Los dominios no son aleatorios: pertenecen a un ecosistema de IPTV/streaming pirata
(agenius*, htvmarket, qicicloud, wak2p). El negocio y el vector de infección son la misma cosa.
La línea
127.0.0.1 pcdnbus-bk.a2k3v.comapunta al downloader a localhost: el malware bloquea su propia vía de actualización una vez que ya no la necesita, para que nadie más lo actualice.
5. El C2
Cuatro IPs respondían en el puerto 9999:
1
2
3
4
45.56.117.139 Linode/Akamai (US) <- conexión observada
173.255.212.243 Linode/Akamai (US)
173.255.221.98 Linode/Akamai (US)
71.19.252.14 eSecureData (CA)
Y una segunda capa de infraestructura en velia.net (DE), GorillaServers (US) y un ELB de AWS con
el nombre pandorabackup-.... La separación entre “C2 principal” y “backup” es deliberada: si tumbas
uno, el bot tiene a dónde volver.
6. Sinkhole local: cortar el mando sin tocar al atacante
Aquí hay una decisión ética y legal importante. No hay que conectarse al C2 del atacante. Lo que
hice fue redirigir el tráfico del propio dispositivo hacia una máquina de mi red, usando iptables
en el propio TV (root) con DNAT:
1
2
3
4
iptables -t nat -A OUTPUT -p tcp -d 45.56.117.139 --dport 9999 -j DNAT --to-destination 192.168.18.14:9999
iptables -t nat -A OUTPUT -p tcp -d 173.255.212.243 --dport 9999 -j DNAT --to-destination 192.168.18.14:9999
iptables -t nat -A OUTPUT -p tcp -d 173.255.221.98 --dport 9999 -j DNAT --to-destination 192.168.18.14:9999
iptables -t nat -A OUTPUT -p tcp -d 71.19.252.14 --dport 9999 -j DNAT --to-destination 192.168.18.14:9999
El sinkhole es un listener TCP que solo escucha:
1
2
3
4
5
6
7
8
9
10
11
12
13
import socket
srv = socket.socket()
srv.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
srv.bind(("0.0.0.0", 9999))
srv.listen(5)
print("sinkhole 9999 escuchando")
while True:
conn, addr = srv.accept()
data = conn.recv(4096)
print(addr, len(data), data[:80])
conn.close()
Efecto secundario útil: mientras las reglas están puestas, el bot no alcanza su C2 real. Es contención sin modificar el malware ni atacar a nadie.
1
2
# revertir siempre al terminar
iptables -t nat -D OUTPUT -p tcp -d 45.56.117.139 --dport 9999 -j DNAT --to-destination 192.168.18.14:9999
Esta contención es temporal y reversible. No sustituye a desconectar el equipo. Si tienes dudas legales sobre tocar el dispositivo, habla con tu CERT antes.
7. El protocolo C2, recuperado
Aquí está la parte que más valor tiene del análisis. Con el sinkhole pasivo se capturó el handshake completo:
- Mensajes de 1024 bytes exactos.
- Payload útil de 60 caracteres, relleno de
0x00hasta 1024. - Codificación base64 con alfabeto propio:
A-Za-z0-9./(usa.en vez de+). - Al decodificar: 45 bytes = 36 B de ID estable + 9 B de nonce.
El ID estable es oro para detección: es el mismo en todos los beacons del dispositivo.
1
2
ID : d92936f3e05db722f56867f063ece82a9d3f304edc934f05faa1f4c67378d743a8f5f4f4
nonce : cambia en cada beacon (9 bytes)
Y el comportamiento temporal:
1
2
3
19:02:27 connect -> beacon inmediato
19:04:32 close (~125 s esperando respuesta)
19:05:08 reconnect
Un beacon cada ~2,5 minutos, con ~2 minutos de espera. Si el servidor no contesta, cierra y reintenta.
7.1 Experimentos de interacción
Con el sinkhole activo probé qué acepta el bot como respuesta del C2:
| # | Respuesta enviada | Tamaño | Reacción |
|---|---|---|---|
| 0 | echo de su propio paquete | 1024 B | cierra en ~20 ms |
| 1 | 1024 bytes de ceros | 1024 B | espera >120 s (equivale a “sin comando”) |
| 2 | su token ASCII devuelto | 60 B | cierra a los ~30 s |
| 3 | "OK" |
2 B | cierra a los ~30 s |
Conclusiones:
- El bot valida el contenido de la respuesta: un echo se rechaza al instante.
- Cualquier respuesta menor de 1024 B provoca un timeout corto.
- El C2 real responde con 1024 B con un formato concreto (probablemente cifrado).
- Para replicarlo hay que reversar el binario. Eso quedó pendiente.
8. Ingeniería inversa del binario
pandoraspearrk es un ELF de 32 bits, ARM, estáticamente enlazado y stripped. Lo interesante
aparece al mirar la estructura:
1
readelf -h pandoraspearrk
- Entropía global 7,85 / 8 → empaquetado o cifrado.
- Tabla de secciones eliminada (solo
.shstrtaby.ARM.attributes). - Cabecera ELF no estándar: dos segmentos
LOAD, ambos desde offset 0. - Entry point en
0x38584c, fuera de cualquier sección normal.
El entry point es un stub de descompresión: lector de bits, copia de literales, copia de coincidencias. Un unpacker clásico.
En ejecución, /proc/<pid>/maps confirma el patrón:
1
2
3
aaac3000-aacee000 r-xp (código descomprimido, ~2.3 MB, sin fichero)
aacee000-aad00000 r--p (rodata)
aad00000-aad29000 rw-p (datos)
El fichero en disco solo aparece mapeado como una página r--p. Todo el código real se
descomprime en memoria anónima. Por eso los antivirus basados en firma de fichero tienen tan poco
que rascar.
8.1 Lo que no funcionó
Intenté volcar memoria desde el propio dispositivo:
1
2
dd if=/proc/3805/mem of=/data/dump.bin bs=1M
# -> I/O error
Falla también para init, con CapEff completo y SELinux en Permissive: no es DAC ni SELinux, es
una restricción del kernel. Core dumps deshabilitados (Max core file size = 0). Los tombstones que
había no eran del malware.
8.2 Emulación con Unicorn
Construí un emulador ARM (Unicorn + Capstone) que mapea los segmentos, implementa syscalls
(open/read/lseek/mmap/brk/mprotect) y ejecuta desde el entry point. En la v5 reconstruí la pila
inicial estilo kernel (argc/argv/envp/auxv con AT_PHDR, AT_ENTRY, AT_PAGESZ, AT_EXECFN),
que es lo que el loader PIE necesita.
El loader avanzó: open(/proc/self/exe), mmap2, mprotect, readlink… pero no completó el
desempaquetado limpio. Es un loader PIE propio, de los que usan los packers de Android. Se extrajo
una segunda etapa de ~2968 B con una rutina de copia inversa y escritura de = y espacios compatible
con base64, pero el formato del paquete de comando del C2 quedó sin recuperar.
Conclusión honesta: con Unicorn puro no merece la pena forzarlo. Lo correcto es Qiling con rootfs
ELF32, o ejecutar el loader en un laboratorio aislado con qemu-arm y red deshabilitada. El objetivo
defensivo principal ya estaba cubierto: el protocolo de registro está íntegramente caracterizado.
9. Quién está detrás
El binario corresponde a Android.Pandora.2 (nomenclatura Dr.Web) y encaja con el malware
pandoraspear + downloader pcdn del grupo Bigpanzi:
- Sindicato de ciberdelito activo desde ~2015, con origen en China/sudeste asiático.
- Reportes públicos hablan de ~800.000 nodos activos diarios.
- QiAnXin Xlabs observó ~170.000 dispositivos al interceptar dos de sus dominios C2.
- El FBI publicó un PSA en 2025 sobre BADBOX 2.0 en dispositivos IoT domésticos.
El modelo de negocio es claro: proxy residencial (vender tu IP y tu ancho de banda a terceros), nodos DDoS y fraude publicitario con apps llenas de SDKs de anuncios.
10. Línea temporal
| Fecha | Evento |
|---|---|
| 2017-08-03 | Build del firmware (Android 5.1.1) |
| 2025-04-21 | Instalación de Downloader + aparición de supervisord.tar.gz |
| 2025-07-03 → 2026-09-27 | Actividad del backdoor (2.012 reinicios) |
| 2025-12-26 | mtime de pandoraspearrk (actualización del backdoor) |
| 2026-01-24 | IP listada en DroneBL (CBL) |
| 2026-10-05 | Análisis, contención y recolección de evidencia |
Ocho meses entre el listado en la lista negra y el análisis. Ese es el coste real de no mirar.
11. Impacto
- Uso fraudulento de la IP y el ancho de banda: la IP pública se alquilaba a terceros.
- Reputación comprometida: listas negras, posible bloqueo de servicios legítimos.
- Riesgo de pivote: un dispositivo con root en la LAN es una posición de salida hacia el resto.
- Exposición de la cuenta de Google asociada al dispositivo.
12. Qué hice mal (y qué haría distinto)
Ser honesto con los errores es la parte útil de un writeup:
- Compré una TV box barata con Android 5.1.1 en 2017 y nunca la actualicé. Sin parches de seguridad desde hace casi una década. Un dispositivo sin soporte no es un electrodoméstico, es deuda técnica.
- Instalé APKs de fuentes externas para tener “más canales”. Ese es el vector.
- Nunca revisé los puertos abiertos de mi propia red. ADB en 5555 llevaba años ahí.
- Tardé meses en reaccionar al aviso de la lista negra.
Lo que sí funcionó: aislar el dispositivo primero, recolectar evidencia con hashes antes de tocar
nada, y no intentar “limpiar” un sistema con root en /system.
13. Cómo saber si tu dispositivo está infectado
Checklist rápido, en orden:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 1. ¿Hay ADB abierto?
nmap -p 5555 192.168.1.0/24
# 2. Procesos raros como root
adb shell ps -A -o PID,USER,NAME | grep -Ei "supervisor|pandora|pcdn|wdog"
# 3. Conexiones al puerto 9999
adb shell cat /proc/net/tcp | grep -i ":270F" # 9999 en hex
# 4. ¿hosts secuestrado?
adb shell cat /system/etc/hosts
# 5. ¿Watchdog persistente?
adb shell ls -l /data/supervisord.tar.gz /data/sp.log
Señales de alarma en el día a día:
- La TV va lenta sobre todo cuando está “apagada” (en standby sigue encendida por dentro).
- Consumo de subida alto en el router sin explicación.
- La IP aparece en DNSBL (DroneBL, Spamhaus).
- Dominios de IPTV pirata resolviendo a IPs que no son las del proveedor.
14. Remediación: lo que realmente funciona
- Desconectar de Internet ahora, aislar en el router o VLAN.
- Bloquear los IOC en router, Pi-hole o firewall.
- Desactivar UPnP y revisar el port forwarding (nunca exponer 5555).
- No confiar en borrar ficheros: el watchdog y las modificaciones en
/systemsobreviven. factory resetno garantiza nada si el malware tiene root en/systemo si el firmware ya venía infectado.- Reflashear firmware limpio de fuente oficial o reemplazar el equipo. En mi caso, reemplazar.
- Cambiar credenciales de los servicios usados en el dispositivo.
- Revisar el resto de la LAN.
- Solicitar la retirada de la lista negra después de confirmar el cese del tráfico.
15. IOC principales
1
2
3
4
5
6
7
8
9
10
11
12
13
14
# C2 activos (puerto 9999)
45.56.117.139 173.255.212.243
173.255.221.98 71.19.252.14
# Infra secundaria
207.38.87.205 209.239.115.206 199.189.87.86
209.126.116.211 50.30.37.108 192.200.112.10
139.144.201.58
# Dominios (extracto)
panddna.com pdonno.com mflve.com bv1tpvdco.com
ageniustv1.cc ageniusapp.cc ageniusvod.cc
htvmarket.com qicicloud.xyz wak2p.com tenlsi1.club
pandorabackup.lftpdo.com pcdnbus-bk.a2k3v.com pcdnfuc.ou2sv.com
| Fichero | SHA-256 |
|---|---|
pandoraspearrk |
52c4d4fc0d883bd9841db4b7673f26f28ccb80a4c959824349453960b6fcb34c |
supervisord |
17ea3b1459382a4f1812e1bb6d17f9b68842107e5ca46019b5b97e77267ddd19 |
supervisord.tar.gz |
4379cd89bd87155c09c1e799586df8f6b4bdd8c1d26e3cdb361d4cadaef40bf8 |
wdog2 |
e4cd104694f1676791cd23747b532320e21c0012ed8998ab37dfaaedf758d770 |
libpreloader.so |
de351e0e3bca13425d1472b39dd9c0a88bfe7ba503be0e330a024e76684d2251 |
server.apk |
cb483cdb56de41523495e128df22489c53863a1e856ebd7d444bafd0b07be4a8 |
16. Conclusiones
- El IoT doméstico es superficie de ataque real. Una TV box de 30 dólares con Android de 2017 puede convertir tu casa en infraestructura criminal sin que notes nada.
- Los avisos de listas negras son señales de incidente, no spam administrativo.
- La persistencia es el verdadero problema. El malware no es sofisticado: es terco.
- El sinkhole pasivo es la técnica más rentable para entender un C2 sin cruzar líneas rojas.
- El empaquetado gana la partida a corto plazo. Si no puedes desempaquetar, documenta el protocolo de red: eso ya te permite detectar y contener.
Y la lección que me llevo: si un dispositivo ya no recibe parches, no es un dispositivo, es un riesgo con enchufe.
Referencias
- QiAnXin Xlabs — Bigpanzi Exposed: The Hidden Cyber Threat Behind Your Set-Top Box
- Dr.Web — Android.Pandora.2
- BleepingComputer — Bigpanzi botnet infects 170,000 Android TV boxes
- FBI IC3 PSA (2025-06-05) — Home Internet Connected Devices (BADBOX 2.0)
- Kaspersky — Is your TV box renting out your network?
- DroneBL — https://dronebl.org
Gracias por leer.
Comments powered by Disqus.