RedTail · Capítulo 6
El minero que esconde su cartera
Desempaquetamos el minero de RedTail y lo abrimos con Ghidra buscando el pool y la cartera. Lo que encontramos es más interesante que un número: el dato va dentro, embebido y cifrado — y nadie ha publicado cómo descifrarlo.
En el Capítulo 5 cacé a RedTail entrando con su propia llave. Ahora toca el binario: desempaquetarlo y abrirlo con Ghidra con un objetivo claro — sacar su pool de minado y su cartera de Monero. Spoiler honesto: la respuesta no es un número, es por qué ese número no está.
Se busca a dónde manda el dinero y de qué depende, para poder detectarlo y cortarlo.
01Quitar el envoltorio
Los binarios venían empaquetados con UPX y sin cabeceras de sección — que es lo normal en cualquier ELF empaquetado con UPX, no un truco suyo. El upx -d no las necesita: descomprime con sus propias estructuras.
$ upx -d x86_64 -o x86_64.unpacked
File size Ratio Format Name
-------------------- ------ ----------- -----------
5199952 <- 1989056 38.25% linux/amd64 x86_64.unpacked
Unpacked 1 file.De 2 MB comprimidos a 5,2 MB en claro. Con el envoltorio fuera, a mirar dentro.
02Es un XMRig a medida
Las strings del binario limpio no dejan duda: RandomX, cryptonight, donate.v2.xmrig.com… es un fork del minero de código abierto XMRig. Pero con dos añadidos que lo delatan como RedTail:
- Una librería propia, libredtail, con evbuffer_tls — su capa de red por TLS para hablar con el C2.
- La ruta de compilación del autor, embebida: /var/build/redtail/scripts/x86_64-build/ — con hwloc 2.14.0, snappy 1.2.2, abseil-cpp. Un entorno de build moderno y cuidado. El proyecto se llama, literalmente, «redtail».
Lo que no le han tocado es el motor. Ahí sigue el núcleo entero de XMRig: las cinco variantes de RandomX (rx/0, rx/2, rx/arq, rx/aH, rx/af), trece de CryptoNight y las tres de argon2 (chukwa, ninja, wrkz), con soporte para Monero, Graft, Wownero, Zephyr, Townforge, Sumokoin, Arqma y Ravencoin. Un minero capaz de todo eso, dedicado a una sola moneda. Retén el detalle, porque en la sección 06 explica bastante.
03A la caza del botín — y la pared
Con el binario abierto, fui a por el pool y la cartera por todos los caminos. Uno a uno, todos dieron en pared:
- ¿Un blob cifrado embebido? Escaneo de entropía de los 5,2 MB → cero regiones de alta entropía. No hay un bloque cifrado escondido.
- ¿El pool/cartera en claro? No. Lo único plano son las plantillas de XMRig (stratum+ssl://%s) y sus dominios de donate por defecto.
- ¿Una IP o dominio propios? Ni uno. Busqué incluso la IP de reparto (217.60.195.113) como texto y como bytes crudos en todos los órdenes. No está.
- ¿Un parser de config raro? No: es el parser JSON estándar de XMRig. El minero espera recibir una config, no la lleva puesta.
Ghidra confirmó lo que las strings insinuaban: las funciones que forman el URL del pool (Pool::parse, stratum+tcp/ssl) son las de XMRig de siempre, alimentadas por una configuración que llega de fuera.
04El veredicto: la cartera no está, por diseño
Juntando las piezas — capa TLS propia (libredtail), sin blob cifrado, sin pool/cartera/C2 embebidos, parser de config estándar — la conclusión es clara y es la firma de RedTail:
Esto es lo que separa a RedTail del malware de serie: los otros escondían el secreto dentro y bastaba leerlo bien (Capítulos 2 y 4). RedTail no esconde el secreto: no lo lleva encima. Si tumbas su C2, los mineros ya desplegados se quedan sin a dónde mandar el dinero — pero tampoco te lo cuentan.
05Dónde está la línea
Sacar la cartera de verdad exigiría uno de dos caminos, y los dos quedan fuera:
- Ejecutarlo en un laboratorio aislado y observar a qué C2 llama y qué config descifra. Es la vía que daría el dato — pero es un minero vivo, y ejecutarlo cruza la línea de esta bitácora (y arriesga la máquina). No se hace.
- Un trace muchísimo más profundo de cómo libredtail construye la dirección del C2 (probablemente ensamblada en memoria pieza a pieza). Horas de ingeniería inversa, sin garantía de premio.
Prefiero contarte lo que sabemos con certeza —que el dato no está, y por qué— antes que forzar una respuesta. Reconocer la pared también es parte del oficio.
06Volví — y la pared se movió de sitio
Lo que dice el resto del mundo
Lo primero que hice fue lo que debería haber hecho antes: mirar si alguien había pasado por aquí. Y la respuesta me sorprendió.
Nadie ha publicado nunca cómo descifrar la configuración de RedTail. Y no es que no haya buscado bien: Akamai lo dice por escrito. Ellos son la referencia de esta familia, y en su informe explican que sacaron los pools mirando la memoria del minero ya en marcha, «evitando el largo proceso de aplicar ingeniería inversa al descifrado». Es decir: llegaron a la misma pared y la rodearon.
Hay más. Malpedia, el catálogo de referencia, no tiene ni una regla de detección para RedTail. No existe ningún extractor de configuración en ningún framework público. Y el dato que más me llamó la atención: en dos años no se ha publicado ni una sola cartera de Monero de esta familia. Ninguna. Lo que sí es constante en toda su infraestructura conocida es el puerto 2137.
Donde me equivoqué
Arriba escribí, con mucha seguridad, que RedTail «se los pide a su servidor de mando en caliente». Ahora creo que eso está mal, y la prueba estaba delante de mí.
XMRig, el minero legítimo del que RedTail es una copia modificada, se configura por línea de comandos o por fichero. Tiene claves para todo: url, algo, coin, config… Fui a buscarlas en el binario de RedTail y falta un puñado — pero no las que yo dije. Contadas sobre los bytes: quedan diecinueve, entre ellas url, y faltan seis.
Y ese seis es el que cuenta la historia, porque no es una amputación al azar. Faltan config, coin, algo, user, cpu y tls: exactamente las que permitirían dirigirlo. Sin config no lee un fichero de configuración. Sin coin ni algo no se le cambia de moneda. Y sin user no se le pone otra cartera. Las de operar —hilos, páginas grandes, reintentos, registro— siguen todas ahí. No le quitaron la interfaz: le quitaron el volante.
Piénsalo un momento: a este minero le han amputado la posibilidad de configurarlo desde fuera. ¿Y por qué haría eso alguien? Si la configuración llegara del C2, no haría falta borrar nada — el minero podría seguir aceptando parámetros y daría igual. Se amputa cuando el dato va dentro y no quieres que nadie lo cambie, lo lea, ni lo sustituya por el suyo.
Y eso coincide con lo que sostienen Akamai y el resto de firmas: la configuración va embebida y cifrada, y se descifra en memoria al arrancar. ¿Y el escaneo de entropía limpio de la sección 03? No se contradicen: aquel barrido, de grano grueso, busca bloques grandes — y un blob de configuración es tan pequeño al lado de 5,2 MB de binario que no llega a asomar. Mi conclusión de que venía del C2 salió de una única fuente que ahora veo poco sólida.
Lo que sí he descartado
Si el dato está dentro y cifrado, la siguiente pregunta es con qué. Y ahí sí traigo algo que no había publicado nadie.
No es XOR. Ni de clave simple ni de clave repetida, y no lo digo por intuición. Hay una prueba estadística bonita para esto: si coges un texto normal y lo cifras con una clave que se repite, al comparar el resultado consigo mismo desplazado justo la longitud de la clave, el bit más alto de cada byte sale siempre a cero. En datos aleatorios sale cero la mitad de las veces. Así que se puede barrer un fichero entero buscando esa firma sin saber la clave.
Lo hice sobre todas las zonas de datos del binario, probando longitudes de clave de 1 a 40. Ni una sola zona da positivo. Los únicos aciertos fueron falsos y hasta simpáticos: tablas de conversión, listas de números… y un trozo de Lorem ipsum que viene de los tests de una de las librerías que lleva enlazadas.
El mapa, hasta donde llegué
Y aquí está el terreno ganado, para quien venga detrás — incluido yo mismo:
0x415b60 main # no es el que dice el decompilador: hay que leer
# el ensamblador del arranque para encontrarlo
0x446e8c ... # llama al cargador con la config ya montada
0x4457c0 cargador # lee la clave "pools" y construye los objetivos
0x460ba0 Pool # usa "rig-id" y "self-select"
0x459e80 reconexión # maneja el "client.reconnect" del pool
??? descifrado # <- aquí me quedéFalta la pieza de en medio: qué convierte esos bytes cifrados en el JSON que lee el cargador. Y no la tengo por dos razones concretas. La primera es que el cargador no se llama directamente, sino a través de una tabla de punteros, así que el rastro se corta y hay que reconstruirlo a mano. La segunda es de fuerza bruta: son 5,2 MB de C++ muy optimizado, con OpenSSL y media docena de librerías dentro, y el decompilador se atraganta — devuelve funciones llenas de bloques que no sabe resolver. A partir de ahí se lee ensamblador a pelo, y eso va a razón de horas por función.
07Por qué lo dejo aquí (de momento)
Esta pared no es como la de la cartera. Aquella era definitiva: el dato no existe en el fichero, y no hay ingeniería inversa que saque lo que no está. Esta es distinta — es una pared de coste. El dato está ahí, sé por dónde se llega, y lo que falta son horas. Muchas.
Y he preferido contártelo así, con el mapa a medias, antes que guardarlo en un cajón hasta tenerlo entero. Por dos motivos. Uno, porque lo descartado también sirve: si alguien retoma esto, ya no tiene que perder la mañana probando XOR. Y dos, porque me parece más honesto enseñar una investigación como es —abierta, con terreno ganado y terreno por ganar— que fingir que los capítulos salen cerrados a la primera.
El binario no se va a ninguna parte. Volveré.
08Indicadores (IOCs)
| Tipo | Valor |
|---|---|
| Familia | RedTail · minero Monero (fork de XMRig) |
| Empaquetado | UPX (sin cabeceras de sección — lo hace UPX, no el autor) |
| Librería propia | libredtail (TLS / evbuffer_tls) |
| Ruta de build | /var/build/redtail/scripts/x86_64-build/ (con hwloc 2.14.0, snappy 1.2.2, abseil) |
| Config | embebida y cifrada · NO es XOR (descartado estadísticamente) |
| Interfaz amputada | faltan las 6 claves que permiten dirigirlo: config · coin · algo · user · cpu · tls (quedan 19, entre ellas url) |
| Motor | core completo de XMRig: RandomX ×5 · CryptoNight ×13 · argon2 ×3 |
| Puerto de sus pools | 2137 (constante en toda la infraestructura conocida de la familia) |
| SHA-256 (miner x86_64) | f0aa83bbbd2c75e2f71ec16029ee5fcfad59f3a8efa30a500b815f0f6c18d987 |
Y el resumen honesto de este capítulo es que la cartera no está, que el «no es XOR» sigue en pie, y que la pared que me paró era de coste, no de imposibilidad — lo que se descarta también sirve al siguiente que lo intente.
Continuará — el cebo sigue encendido. Cuando el próximo bicho traiga algo nuevo, habrá séptimo capítulo. 🍯
Comentarios