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í:
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.
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.
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:
"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.
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.
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.
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.
¿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:
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.
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.
06Tres semanas después
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.
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.
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