ES EN
Índice

Cling · Capítulo 19

¿Un Mirai que no sabía atacar?

Bajé los tres binarios del capítulo anterior esperando el arsenal de DDoS de siempre. No estaba: ni una función de ataque. Lo que hace en su lugar —y, sobre todo, el canal por el que le llegan las órdenes— es lo más raro que ha entrado por el cebo. Un bicho pensado, de arriba abajo, para que no se le vea.

En el capítulo anterior cacé la infección: una escopeta multiarquitectura, nombres que rotan, una oleada de binarios con hashes que cambian a cada poco. VirusTotal lo canta «Mirai», todo apuntaba a otro bot de DDoS — así que lo abrí con Ghidra buscando su arsenal de ataque. No hay ninguno. Y esa ausencia es el capítulo entero.

01No sabe atacar

Un bot de la escuela Mirai lleva dentro una tabla de métodos de ataque —attack_udp, attack_tcp, floods de todo tipo— y un protocolo de C2 con opcodes para dispararlos. Fui a buscarlo y no encontré nada: ni la tabla de métodos de ataque, ni un solo flood de ningún tipo, ni una credencial o un escáner de Telnet, ni un opcode de ataque en su C2.

Ni una función de ataque. No ofuscadas, no escondidas: ausentes. Y no es que no mirara bien: en todo el binario hay un solo socket crudo —el que hace falta para fabricar un paquete a mano— y no es de ningún flood, es del escáner: el que dispara sus sondeos con el puerto de origen clavado en 9999. Ni uno más. Un bot de DDoS sin forma de hacer daño es un contrasentido. Entonces, ¿qué es? Lo dice su propio bucle de mando: lee el byte de la orden y salta a una de siete ramas —ni una más—, y ninguna es un ataque.

ghidra · el bucle de mando despacha la orden (recortado)
switch (orden - 1) {                 // 7 ramas = 4 acciones + sus 3 apagados
  case 0: FUN_080491e7();            // 1 · shell (system con lo que descargue)
  case 1: …  FUN_0804a77c();         // 2 · escáner + cargador
  case 2: …                          // 3 · apaga el escáner (cierra sus sockets, sin función propia)
  case 3: …  FUN_08049b45();         // 4 · relay TCP inverso
  case 4: …                          // 5 · apaga el relay
  case 5: …  FUN_08049469();         // 6 · túnel SOCKS
  case 6: …                          // 7 · apaga el túnel
}                                    // 4 funciones, ni una de ataque — no hay flood, no hay una octava rama

Las cuatro órdenes que de verdad hacen algo (las otras tres solo las apagan):

  • Un shell remoto. Una orden abre una conexión, lee lo que le manden y lo pasa a system(). Ejecución de comandos a la carta.
  • Un escáner con cargador. Busca víctimas nuevas —por su catálogo de exploits, no por telnet— y les inyecta el bicho: es su propia forma de propagarse (a fondo en el §06).
  • Un relay TCP inverso. Escucha en un puerto y reenvía cada conexión hacia otra dirección: hace de tubería.
  • Un túnel multiplexado tipo SOCKS. Protocolo de tramas propio, hasta 254 canales a la vez, TCP y UDP.

De las cuatro, el escáner es solo cómo consigue más nodos; las otras tres —shell, relay y túnel— son el negocio: no tumbar servicios, sino dar acceso y dar salida. Es un nodo de proxy — una tubería por la que el tráfico de otro sale a internet con la cara de un cacharro doméstico infectado. Los repositorios la etiquetan Ngioweb —el motor del servicio de proxy residencial NSOCKS—, aunque esa etiqueta tiene matices que veremos en el hilo. Nada de eso lo saben los motores que lo llaman «DDoS».

Lo que desmonta este capítuloLa suposición de que un bicho de IoT que se propaga con los exploits de siempre y que los AV llaman «Mirai» es, por defecto, una botnet de DDoS. Este no lo es, y lo demuestra el código: cero funciones de ataque, y en cambio un túnel SOCKS con 254 canales. Toda la fauna IoT del blog —Gafgyt, Mirai, Condi— viene a tumbar. Este viene a colarse. Mismos aparatos, mismos exploits, negocio distinto.

02Las siete órdenes

La tabla de saltos del C2 tiene siete entradas. Las recuperé una a una:

OrdenQué hace
1Shell remoto: descarga un comando de una IP:puerto y lo pasa a system()
2Arranca el escáner/propagador. La IP:puerto es el cargador que inyectará en las víctimas nuevas
3Para el escáner
4Relay TCP inverso hacia una IP:puerto (solo si el equipo tiene IP pública)
5Para el relay
6Túnel tipo SOCKS: 254 canales, abrir/datos/cerrar, TCP y UDP
7Para el túnel

La orden 2 explica un cabo suelto del capítulo anterior: el servidor de reparto no está en el binario porque llega aquí, en una orden. El operador le dice al bot desde qué IP servir los binarios a las próximas víctimas. Cargador y servidor son intercambiables sin recompilar — nada fijo, otra vez.

Y no hay nada que descifrarEn XorDDoS (cap. 2), Mirai (4) y Sysorbit (8) el primer paso era romper un cifrado. Aquí no hay ninguno: todas las cadenas van en claro, la entropía de los datos es la de un texto normal, no hay ni un bloque cifrado. La «config» de Cling es dinámica —la etiqueta llega en argv[1], todo lo demás en órdenes—, así que no hay config que sacar. Es un diseño que frustra al que abre el binario buscando el secreto: no lo lleva encima.

03El C2 que habla por teléfono

¿Y por dónde le llegan esas órdenes? Aquí está lo más raro que he visto. El bot, al arrancar, abre trece sockets UDP y le habla STUN a trece servidores distintos.

STUN es el protocolo que usan las apps de videollamada para averiguar su propia dirección pública cuando están detrás de un router. Y Cling lo habla de verdad: manda un Binding Request con la cookie mágica auténtica (0x2112A442), y de la respuesta saca su puerto público (el atributo XOR-MAPPED-ADDRESS, des-XOR con 0x2112). Trece servidores, trece puertos públicos aprendidos.

Con eso, emite su baliza de registro a los trece, cada 5 segundos. La etiqueta va delante y detrás los 13 puertos, así que el tamaño es la longitud de la etiqueta + 26 (13 × 2 bytes): con «telnet», 6 + 26 = 32 bytes.

la baliza de registro · etiqueta telnet → 32 B
74 65 6c 6e 65 74   94d7 ccb0 ad10 899a d13a b090 …
└─── "telnet" ────┘  └──── 13 puertos públicos (2 B cada uno) ─────┘
   la etiqueta          uno por cada uno de los 13 sockets

Es su ficha: le dice a quien escuche quién es (la etiqueta) y por dónde alcanzarlo (los trece puertos). Y lo bonito del disfraz: para un monitor de red, un cacharro mandando UDP a trece servidores STUN parece un teléfono haciendo videollamadas. No levanta una ceja.

04La puerta sin cerradura

Las órdenes llegan de vuelta por esos mismos trece sockets —los del STUN—, como paquetes de 20 bytes. Y su estructura tiene dos detalles que van de lo elegante a lo temerario.

una orden del C2 · con forma de cabecera STUN
+0   8 bytes   cabecera STUN (tipo, longitud, cookie mágica)  <- el bot los ignora
+8   12 bytes  el hueco del transaction ID                    <- aquí viaja, camuflada, la orden
             ────────────
             20 B = una cabecera STUN completa, sin atributos

Los veinte bytes son, exactos, una cabecera STUN completa sin atributos. Los primeros —tipo, longitud, cookie— el bot los ignora. El resto es el hueco donde STUN pone su transaction ID, un identificador que un cliente de verdad genera al azar… salvo que aquí, en ese hueco, viaja la orden. El bot solo lee un puñado de esos bytes; el resto le da igual. Por eso una orden pasa por STUN ante un cortafuegos: tiene la forma de una cabecera STUN, con su cookie en su sitio.

Ahora, cuidado con lo que esto no prueba. Al bot le basta una orden de 20 bytes: unos pocos de mando y el resto ignorado. Una Binding Response de verdad no se le parece —lleva la dirección del cliente en el atributo XOR-MAPPED-ADDRESS, así que pesa más, y repite el transaction ID de la petición que contesta—. Sería tentador concluir que las órdenes, entonces, se cazan al vuelo entre el tráfico STUN. Pero no capturé ninguna orden real —nadie llegó a comandar a mi bot—, así que esto lo deduzco del descodificador, no lo vi en el cable. Y hay trampa: como el bot ignora casi todo el paquete, el operador puede vestir la orden con un XOR-MAPPED-ADDRESS falso y un ID que imite una respuesta, y obedecería igual. La firma de red de fiar, por eso, está en lo que el bot emite —sus balizas, que veremos salir en la jaula—, no en lo que recibe, que puede disfrazarse de cualquier cosa. deducido

Elegante. Lo siguiente, no:

No comprueba quién mandaLa llamada que recibe la orden es recvfrom(fd, buf, 20, 0, NULL, NULL) — esos dos NULL al final significan «no me digas de dónde viene». No hay autenticación, ni firma, ni cifrado, ni un triste token. Cualquiera que sepa la IP pública del bot y uno de sus trece puertos puede mandarle una orden — incluida la número 1, que ejecuta comandos. El operador le puso a su bot una puerta trasera potentísima… y se dejó la llave puesta para todo el mundo.

05Preguntas quién es y te contesta «init»

Un nodo de proxy tiene un objetivo que un bot de DDoS no comparte: que no lo encuentren, para durar años en el router de alguien. Lo primero que hace Cling al arrancar es robarle la identidad al proceso número 1.

suplantar a init
cp /proc/1/stat /proc/1/status /proc/1/cmdline  /tmp/
mount --bind /tmp /proc/<su_propio_pid>

En Linux, para saber qué es un proceso miras /proc/<pid>/status. Cling monta encima los datos que copió de init. Preguntas quién es, y el kernel te contesta «init» — el proceso más intocable del sistema. Y remata: nombre de proceso en blanco (prctl(PR_SET_NAME, "")), su línea de órdenes borrada a espacios, el watchdog del hardware desactivado (para que el aparato no se reinicie y se lo lleve por delante) y, si es root, -1000 en oom_score_adj: «mata a quien quieras menos a mí».

06El escáner con modales, y el killer sin ninguno

Para conseguir más nodos, Cling escanea internet — como todos. Pero con un detalle que no había visto: antes de escanear, se cronometra. Manda mil paquetes a 8.8.8.8, mide cuánto tarda, y calcula a qué velocidad puede escanear sin ahogar la conexión de la víctima. Mirai va a toda pastilla porque le da igual durar; este se auto-estrangula para que el dueño no note que su internet va lento. Un bicho que quiere quedarse no puede permitirse cantar. Y sin embargo, en un detalle canta: todos sus sondeos salen con el puerto de origen fijo en 9999 —un escaneo normal lo deja al azar—, así que una ráfaga de SYN saliendo todos desde el 9999 hacia puertos de router es, ella sola, su firma.

Cuando encuentra un objetivo, dispara el exploit que toque, de un catálogo de ocho puertas —el repertorio habitual de router y grabador de vídeo—:

PuertoObjetivoEtiqueta
7547TR-064 / CWMP (caso Deutsche Telekom 2016). Firma User-Agent: clingwasheretr064.selfrep
52869UPnP de Realtek SDK (CVE-2014-8361)selfrep.realtek
60001JAWS (grabadores MVPower)selfrep.jaws
85 · 80 · 8080DVR TBK · Linksys «TheMoon» · B-Link/Tenda · CGI de routerselfrep.*
9034/udpinyección de comando en el Realtek Jungle SDK (CVE-2021-35394)realtek.selfrep

Casi cada puerta tiene la suya —siete etiquetas para ocho peticiones: jaws firma dos—: tr064.selfrep, selfrep.realtek… y es la que el bot reporta como argv[1] cuando se propaga por ahí. (Las dos de Realtek no son una errata mía: selfrep.realtek es el UPnP de 2014, realtek.selfrep el Jungle SDK de 2021 — el propio bicho las escribe con las palabras al revés.) Pero fíjate: telnet no está en la lista, y Cling no fuerza contraseñas ni escanea el puerto 23. Entonces, ¿cómo llegó a nuestro cebo con la etiqueta telnet (cap. 18)? Porque quien lo metió no fue Cling: fue un cargador de telnet aparte —otra pieza de la maquinaria de reparto— que forzó el 23, soltó la receta y lanzó el binario poniéndole telnet de sello. El argv[1] guarda quién lo trajo, aunque el bicho no sepa abrir esa puerta. Es la división de trabajo del ecosistema: un loader propaga, el bot es la carga.

Detalle con gracia de su propagación propia: cuando Cling sí entra —por el UPnP de Realtek— deja en la tabla NAT del router un mapeo etiquetado syncthing, disfrazado de un programa legítimo.

La nota fea — y lo que delataNo todo son modales. Cling lleva un killer a lo bruto: un bucle que cada segundo mata todo proceso que no sea busybox ni viva en /tmp, sin distinguir — en un aparato de verdad puede cargarse servicios legítimos del cacharro. Y aquí hay algo más que fealdad: es una contradicción. Un proxy que quiere durar años en casa de alguien no debería ir matando a ciegas cada segundo. El killer, el escáner que se auto-estrangula, el catálogo de ocho exploits… todo eso viene tal cual del mundo Mirai/DDoS. Cling no se diseñó de cero: se ensambló sobre un esqueleto de botnet de ataque al que le quitaron las armas y le atornillaron un túnel. Esa herencia deja huellas — y de ellas tira el hilo.

07Persiste — porque es un proxy

Prueba de fuego de la tesis: Mirai, a propósito, no persiste (vive en memoria; un reinicio lo borra). Cling hace lo contrario — se copia a disco y se clava en el arranque por tres mecanismos, para cubrir firmwares distintos:

persistencia · el mecanismo .cling
cp <self> /root/.cling ;  cp <self> /usr/local/bin/.cling
echo ::once:/root/.cling >> /etc/inittab      # init de BusyBox
echo /root/.cling        >> /etc/init.d/rcS    # SysV
echo /root/.cling        >> /etc/rc.d/rc.boot  # rc.boot

Sobrevive al reinicio. En un bot de DDoS eso sería un lujo raro; en un nodo de proxy es el requisito: lo que le pides es exactamente que siga ahí mañana. La persistencia no es un adorno — es la confirmación de para qué sirve.

08Lo encendí (en una jaula)

Todo lo anterior sale de leer el binario. Pero la baliza de arriba —esa etiqueta seguida de los trece puertos, a 5 segundos— la quería ver salir de verdad. Así que hice algo que en el cebo no se hace nunca: lo ejecuté.

Dónde está la línea, y por qué la muevoEn el honeypot no se ejecuta nada — es una máquina con IP pública y encender ahí un gusano podría contagiar a terceros. Lo hice en otra máquina: aislada, sin salida real a la red, con el bicho hablándole a un respondedor STUN falso que le monté yo. Ni un paquete salió al mundo. Observar en una jaula no es soltar; la diferencia es la jaula. (Es la misma línea que moví en los capítulos 13 y 15.)

Y salió exactamente lo que decía el código: trece Binding Requests, trece puertos aprendidos, y a partir de ahí la baliza a los trece servidores, cada 5,0 segundos, sostenida741 balizas: cincuenta y siete rondas de trece, a lo largo de casi cinco minutos. Luego, quieto, escuchando órdenes por esos mismos trece sockets. En la jaula nadie se las manda, así que ahí se queda — dando su latido al vacío.

Por qué mi baliza mide 34 y no 32En la jaula le puse una etiqueta de laboratorio, zlab0903, de ocho letras, para no confundir su latido con tráfico real. Ocho de etiqueta + los veintiséis de siempre = 34 bytes. La fórmula del apartado 3 no falla: cambias la etiqueta, cambia el tamaño, exacto. Con la de nuestra infección, telnet, serían 32.
strace · el saludo STUN a los 13 (recortado)
sendto(0, "\x00\x01\x00\x00\x21\x12\xa4\x42\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 20, {74.125.250.129:19302})  # Google
                  └ cookie ┘└──── transaction ID = TODO CEROS ────┘
sendto(1, "\x00\x01\x00\x00\x21\x12\xa4\x42\x00…", 20, {145.249.115.184:3478})    # 145, la sospechosa — mismo saludo
  … 13 sendto idénticos, uno por socket

Ese 21 12 a4 42 es la cookie mágica de STUN: habla el protocolo de verdad. Y fíjate en el segundo — al futuro sospechoso, 145.249.115.184, le manda el mismo saludo que a Google. Indistinguible.

Pero hay un descuido que el disfraz no tapa: los doce bytes que siguen a la cookie —el transaction ID, que un cliente de videollamada genera al azar en cada petición— van a cero. Siempre. Cling no se molesta en aleatorizarlos. Y eso es una firma de red preciosa: un Binding Request con el transaction ID en blanco no lo manda ningún teléfono del mundo. Lo manda esto.

Ese latido responde, de paso, una pregunta que el código dejaba abierta: ¿cómo le vuelve la respuesta al bot si está detrás de un router? Cada baliza abre un agujero en el NAT de la víctima —el mecanismo de siempre del NAT traversal— y lo mantiene abierto latiendo cada 5 segundos. No es un grito al vacío. Pero lo que de verdad cambia las cosas lo dice la última tanda del strace: a dónde habla el bicho, y a dónde no.

strace · a dónde habla el bicho — TODOS los destinos en casi 5 min
las 13 IPs STUN ......  las 741 balizas (una cada 5 s; 145.249.115.184 entre ellas)
0.0.0.0 ..............  13   (bind de los sockets — no es un destino real)
127.0.0.1:33957 ......  1    (el cerrojo de instancia única)
cualquier otra IP ....  0    ← ni un solo paquete a nada más

No hay un C2 aparte, en ninguna parte: el bot no habla con nadie más que con esas trece direcciones — a ninguna otra va un solo paquete. (Qué se deduce de eso sobre el operador, en el hilo.)

09La pregunta que queda

El bot solo le anuncia sus trece puertos —su ficha para que lo alcancen— a esos trece servidores. A nadie más: el strace lo enseña, «cualquier otra IP: 0». Así que quien quiera mandarle una orden tiene que haber recibido esa ficha — tiene que ser uno de los trece. Y esto no depende de cómo esté montado el router de la víctima: uno de esos trece servidores STUN es del operador. No hay otra vía plausible de que le llegue el mando — salvo un resquicio remoto que persigo en el hilo.

Y ahí está el problema, y el gancho del siguiente hilo. Miré las trece, una por una, en Shodan: son servidores STUN públicos de proveedores VoIP reales —IONOS, Google, LeaseWeb, una ISP británica, la rusa SIPNET…—. Once de las trece respondían STUN cuando escribí esto, a primeros de septiembre. A simple vista, ninguna es «el buzón del operador»: todas parecen víctimas involuntarias de un camuflaje brillante.

Pero eso ya lo sabemos: el bot solo a esas trece les anuncia sus puertos, así que una tiene que ser el buzón. ¿Cuál? Esa pregunta —separar los doce servidores inocentes del único que es un buzón escondido a plena vista— no se responde leyendo el binario. Se responde tirando del hilo. Y es lo que hago en el próximo, en Tirando del hilo.

10Indicadores (IOCs)

Como el operador rota los binarios a cada poco (cap. 18), los hashes valen poco. Lo que caza a Cling es su comportamiento:

TipoValor
Firma de red vistoun IoT hablando STUN a 13 servidores casi a la vez (UDP a :3478/:19302) con el transaction ID a cero (un cliente real lo aleatoriza) · baliza de etiqueta + 26 B cada 5 s a los trece
Cadena delatoraclingwashere como User-Agent hacia el 7547
Firma del escánerSYN salientes con puerto origen fijo 9999 a 80/8080/85/60001/52869/7547
Persistencia/root/.cling · /usr/local/bin/.cling · .cling en inittab/rcS/rc.boot
Suplantación de initun /proc/<pid> que es bind mount de /tmp (idéntico a /proc/1)
Cerrojo local127.0.0.1:33957 en escucha
Protocolo C2 deducidoorden UDP de 20 B con cookie STUN; el bot lee unos pocos bytes del hueco del transaction ID e ignora el resto, sin autenticación. Deducido del descodificador — no capturamos ninguna orden; y como ignora el resto de bytes, la orden puede disfrazarse: no sirve como firma de red fiable
Trece IPs que NO son indicadoresLas trece direcciones STUN son servidores públicos de proveedores VoIP reales. Publicarlas como IOCs maliciosos sería injusto y llenaría de falsos positivos a quien se fíe. Van como contexto — salvo, quizá, una. Cuál, en el hilo.

⬇ descargar la regla (cling-ngioweb.yar)

Continuará — en Tirando del hilo: doce servidores STUN inocentes y un buzón escondido entre ellos. Cómo se distingue el uno de los otros doce sin más que registros públicos — y a dónde lleva. 🍯

Comentarios