ES EN
Índice

RedTail · Capítulo 15

Volví a por RedTail, y RedTail ya tenía dueño

Monté una jaula, ejecuté el minero y le saqué de la memoria la configuración que en el capítulo 6 no supe descifrar. Me emocioné… y me llevé dos cosas que no buscaba: que esta familia ya estaba documentada de arriba abajo —en un sitio donde no se me ocurrió mirar— y que la pieza que creía haber descifrado servía para otra cosa.

El capítulo 6 lo dejé con una promesa: «El binario no se va a ninguna parte. Volveré.» Y con un hueco en el mapa — un renglón que ponía ??? descifrado y una flecha al lado: aquí me quedé.

Recuerdo dónde lo dejé, que es de donde arranca esto. Abrí el minero de RedTail buscando su cartera de Monero —a dónde va el dinero— y salí de allí con tres cosas: que la cartera no está en el binario, y no está a propósito; que la configuración sí va dentro, embebida y cifrada; y que, barriendo el fichero entero con una prueba estadística, no aparecía por ningún lado la huella de un XOR de clave repetida — de 1 a 40 bytes de clave, ni un positivo. Lo que no saqué fue la pieza de en medio: qué convierte esos bytes cifrados en el texto que lee el minero.

Días después tenía dos cosas que entonces no tenía. Una jaula donde ejecutar bichos vivos sin que se escapen. Y una espina que me había dejado clavada el capítulo 13. Te lo cuento.

01La lección que ya tenía escrita

Lo del 13 fue así. Destripé un bicho, me creí el primero en verle una tripa concreta y, antes de publicar, hice lo que hago siempre: pegar el hallazgo en un buscador. Salió un informe chino de 2020 con todo lo mío dentro, seis años antes. No era el primero. Ni de lejos.

Con esa espina releí mi propio capítulo 6. Y lo incómodo no fue encontrar un error — fue encontrar, en la última sección, esta conclusión firmada por mí:

Lo que yo mismo escribí en el capítulo 6«Comprobar antes si alguien ya pasó por aquí. Debería haber sido lo primero, no lo último.»

La lección ya estaba escrita. La había escrito yo. Y aun así, unos párrafos más arriba de esa frase, había otra que no había verificado nunca: «nadie ha publicado cómo se descifra la configuración de RedTail».

Y ojo, que buscar, busqué. Fui a Akamai, que es la referencia de esta familia, y a Malpedia, que es el catálogo. No encontré nada, y me quedé tan ancho. Lo que no hice fue seguir buscando después de que la fuente grande me diera la razón.

Así que esta vez busqué de otra manera: con otras palabras, en otros idiomas y en sitios más pequeños. Y volví a equivocarme — pero de otra forma.

02Estaba escrito. En italiano.

No hizo falta cavar mucho, una vez que dejé de cavar donde ya había cavado. Un análisis técnico italiano tenía la familia abierta en canal, y encajaba pieza a pieza con lo que Akamai ya había contado:

  • Los dominios de sus proxies, con nombre y todo, publicados. Y dos más de los que yo llegué a ver.
  • El puerto 2137 —el que en el capítulo 6 señalé como su firma— documentado como su canal de minado sobre TLS.
  • El porqué de la cartera ausente. Y aquí voy a ser justo conmigo mismo, que en esto es fácil pasarse de flagelante: en el 6 acerté el qué. Dije que la cartera no está en el fichero y que no está por diseño, y lo deduje mirando lo que le habían amputado al minero. Lo que no tenía era el cómo, y lo cuenta Akamai con todas las letras: RedTail no apunta a un pool público — se monta su propia infraestructura de minado, proxies y pools privados suyos, que reconocen a sus mineros por la IP desde la que llaman. La cartera se queda de ese lado. En el binario no le hace falta.

O sea: mi conclusión era buena, y el mecanismo que la explica llevaba más de un año publicado. Que es una manera elegante de decir que me pasé una tarde deduciendo algo que podía haber leído.

Y la otra pared, la del descifrado —la que dejé abierta con un volveré—, tampoco era un misterio abierto. Era un misterio resuelto en un idioma que no se me ocurrió probar.

03Pero tenía la jaula montada

Podría haberlo dejado ahí, con la lección aprendida por segunda vez y la cara un poco roja. Pero me quedaba un gusanillo — y una herramienta.

De Trinity, el gusano del capítulo 13, me quedó montado un laboratorio para ejecutar malware sin que se escape: una máquina aislada, con el cable de red desenchufado, y el proceso encerrado en una red de mentira —una interfaz falsa, una ruta a ninguna parte— desde la que no sale un solo paquete. Lo probé antes de nada: desde dentro de esa jaula, internet no existe.

Y pensé: ya que la tengo, hago el mismo camino. No para descubrir nada que no supieran los demás —eso ya lo había asumido— sino por verlo con mis propios ojos, desde mi propio cebo. El minero descifra su config al arrancar, la tiene en memoria unos instantes antes de intentar conectar. Solo había que mirar en ese momento.

La misma línea que moví en el 13Sí, esto es ejecutar un bicho vivo, y sí, va contra la regla con la que empecé esta bitácora. La muevo igual que la moví en el capítulo 13, y por lo mismo: en una jaula, sin red, observar no es soltar. Y aquí, además, es un minero, no un gusano: este binario no se propaga por sí mismo — lo suyo es minar y hablar con su infraestructura. (Que la campaña que lo reparte use exploits y credenciales robadas es otra historia, y no es la que se ejecuta aquí.) En una red de mentira, esa llamada muere contra una pared pintada.

04Lo que vi en la memoria

Lanzo el minero dentro de la jaula y lo pierdo de vista en dos segundos. Porque lo primero que hace no es minar: es desaparecer. Se suelta de la terminal que lo ha arrancado y se presenta en la tabla de procesos con otro nombre. Donde debería ir el nombre del fichero pone php-fpm: pool www — un proceso de servidor web de lo más aburrido, de esos por los que nadie que mire un ps pasa el dedo. Entre sus otros disfraces lleva /bin/mariadbd y /usr/sbin/ip: una base de datos y una herramienta de red del propio sistema.

Detalle de oficio, para quien venga detrásPor el nombre no lo vas a encontrar, porque el nombre se lo ha inventado él. Lo encontré por el usuario con el que lo lancé: un proceso puede cambiarse la etiqueta, pero no el dueño.

Y aquí está la gracia de la jaula: no hay prisa. El bicho ya está corriendo, ya se ha descifrado la configuración para poder usarla, y todavía no ha conseguido hablar con nadie. Así que lo congelo en seco —una señal de parada, y el proceso se queda a media frase— y le leo la memoria desde fuera, con calma, por el mismo sitio por el que el sistema te deja mirar cualquier proceso tuyo.

Y ahí está. En claro. Lo mismo que en el disco va cifrado:

config descifrada · sacada de la memoria del proceso
"url": "proxies.identities.network:2137"
"url": "proxies.insanecppdev.com:2137"
"url": "proxies.insanitycpp.cx:2137"

Los tres proxies, en el 2137. Exactamente lo que decían los informes — pero saliendo de mi bicho, en mi máquina, con mis manos. No es una exclusiva; es otra cosa. Es la diferencia entre creerte algo y haberlo visto.

Un aviso si vas a apuntar esos nombres: rotan. Un rastreador público listaba ese mismo día otros dominios distintos de la misma familia. Estos tres son los que traía mi muestra, no la lista eterna de RedTail.

Y con la configuración salió un detalle que no esperaba. La única conexión que el minero intenta al arrancar no va a ninguno de sus proxies: va al puerto 853 de una IP. El 853 es el puerto habitual del DNS sobre TLS, así que todo apunta a que va por ahí — que la conexión lleve dentro un DoT de verdad no lo he abierto a mirar; lo que tengo es el destino y el puerto. En vez de preguntar «¿qué IP tiene proxies.insanecppdev.com?» a la vista de todo el mundo —de su proveedor, del cortafuegos de la casa, de cualquiera que mire el tráfico—, lo pregunta por un canal cifrado. No esconde solo con quién habla: esconde a quién pregunta por él.

Fui a mirar de quién era esa IP antes de emocionarme —lección aprendida, y esta vez a tiempo— y menos mal. Esa IP responde por dns.njal.la: el resolutor público de Njalla, un servicio sueco orientado a la privacidad que usa muchísima gente legítima. No es una IP que yo pueda atribuir a la infraestructura de RedTail — es un servicio de terceros al que el bicho le pregunta. O sea: no monta su propio DNS. Se apoya en uno ajeno, orientado a la privacidad, y mete la consulta dentro de un canal cifrado.

Por qué esto NO es un indicadorEstuve a punto de apuntar esa IP como «infraestructura de RedTail». Habría sido un error de los que se copian: dns.njal.la lo usan miles de personas que no tienen nada que ver con esto. Señalarlo sería como denunciar a la compañía de teléfonos porque un delincuente hizo una llamada. El dato bueno no es la IP — es la técnica: RedTail resuelve sus dominios por un DNS anónimo cifrado. Eso sí retrata al operador.

05Y encima, la tabla que no era

Y ahora el error propio, que es el que de verdad escuece: este no me lo hizo nadie, me lo hice yo. Antes de tirar de la jaula había intentado el camino limpio —encontrar el descifrador dentro del binario— y creí haberlo encontrado.

Un barrido de entropía fino —midiendo el «desorden» del binario en ventanas pequeñas, no grandes como en el capítulo 6— destapó una tabla de 256 bytes escondida entre los datos, una permutación perfecta de todos los valores posibles. Una tabla de sustitución: la pieza típica de un cifrado casero. Mi primer reflejo fue llamarla «S-box» — y ese nombre ya presupone un papel de cifrado que yo no había probado.

02468entropía (bits/byte)0x465a00 · 7,6720.312 ventanas de 256 B · 5,2 MB →
Todo el binario, medido en ventanas de 256 bytes. Veinte mil trozos, todos con entropía normal… y una sola aguja. Eso es la tabla escondida.

Estaba idéntica, byte a byte, en las cinco arquitecturas del bicho — cuando escribí esto conté cuatro, porque al binario de RISC-V mi versión de UPX no le podía quitar el envoltorio y lo dejé fuera. Con una versión más nueva abre sin protestar, y la tabla está también ahí. Otra vez lo mismo: el fallo era de mi herramienta, no de la muestra. Eso la coloca en el tronco común de las cuatro —forma parte de lo que llevan dentro, no de lo que añade el compilador de cada plataforma—, aunque no demuestre que la escribieran ellos: podría venir de alguna librería que arrastran.

00112233445566778899AABBCCDDEEFF256 casillas · 256 valores distintos · cero repetidos
La tabla que encontré, dibujada casilla a casilla (el color es el valor). Los 256 valores posibles, cada uno una vez: una permutación. Olía a cifrado. No lo era.

Y encontré la rutina que la usa: una mezcla de sumas y XOR encadenados. Me convencí de que ese era el cifrado de la config, y me relamí.

Pues no. Cuando por fin busqué bien, el análisis italiano contaba que la configuración de RedTail se descifra con algo muy distinto: un generador de números pseudoaleatorios. Uno de esos que, dándoles un número de partida, escupen un chorro de bytes que parece aleatorio pero es siempre el mismo. Ese chorro es la clave, y con él se deshace el cifrado.

Y daban una pista para reconocerlo: la constante 0x6c078965, que es la que usa el Mersenne Twister —el MT19937, un generador clásico— al montar su estado inicial. Fui a buscarla a mi binario. Ahí estaba, agazapada en el 0x1c409.

Aquí voy a hilar finoQue es justo lo que no hice en el capítulo 6. A partir de ahora conviene separar tres cosas que no valen lo mismo. Lo que he visto yo: la configuración en la memoria, los nombres con los que se disfraza, la tabla de 256 bytes, y esa constante en mi muestra. Lo que he leído: que la configuración se descifra con ese generador — lo dicen ellos; yo no he seguido la rutina hasta el final. Y lo que deduzco juntando las dos cosas: que esa tabla no pinta nada ahí. Encaja bien. Pero deducir no es haber visto, y esa distinción es la que me faltó la primera vez.

¿Y no será RC4?

Es la pregunta que tocaba hacerse: una permutación de 256 bytes movida con sumas y XOR es exactamente la pinta de RC4. Pero esta vez no me fié del parecido — lo miré en el binario. Y no cuadra por tres sitios.

La tabla vive en un segmento de solo lectura (R-X, sin bit de escritura), y RC4 necesita reescribir su estado en cada byte que produce (swap(S[i],S[j])): ahí no puede correr. Y en todo el binario nadie la escribe — de las tres únicas veces que se la toca, las tres son lecturas, y las tres caen en el mismo bucle:

la tabla en el binario · permisos y referencias
va 0x8659e0   segmento R-X   ; solo lectura, sin escritura

3 referencias en 962.205 instrucciones, las 3 lecturas:
  0x6c82e9   movzx eax, byte [rax + 0x8659e0]
  0x6c8309   movzx eax, byte [rax + 0x8659e0]
  0x6c832a   movzx eax, byte [rax + 0x8659e0]
ni un swap, ni una escritura, en todo el fichero

Y la forma del código tampoco es la de RC4 —que es swap y un único XOR contra el dato—: lo que hay es h = T[h ⊕ c] encadenado, una pasada hacia delante sumando y otra hacia atrás con XOR. Cero swaps. Es una sustitución encadenada estilo Pearson, y el bucle cuelga de dos descriptores que solo cambian parámetros — forma de tabla de algoritmos de una librería, no de código escrito a mano (a un tiro de piedra, en la .rodata, hay un muro de cadenas de libuv). Así que la etiqueta honesta es la más sosa: una tabla de sustitución de 256 bytes. De qué librería y para qué exactamente, no lo sé — el binario está stripped, sin símbolos, y no me da para identificarla. Y no me lo voy a inventar, que de eso va justo este capítulo.

Y ahora, la parte que no esperaba: esto me absuelve de otra cosa. En el capítulo 6 me gasté una tarde en demostrar que la configuración no está cifrada con XOR de clave repetida — barrí el fichero entero buscando la huella estadística que deja ese cifrado, probando claves de 1 a 40 bytes, y salió limpio. Era lo único de aquel capítulo que no había leído en ningún sitio, así que confieso que, a estas alturas del día, temí que esto me lo tumbara también.

Pues no me lo tumba: le da la vuelta. Un generador de estos produce una secuencia de bytes determinista —siempre la misma si le das el mismo número de partida— que se puede usar como clave. Y si el descifrado consiste en aplicar esa secuencia byte a byte con XOR, entonces sigue siendo XOR. Lo que se cae no es el XOR: es la hipótesis de la clave corta que se repite. Una secuencia así tarda un disparate en volver sobre sus pasos —incomparablemente más que los cinco megas de este fichero—, así que una prueba que busca repeticiones cada 1 a 40 bytes no tenía nada que encontrar.

Que es el resumen honesto de aquella tarde: no me equivoqué buscando XOR. Me equivoqué buscando una clave corta.

La tabla tiene nombreCerré esa línea diciendo que no sabía de qué librería salía y que no me lo iba a inventar. Ya no hace falta inventar nada.

Esos 256 bytes están tal cual dentro de OpenSSL, de Nettle y de libgcrypt. Son la PITABLE del cifrador RC2, la que define el RFC 2268 — y RedTail enlaza OpenSSL estáticamente, así que la arrastra sin querer.

Y encaja con lo que describí sin saber qué era: el calendario de claves de RC2 es, palabra por palabra, una pasada hacia delante sumando y otra hacia atrás con XOR. Que esas tres lecturas sean exactamente la expansión de clave de RC2 es lo único que aquí deduzco; que la tabla es la de RC2, está medido.

✅ Y no desmonta nada: confirma. La tabla no es de RedTail y no descifra la configuración, que era la conclusión. Y el descarte de RC4 también estaba bien hecho — la intuición de que «tenía toda la pinta» fallaba por un dígito: RC2, no RC4.
La diferencia, que es la lecciónMi tabla existe, es real y está ahí dentro. Pero no descifra la config — y, como acabo de ver, probablemente ni siquiera es suya, sino de una librería que arrastra. Me la creí el cifrado principal porque encajaba con lo que yo quería encontrar. Encontré una pieza de verdad, y le puse la etiqueta que me convenía. Así es como se cuela un error: no inventándote un dato, sino queriendo que el dato que tienes sea el que buscabas.

06Tres semanas después

Los tres proxies, hoyLos tres proxies siguen vivos y resuelven a 31.56.209.165 y 130.12.180.51. (Los dominios raíz no resuelven: solo existen los subdominios proxies.*.) Esta campaña no se ha ido a ninguna parte.

Y al mirar dónde viven esas direcciones ha salido un hilo que no había visto, y que une dos capítulos separados por diez.

La IP del proxy de minado, 31.56.209.165, y el servidor de reparto del Capítulo 5, 217.60.195.113, están en rangos distintos — pero comparten el mismo número de red y el mismo nombre de registro: AS209373, SWISSNET. (Y aquí los dos datos concuerdan, sin el lío de titulares y anunciantes que salió en el Capítulo 11.)

Es decir: el sitio desde el que reparte el binario y el sitio al que ese binario manda el dinero están en el mismo proveedor. Ningún capítulo había conectado esas dos piezas, y solo aparece comparando el 5 con el 15. Es justo lo que celebraba el Capítulo 4: guardar y comparar paga.

La pieza que le faltaba al Mersenne TwisterAquí arriba publiqué la constante con la que se siembra el generador, 0x6c078965. Verificando los capítulos 5 y 6 apareció la otra mitad: la implementación completa del generador, en 0x466cb0, con las cuatro constantes de manual. Son dos funciones distintas y encajan — una siembra, la otra genera.

Un detalle que orienta: esa función tiene un único consumidor en todo el binario. Un generador de biblioteca de uso general tendría decenas.

⚠️ Para qué se usa no está establecido. Es una pista, no una conclusión: no digo que ése sea el chorro de bytes que descifra la configuración, porque no lo he seguido hasta el final.
Y algo que me escuece un pocoEl dato del DNS cifrado lo saqué encendiendo el minero en la jaula. Resulta que llevaba desde el 30 de julio publicado gratis en un informe automático de VirusTotal. Dos caminos distintos, el mismo resultado — pero uno costaba montar un laboratorio y el otro, mirar. La configuración en memoria sí exigía detonar; esto no.

El capítulo 6 terminaba diciendo que reconocer una pared es parte del oficio. Lo sigo pensando. Solo que ahora sé que hay tres clases de pared: la que no se puede derribar, la que solo cuesta horas… y la que alguien ya derribó mientras tú te dabas cabezazos contra la de al lado.

RedTail tenía dueño desde el principio. El nombre 2137gang venía en los propios dominios que traía mi muestra, y un puñado de firmas de seguridad tenían la familia abierta en canal desde 2024. Yo llegué en 2026, con una jaula recién montada y una lección que ya me había escrito a mí mismo.

Volví, como dije que volvería. No traigo la exclusiva que quería: traigo el mapa completo, la pieza que faltaba puesta por otros, y un error mío contado antes de que lo cuente nadie. Me vale.

Continuará — el cebo sigue encendido. Y yo, a partir de ahora, con el buscador abierto antes de la primera línea — y sin cerrarlo porque el primer sitio me haya dado la razón. 🍯

Comentarios