Mirai · Capítulo 4
Mirai al descubierto: 16 métodos y un XOR de un byte
El bot del capítulo anterior guardaba su config cifrada y sus órdenes en clave. Con Ghidra sale todo: el descifrado (un XOR ridículo), el nombre real de la botnet, el protocolo de su C2 y su arsenal completo — sin repartir armas.
En el Capítulo 3 cacé un Mirai multiarquitectura y me llevé los doce binarios. En sus strings asomaba el C2 en claro, pero el resto de su configuración y todas sus órdenes estaban cifradas. Toca abrirlo con Ghidra.
Sacamos a quién llama el bot y de qué es capaz para poder detectarlo y bloquearlo. La mecánica de los ataques —cómo se lanza cada flood— no se cuenta: se cataloga el arma, no se entrega.
01El cifrado: un solo byte
El XorDDoS del Capítulo 2 usaba una clave XOR de 16 bytes y hubo que decompilar la rutina para dar con ella. Este es de otra liga — para abajo. Mirando las strings cifradas ya se ve el truco: todas terminan en T (byte 0x54).
# cadena cifrada tal cual sale del binario:
?5$$50;7=5z:1 T
# XOR cada byte con 0x54:
?5$$50;7=5z:1 T ⊕ 0x54 = kappadocia.netLa config lleva terminadores nulos (\0) al final de cada cadena, y 0 XOR clave = clave. Ese T repetido era el \0 delatando la clave: 0x54. Un XOR de un solo byte. Ni siquiera hizo falta encender el decompilador — se descifra desde strings.
Un apunte, porque puede chirriar: en el capítulo anterior ese mismo dominio ya asomaba a pelo en el strings. Lo que no sabíamos entonces es que también vive aquí dentro, en la tabla cifrada — y es esta copia, la que el bicho lee de verdad al arrancar, la que acabamos de abrir.
02La config al desnudo
Aplico XOR 0x54 a toda la tabla de cadenas y sale limpia. Y con ella, la identidad del bicho:
# identidad
milnetv4 # nombre/versión de la botnet
.anime # marcador delator del linaje Mirai
# C2
kappadocia.net · 141.98.10.50
# comportamiento
/dev/watchdog · /dev/misc/watchdog # desactiva el watchdog
/proc/ /exe /maps /proc/net/tcp /proc/net/route
/etc/resolv.conf · nameserver # resuelve el C2
bash
# ataque
Source Engine Query # la plantilla del método 1 (VSE)
assword # sin la P: caza «Password:» y «password:» a la vezLa firma es inconfundiblemente Mirai: la cadena .anime (marcador de un linaje conocido) y el ataque al /dev/watchdog — que desactiva el reinicio automático del aparato para que no pueda limpiarse solo. El barrido de /proc le sirve para matar botnets rivales y averiguar su propia IP. La variante se llama a sí misma milnetv4.
Un apunte que en su día no hice, y que corrijo aquí: la tabla de cadenas tiene dos mitades, y no todo lo que el bicho lleva dentro está cifrado. sshd, dropbear y /bin/sh viven en la parte en claro; lo de arriba pasa por la rutina XOR. bash es el único que aparece en las dos. No es un descuido del autor: son dos mecanismos distintos — lo que consumen directamente el exec() y el módulo que mata procesos no necesita descifrarse. Y refuerza el apunte de la sección anterior, porque kappadocia.net es justo de lo que está en las dos.
03El protocolo del C2
Con la config clara, decompilé en Ghidra el bucle principal. El bot se conecta al C2, se registra (arquitectura + milnetv4 + .anime) y manda un latido periódico. Las órdenes llegan con un prefijo de longitud:
[ 2 bytes big-endian = longitud ][ payload ]
└ se descarta si la longitud > 0x400
# los primeros 4 bytes deciden el tipo:
0xFE → matar TODOS los ataques
0xFD → matar UN ataque por su id
otro → orden de ATAQUELa orden de ataque trae la duración, el id del vector (menor que 16), la lista de objetivos (cada uno como IP + prefijo de red) y una lista de opciones (puerto, tamaño…). Por cada ataque, el bot hace fork() y guarda el proceso hijo en una tabla — así mantiene hasta 15 ataques a la vez y puede matarlos individualmente con la orden 0xFD. Es, pieza por pieza, la arquitectura de Mirai.
Y ahí está el porqué de que las dos órdenes de control midan distinto. La orden es una palabra de cuatro bytes, así que 0xFE —«mátalos todos»— ocupa cuatro y no necesita nada más. 0xFD ocupa ocho porque lleva pegado detrás el identificador del ataque que hay que parar. La asimetría no es un capricho del formato: es que una de las dos órdenes tiene que decir a cuál.
04El arsenal: 16 métodos
El bot registra sus métodos de ataque en una tabla {función, id}. Recuperé las 16 entradas y clasifiqué cada una por el tipo de socket que abre y las cabeceras que fabrica. Lo presento como inventario defensivo — qué sabe hacer, para poder reconocerlo — sin entrar en cómo se ejecuta cada uno:
| ID | Tipo de ataque |
|---|---|
| 0 | UDP flood (raw) |
| 1 | UDP flood, paquete pequeño (juegos/VSE) |
| 2 | DNS flood |
| 3 | TCP flood (raw) |
| 4 | TCP flood (ACK/PSH) |
| 5 | TCP flood (STOMP) |
| 6 | GRE flood (GRE-IP) |
| 7 | GRE flood (GRE-ETH) |
| 8 | UDP-PLAIN flood |
| 9 | STD flood |
| 10 | TCP flood (banderas configurables) |
| 11 | UDP flood multi-socket |
| 12 | TCP con datos |
| 13 | TCP connection flood |
| 14 | Shell inversa hacia el C2 — no es un flood |
| 15 | HTTP flood (capa 7) |
Un catálogo de manual: UDP (crudo y plano), DNS, GRE, varias formas de TCP y un flood HTTP de capa 7. La evidencia dura viene del propio código — el tipo de socket y el protocolo que pide no dejan lugar a dudas. Doce arquitecturas, un mismo arsenal: la tabla es idéntica en todos los binarios, prueba de que salen de una única fuente. Y luego está el 14, que no encaja en ese catálogo.
Los quince primeros hacen lo mismo con distinto envoltorio: mandar paquetes contra alguien. El 14 abre una shell inversa: prepara una sesión nueva, engancha entrada, salida y errores a un socket, y lanza un intérprete de órdenes. Eso no inunda a nadie — eso es alguien sentándose en la máquina.
Y lo mejor es a dónde llama. No a una dirección misteriosa: al mismo sitio de siempre. Marca la estructura que el bicho rellena al arrancar cuando resuelve su central — la que sale del XOR 0x54 de la sección anterior. Misma IP, mismo puerto, misma puerta. Cuando el operador quiere, el bot que lleva todo el rato hablando con su C2 le abre una /bin/sh por el mismo canal.
Un Mirai clásico es un cañón: tú le dices a quién disparar y dispara. Este trae además una silla.
Y si has ido siguiendo los números, hay algo más que mirar: en qué orden se registran. Es este: 0, 1, 2, 8, 3, 4, 5, 10, 6, 7, 9, 11, 12, 13, 14, 15.
Uno ocupa en la fila un sitio que no se corresponde con su número: a udpplain le han cambiado la etiqueta sin moverlo de su hueco en la cola. El tcpxmas del octavo puesto es otra cosa — no es un veterano renumerado, es nuevo, y se ha quedado un número que alguien dejó libre. Eso es lo que se ve, y es lo que sostiene la conclusión: no es un Mirai recortado, es uno con la tabla remapeada y estirada. Estirada por arriba, además, que es donde viven el 11, el 12, el 13, el 14 y el 15.
La huella, por tanto, no está en lo que falta —no falta nada—: está en el orden, y en que arriba del todo, donde el original no llegaba, alguien metió una silla.
He abierto el código filtrado de 2016. Allí la tabla se llena con diez métodos, en este orden: 0, 1, 2, 9, 3, 4, 5, 6, 7, 10. Quítale a la nuestra los seis añadidos y queda esa misma secuencia, clavada. Cambian dos etiquetas: udpplain baja del 9 al 8 —un número que en el original no usaba nadie— y el HTTP sube del 10 al 15, aunque sigue entrando el último. Los dos números que quedan sueltos, el 9 y el 10, se los quedan métodos nuevos.
Y la firma está en una manía. En el original, udpplain se registra el cuarto, fuera de orden, por puro capricho de quien lo escribió en 2016. Este bicho, diez años después, lo sigue registrando el cuarto. Eso no se reinventa por casualidad: viene de ahí.
Y hay una última cosa en el inventario que tampoco sirve para atacar a nadie. El bot lleva un módulo que, al arrancar, cierra treinta y dos puertos de la máquina en la que acaba de entrar. Entre ellos el telnet, el HTTP y el FTP; una colección de puertos de puertas traseras e IRC que usan otras botnets; y tres muy concretos — el 53413 de los Netcore, el 37215 de los Huawei HG532 y el 52869 de los Realtek. Los tres son puertas conocidas de entrada a cacharros de red.
El efecto es que el aparato queda más difícil de infectar. Para otros.
Y el primero de esa lista es el que más dice: el 48101, que es el puerto que Mirai usa como cerrojo de instancia única — el que un Mirai abre para que no se le cuele encima otra copia de sí mismo. Este mata lo que esté escuchando ahí. Es decir: va a por otros Mirai.
05La pista que conecta con el pasado

OSINT pasivo sobre el C2, sin tocar la máquina. El kappadocia.net resuelve a 141.98.10.50. Y ahí salta la sorpresa:
Aquel dominio del XorDDoS no siempre apuntó al 11.51: en junio resolvía a 141.98.10.115. O sea que durante un tramo las dos familias tuvieron su centro de mando en el mismo /24, el 141.98.10.0/24. Y las tres direcciones —la del Mirai y las dos del XorDDoS— están en el mismo sitio: AS209605. Dos familias distintas, dos capturas separadas, y no una esquina de internet: un pasillo. Es exactamente el tipo de hilo que solo aparece cuando guardas y comparas todo lo que cazas.
06Indicadores (IOCs)
| Tipo | Valor |
|---|---|
| Familia | Mirai · variante "milnetv4" · marcador ".anime" |
| C2 | kappadocia.net · 141.98.10.50 (AS209605, igual que el C2 del Cap. 2) |
| Cifrado config | XOR · clave de 1 byte 0x54 |
| Protocolo C2 | órdenes con prefijo de longitud 2B BE · control 0xFE (4 B) / 0xFD (8 B, con el id del ataque) |
| Capacidad | 16 métodos (15 de DDoS + 1 shell inversa al propio C2) · hasta 15 ataques concurrentes |
| Puertos que cierra | 32, entre ellos 48101 (cerrojo de instancia de Mirai) · 23 · 80 · 21/20 · 53413 · 37215 · 52869 |
Y con esto se cierra la familia Mirai — de la caza en caliente al arsenal sobre la mesa, todo para entenderlo y poder pararlo. Con una rima que no esperaba: en el Capítulo 1, el XorDDoS renombraba wget y curl para que ninguna otra botnet pudiera descargar nada en esa máquina. Este cierra treinta y dos puertos, el del cerrojo de Mirai incluido. Dos familias que no se parecen en nada haciendo lo mismo: marcar territorio. Y el aparato de la víctima, de paso, un poco más seguro — para todos menos para el que ya está dentro.
Continuará — el cebo sigue encendido. Cuando el próximo bicho traiga algo nuevo, habrá quinto capítulo. 🍯
Comentarios