ES EN
Índice

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).

la pista
# 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.net

La 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:

config descifrada (XOR 0x54)
# 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 vez

La 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:

formato de las órdenes del C2
[ 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 ATAQUE

La 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:

IDTipo de ataque
0UDP flood (raw)
1UDP flood, paquete pequeño (juegos/VSE)
2DNS flood
3TCP flood (raw)
4TCP flood (ACK/PSH)
5TCP flood (STOMP)
6GRE flood (GRE-IP)
7GRE flood (GRE-ETH)
8UDP-PLAIN flood
9STD flood
10TCP flood (banderas configurables)
11UDP flood multi-socket
12TCP con datos
13TCP connection flood
14Shell inversa hacia el C2 — no es un flood
15HTTP 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.

Dónde paroSé que existen estos vectores y cómo reconocerlos en el tráfico; ahí me quedo. Catalogar el arsenal es defensa; explicar cómo se dispara cada flood sería repartir armas — y esa es la línea de esta bitácora. Con el 14, lo mismo: cuento que la shell inversa está ahí y qué hace. El saludo que la abre, no.

05La pista que conecta con el pasado

Ghidra mostrando la función de resolución del C2 de Mirai, con el dominio kappadocia.net y la IP de reserva 141.98.10.50
El plan B del bicho, en Ghidra. Esta función intenta resolver kappadocia.net por DNS; y si le devuelve vacío —dominio caído, DNS bloqueado, lo que sea— se va directa a 141.98.10.50, que lleva escrita dentro. Es decir: tumbarle el dominio no lo desconecta, porque la IP la sabe de memoria. Cuando el DNS sí responde y da varias direcciones, elige una al azar: el decompilador termina esa asignación con un % —el resto de una división— que reparte la elección entre las direcciones devueltas. En la captura queda justo al filo del panel, medio cortado. Nada de esto se ve en el strings; hace falta leer el código.

OSINT pasivo sobre el C2, sin tocar la máquina. El kappadocia.net resuelve a 141.98.10.50. Y ahí salta la sorpresa:

Misma vecindad que el Capítulo 2El C2 del XorDDoS del Capítulo 2 era 141.98.11.51. Este Mirai llama a 141.98.10.50. Cuando lo escribí dije que compartían la misma red 141.98.0.0/16, que es una vecindad muy ancha. Se puede apretar bastante más.

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)

TipoValor
FamiliaMirai · variante "milnetv4" · marcador ".anime"
C2kappadocia.net · 141.98.10.50 (AS209605, igual que el C2 del Cap. 2)
Cifrado configXOR · 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)
Capacidad16 métodos (15 de DDoS + 1 shell inversa al propio C2) · hasta 15 ataques concurrentes
Puertos que cierra32, entre ellos 48101 (cerrojo de instancia de Mirai) · 23 · 80 · 21/20 · 53413 · 37215 · 52869
Estado a 11 de septiembre de 2026kappadocia.net ya no resuelve, y el servidor de reparto del Capítulo 3 ha desaparecido. Pero 141.98.10.50 sigue viva. Es, en vivo, lo que este mismo capítulo predijo tres semanas antes: tumbarle el dominio no lo desconecta, porque la IP la lleva escrita dentro.

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