Actualización - 5 de junio @ 3:00 p. m. hora del Este: Este blog originalmente atribuyó la actividad a DesckVB RAT. Después de que esto se nos hizo notar y realizamos una revisión de seguimiento, concluimos que la atribución era incorrecta. Hemos actualizado la publicación original para reflejar nuestra evaluación actual de que el malware es un loader .NET no identificado, y corregimos las referencias relacionadas. Gracias a los miembros de la comunidad que nos impulsaron a revisarlo de nuevo.
Antecedentes
En mayo de 2026, el Huntress SOC respondió a una infección de loader .NET que comenzó con un malspam. Abreviatura de "malicious spam" (spam malicioso), el malspam es un correo diseñado para entregar malware o engañar a un usuario para que realice una acción que inicie la cadena de infección, ya sea abrir un adjunto con trampa, hacer clic en un enlace malicioso o entregar credenciales en una página de inicio de sesión falsa. Aún hoy, el malspam sigue siendo uno de los vectores de acceso inicial más prolíficos para los atacantes. A primera vista, este caso podría confundirse con una infección de malspam más, pero la cadena de entrega cuenta una historia más interesante.
Antes de que la víctima llegue a la infraestructura controlada por el atacante, el señuelo pasa por DoubleClick, un dominio legítimo propiedad de Google que muchas herramientas de seguridad son menos propensas a tratar como sospechoso. Desde ahí, la víctima se redirige a un kit de malspam que se personaliza al vuelo usando la dirección de correo de la víctima, extrayendo dinámicamente la marca de la empresa y los detalles de ubicación para que la página se vea convincente sin que los operadores tengan que crear a mano un señuelo para cada objetivo.
Esto importa porque muestra cómo las operaciones de malspam se están volviendo más escalables sin volverse menos creíbles. Los atacantes no necesitan un kit a medida para cada organización. Pueden reutilizar la misma infraestructura y aun así darle a cada víctima una página que parece específica para ella, lo que reduce el costo de la campaña mientras aumenta las probabilidades de que alguien haga clic.
Una vez que la víctima interactúa con el señuelo, la operación pasa de la ingeniería social a una preparación de malware por capas. En su núcleo, es un troyano basado en JavaScript, pero lo que lo hace peligroso es la cadena que activa una vez que aterriza en una máquina. En lugar de soltar un payload evidente, la cadena pasa por componentes de JScript, PowerShell y .NET que trabajan juntos para obtener, ensamblar y ejecutar el malware final mayormente en memoria.
Desde ahí, el loader se comunica con un servidor C2, entregando al atacante el control remoto total del equipo. En ese punto, el atacante puede extraer datos, ejecutar comandos o usar el punto de apoyo como le convenga, todo mientras mantiene una huella de detección mínima.
El loader enumera los productos antivirus instalados mediante WMI en el primer beacon, lo cual es un perfilado estándar antes de decidir qué soltar a continuación. También se toma el trabajo de detectar específicamente GPUs NVIDIA (GTX/RTX) y AMD (Radeon) tanto por WMI como por lecturas directas del registro. Ese nivel de interés específico en la GPU podría apuntar hacia la minería de criptomonedas como objetivo posterior, o podría usarse con fines de camuflaje.
Para los defensores, esto es lo que hace que valga la pena prestarle atención: el malspam es solo el movimiento inicial, y todo lo que viene detrás está construido para aterrizar en silencio, permanecer oculto y darle al atacante espacio para operar.
Cronología
Figura 1: Diagrama que muestra la ruta del ataque
Descubrimiento inicial
Nuestra investigación comenzó con una señal de ejecución de procesos que indicaba la ejecución de un archivo JavaScript sospechoso mediante WScript.exe.
Figura 2: Detecciones iniciales de WScript
La siguiente pista fue un código PowerShell muy codificado y ofuscado que, al decodificarlo y desofuscarlo, resultó ser un dropper de PowerShell.
Figura 3: PowerShell codificado y ofuscado
Análisis de malspam
El acceso inicial ocurrió mediante un correo de malspam que contenía un archivo adjunto HTML malicioso, Bestellung_2026.html (“Bestellung” es la palabra alemana para “pedido/orden de compra”). El adjunto es un archivo HTML mínimo cuyo contenido completo es una redirección meta-refresh de cero segundos. El meta-refresh envía al navegador a una URL de seguimiento de clics de Google DoubleClick Campaign Manager (ad.doubleclick[.]net/ddm/trackclk/N4892.5020.4774291382421/B23999293.271539123), que lleva identificadores de campaña y creatividad (dc_trk_aid=466016770, dc_trk_cid=131101292). El siguiente destino se agrega después del ?, con el correo del destinatario codificado en base64 dentro del fragmento de la URL de ese destino. ¿Por qué los actores de amenazas enrutan a través de DoubleClick? La razón probable es evadir filtros y reputación. ad.doubleclick[.]net es un dominio de alta reputación propiedad de Google que rara vez se bloquea y que suele estar en la lista de permitidos de las pasarelas de seguridad de correo, los proxies web y los servicios de reputación de URL; así, el único dominio visible en el cuerpo del correo y al inicio de la cadena es uno que los defensores difícilmente marcarán, denegarán o enviarán a sandbox.
Figura 4: Adjunto HTML malicioso
DoubleClick reenvía a fostercareintheus.optimizationprime[.]com/b#<base64-email>. Este servidor no aloja el kit; es una etapa de redirección. Decodifica el correo en base64 y reenvía el navegador al servidor del kit, llevando el correo como fragmento de texto plano. Esta etapa agrega una capa de dominio entre el frente confiable de DoubleClick y el servidor real del kit. La cadena termina en el kit de entrega en hxxps://bth.startthewave[.]org/a/#<plaintext-email> (por ejemplo, bth.startthewave[.]org/a/#<email_address>). Al hacer clic en “PDF herunterladen” (“Descargar PDF”) se activa downloadFile(), que crea un formulario HTML oculto y envía una solicitud POST con email=<address> a hxxps://pengajian.muliastudy[.]com/images/edu/u.php. El servidor responde con un archivo ZIP (A021185521S210008-11521.zip) como cuerpo de la respuesta.
La página señuelo (servida desde bth.startthewave[.]org/a/) es un único archivo HTML autocontenido. setupEmailAndUI() lee el correo del destinatario desde el fragmento de la URL (window.location.hash), extrae el dominio y reescribe el título de la página, el texto del logotipo del encabezado y el pie de página para hacerse pasar por el empleador del destinatario. El logotipo de la empresa se obtiene en tiempo de ejecución mediante una cadena de respaldo: Clearbit, logo.dev, favicones de Google, favicone, DuckDuckGo y, por último, el propio /favicon.ico de la empresa. El kit no contiene contenido específico de la organización; con solo cambiar el correo se rebautiza al instante.
fetchLocationAndTime() llama a ipapi[.]co/json/ y muestra la ciudad, la región, el país y la hora local del espectador en el encabezado; una táctica de ingeniería social para que la página se sienta personalizada y “segura”.
El kit solo se renderiza si hay presente un fragmento de URL #email; setupEmailAndUI() construye toda la página a partir de este. Sin el fragmento, la página muestra “E-Mail nicht gefunden. Weiterleitung…” (”Email not found. Redirecting…”) y redirige a bing[.]com después de dos segundos.
Cargador JScript de varias etapas
El archivo ZIP entregado por u.php contiene un archivo JScript llamado A021185521S210008-11521.js. No es el malware final en sí, sino un cargador de varias etapas cuya función es obtener y ejecutar un cargador .NET mientras evade el análisis. El archivo está muy relleno de basura: cientos de funciones muertas repetidas, cadenas Unicode sin sentido, relleno de ;;;;; y comentarios en portugués.
Figura 5: Contenido de A021185521S210008-11521.js
Al ejecutarse, el script verifica su propia ruta mediante WScript.ScriptFullName. Si se está ejecutando desde una carpeta Temp o Downloads —es decir, si la víctima acaba de extraerlo del ZIP—, se copia a sí mismo en C:\Users\Public\ktncm.js, relanza esa copia con wscript.exe //nologo y cierra la instancia actual. Si se ejecuta desde cualquier otro lugar, se omite la reubicación (la rama else está vacía) y la ejecución continúa directamente hacia la lógica de preparación del payload. El efecto neto es que la primera ejecución reubica el script en un directorio estable con permisos de escritura para todos, y la copia reubicada, al ya no estar en una ruta Temp/Downloads, pasa de largo en su segunda ejecución y procede a detonar.
El script contiene un bloque base64 grande alterado con caracteres literales A y el token de autenticación 9999 insertado en todo el bloque para evadir la detección estática. La función vjwNvhDoHz() repara el bloque con reemplazos de cadenas, lo decodifica de base64 a un script de PowerShell, lo escribe en C:\Users\Public\nlbzl.ps1 y lo ejecuta de forma oculta con powershell -ExecutionPolicy Bypass -file.
El PowerShell colocado realiza, en orden: una verificación de conectividad (Test-Connection a www.google.com); si está sin conexión, no simplemente se cierra, sino que invoca una función que ejecuta Restart-Computer -Force, reiniciando el equipo; un barrido antianálisis que enumera los procesos en ejecución contra una lista de bloqueo de herramientas de depuración y sandbox (Dbgview, tcpvcon, tcpview, OLLYDBG, ImmunityDebugger, Wireshark, una referencia a autoruns/runsc, apateDNS, y las cadenas any.run, sandbox, analyze), con la misma respuesta de Restart-Computer si se encuentra alguna; y una descarga de payload mediante WebClient. Para la descarga, el script decodifica una cadena base64 incrustada a una URL en hxxps://andrefelipedonascime1778799406970.2241107.meusitehostgator[.]com[.]br/GpazlLUWIJ_14_05_Meus_ArquivosDeTexto/03.txt, la escribe en C:\Users\Public\zkrbx.txt, luego vuelve a leer ese archivo y usa su contenido como destino para WebClient.DownloadData; la respuesta se divide con un delimitador %x%, y el segundo fragmento se escribe en C:\Users\Public\gglhn.txt.
El PowerShell soltado (nlbzl.ps1) no realiza por sí mismo la carga reflectiva: actúa como un generador, ensambla un script de PowerShell adicional como cadena de texto y lo escribe en C:\Users\Public\shmvg_01.ps1, que luego ejecuta con powershell -ExecutionPolicy bypass -File. Es este script generado el que realiza la carga reflectiva de .NET ([Reflection.Assembly]::Load), resuelve el tipo ClassLibrary3.Class1 e invoca el método prFVI. El binario firmado de Microsoft InstallUtil.exe (C:\Windows\Microsoft.NET\Framework\v4.0.30319\installutil) se pasa como uno de los argumentos a prFVI para la ejecución mediante proxy de binarios firmados. Dos URL -hxxps://andrefelipedonascime1778799406970.2241107.meusitehostgator[.]com[.]br/GpazlLUWIJ_14_05_Meus_ArquivosDeTexto/PeNo y hxxp://catalogo.castrouria[.]com/c84da/bl.txt- también se pasan como argumentos a prFVI; estas son consumidas por el payload final de .NET en lugar de ser obtenidas por alguna etapa de PowerShell.
El cargador de .NET (03.txt)
El payload obtenido 03.txt no es el malware final: es un stager cuya función es verificar que no esté siendo analizado, deshabilitar las defensas del equipo, descargar y ensamblar las siguientes etapas, establecer persistencia y ejecutar el payload final.
Figura 6: Campos de configuración codificados de forma fija en el cargador de .NET
Cuando se ejecuta el cargador, la etapa de PowerShell le pasa dos direcciones web. La primera, después de que el cargador la decodifica, apunta a hxxp://catalogo.castrouria[.]com/c84da/bl.txt. La segunda está en hxxps://andrefelipedonascime1778799406970.2241107.meusitehostgator.com[.]br/GpazlLUWIJ_14_05_Meus_ArquivosDeTexto/PeNo. PeNo no es un archivo: es un indicador de configuración. El cargador lee ese indicador, lo quita del final de la URL y trata lo que queda como una ruta base, agregando 01.txt y 02.txt para construir las dos URL desde las que realmente descarga.
Verificaciones antianálisis
Antes de hacer cualquier otra cosa, el cargador examina el entorno y se detiene -eliminando sus archivos y, en la mayoría de los casos, reiniciando el equipo- si detecta análisis. Verifica la presencia de máquinas virtuales y sandboxes (artefactos de VirtualBox, VMware, Hyper-V, Parallels, QEMU y Sandboxie, incluidos SbieDll.dll, vmhgfs.dll, el directorio de VirtualBox Guest Additions y la clave de registro de Sandboxie); servicios de sandbox y triaje (mksSandbox, joeboxserver, triage); indicadores de cloud y de equipos con RDP (WindowsAzureGuestAgent, azurearcsystray, rdpclip, Servermanager); valores del registro del BIOS que contengan "virtual" o "Hyper-V"; la presencia de Desktop\scan.7z; un depurador conectado; y herramientas de análisis por proceso y título de ventana (Wireshark, OllyDbg, ImmunityDebugger, apateDNS, Process Monitor, tcpvcon, handle, autorunsc, Dbgview, any.run). Cuando se activa un disparador, el cargador escribe un archivo marcador que lo describe -vm.txt para una detección de VM o sandbox, Debugger.txt para un depurador conectado, 01_detect_analisse_process.txt para una herramienta de análisis (este registra en su contenido el nombre del proceso y el título de la ventana en cuestión)- y luego termina.
Limpieza del entorno y directorio de preparación
El loader elimina todos los archivos .txt y .ps1 de %TEMP% y C:\Users\Public, eliminando los artefactos de etapas anteriores. Luego crea un directorio de trabajo profundamente anidado bajo %UserProfile%\AppData\LocalLow\, usando nombres de carpetas con temática NVIDIA (LocalLow Windows, Program Rules, Program Rules NVIDEO) repetidos en varios niveles para imitar una ruta de instalación de driver legítima.
Manipulación de Defender y manejo de antivirus
Si no hay ningún proceso de Avast, AVG o Malwarebytes (MBAMService) en ejecución, el loader ejecuta un comando de PowerShell para deshabilitar las protecciones de Microsoft Defender -monitoreo en tiempo real, prevención de intrusiones, reportes MAPS y envío de muestras- y agrega toda la unidad del sistema como ruta de exclusión de Defender. También termina el producto de seguridad QHActiveDefense (Qihoo 360) si está presente.
Obtención del payload
El loader descarga tres archivos remotos usando un User-Agent heredado y codificado de Internet Explorer 8 “Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.0; WOW64; Trident/4.0; SLCC1; .NET CLR 2.0.50727; Media Center PC 5.0; .NET CLR 3.5.21022; .NET CLR 3.5.30729; .NET CLR 3.0.30618; InfoPath.2; OfficeLiveConnector.1.3; OfficeLivePatch.0.0”:
- hxxp://catalogo.castrouria[.]com/c84da/bl.txt
- hxxps://andrefelipedonascime1778799406970.2241107.meusitehostgator.com[.]br/GpazlLUWIJ_14_05_Meus_ArquivosDeTexto/01.txt
- hxxps://andrefelipedonascime1778799406970.2241107.meusitehostgator.com[.]br/GpazlLUWIJ_14_05_Meus_ArquivosDeTexto/02.txt
Las URL de 01.txt y 02.txt se construyen en tiempo de ejecución a partir de la ruta base descrita arriba.
Cada archivo descargado se procesa a través de varias capas: una división por un delimitador multibyte, inversión de cadena, decodificación base64 y una rutina de sustitución de caracteres. El loader luego sustituye un conjunto de tokens de marcador de posición con valores de tiempo de ejecución, ensamblando el script final.
Persistencia
La persistencia se establece de tres maneras: una entrada de registro Run y una entrada de registro RunOnce bajo HKCU\SOFTWARE\Microsoft\Windows\CurrentVersion\, cada una llamada Update Drivers NVIDEO_<random> y configurada para lanzar el script preparado de forma oculta mediante powershell.exe -WindowStyle Hidden -ExecutionPolicy Bypass; y una copia del loader colocada en la carpeta de Inicio del usuario. La nomenclatura con temática NVIDIA y los nombres de valores de registro aleatorizados buscan evadir una inspección superficial.
Ejecución final
El payload ensamblado se escribe como un archivo .ps1 con nombre aleatorio dentro del directorio LocalLow con temática NVIDIA y se ejecuta con powershell.exe -ExecutionPolicy Bypass -File, de forma oculta y sin ventana. El loader alterna la redirección del sistema de archivos WOW64 en torno a la ejecución según si el payload final apunta a una ruta de .NET framework de 64 bits o 32 bits - un comportamiento consistente con una etapa final de inyección de procesos (RunPE) que carga código en un proceso objetivo.
El script de plantilla (02.txt)
El archivo 02.txt no es un payload independiente, sino una plantilla de PowerShell que contiene tokens de autenticación de marcador de posición. Después de que el cargador completa los valores de los tokens de autenticación, el script resultante realiza una operación: toma los bytes decodificados de 01.txt y los carga de forma reflexiva como un ensamblado .NET (ClassLibrary1.dll), resuelve el método Run mediante reflexión, dividiendo el nombre de la clase en concatenaciones de cadenas para romper las firmas estáticas, y lo invoca de forma estática con tres argumentos:
- Una cadena que contiene la ruta de destino de InstallUtil.exe concatenada con valores de metadatos separados por delimitadores (ruta del directorio de preparación, IDs de proceso y marcadores de configuración).
- Un arreglo de bytes que contiene el contenido decodificado de bl.txt, el payload empaquetado del cargador.
- Un booleano que selecciona la ruta de inyección de 32 o 64 bits.
La plantilla antes de la sustitución de los tokens de autenticación:
El token de autenticación %qlxKP% se reemplaza con el arreglo de bytes decodificado de 01.txt (el inyector RunPE), %nkGMv% con el arreglo de bytes decodificado de bl.txt, %yzXVM% con la ruta de InstallUtil.exe, y %trueorfalse% con el marcador de 32/64 bits. La cadena NhqIM sirve como delimitador que el método Run del inyector analiza para extraer los campos de metadatos.
El objetivo de inyección es configurable por el operador: el token de autenticación %yzXVM% se completa en el momento de compilación mediante ClassLibrary3. En la muestra analizada de forma estática, InstallUtil.exe era el objetivo configurado. En la telemetría de una intrusión real, observamos que se usaba MSBuild.exe en su lugar.
El inyector RunPE (01.txt - ClassLibrary1.dll)
El contenido decodificado de 01.txt es una DLL de .NET. Su propósito es el process hollowing: toma un payload y un proceso objetivo, e inyecta uno en el otro.
El payload implementa el process hollowing real mediante la cadena estándar de la API RunPE: CreateProcessA (crea el objetivo suspendido), ZwUnmapViewOfSection (desasigna la imagen original), VirtualAllocEx (asigna nueva memoria), WriteProcessMemory (escribe el payload), GetThreadContext/SetThreadContext (corrige el punto de entrada), y ResumeThread (inicia la ejecución). También maneja Wow64GetThreadContext/Wow64SetThreadContext para la inyección entre arquitecturas. Todos los nombres de las API están ofuscados en el binario. Cabe destacar que el envoltorio de CreateProcessA conserva la palabra en portugués “criando” (que significa “creando”) en su nombre ofuscado.
Figura 7: El método VirteyQDs64x revelado: un DllImport para VirtualAllocEx de kernel32.dll, con nombres de parámetros ofuscados
Antianálisis
El ensamblado verifica si hay depuradores conectados en tiempo de ejecución y enumera los procesos en ejecución contra una lista de bloqueo codificada de forma fija: debugger, Wireshark, apateDNS, analyze, Analysis, any.run, anyrun y tcpvcon. Si se encuentra alguna coincidencia, el inyector no continúa con la ejecución.
Análisis del cargador (bl.txt)
El contenido de bl.txt, inyectado en InstallUtil.exe por el inyector RunPE, no es el loader en sí, sino otra capa de empaquetado. El ensamblado inyectado activa una cadena de desempaquetado de varios pasos antes de que se ejecute el malware real.
Cuando el ensamblado se carga, un constructor estático lee un recurso incorporado y lo descifra usando un cifrado de flujo personalizado. Se cargan una clave de 32 bytes y un IV de 16 bytes; en esta muestra, el IV es idéntico a los primeros 16 bytes de la clave, por lo que al aplicar XOR entre la clave y el IV se anula la primera mitad, dejando solo los últimos 16 bytes como clave efectiva. El cifrado procesa los datos del recurso en bloques de 4 bytes: para cada bloque, un acumulador en ejecución pasa por una función de mezcla que genera una palabra doble de keystream mediante intercambio de bytes, operaciones de multiplicación-acumulación con constantes mágicas (9495, 10476, 22014), mezcla de desplazamiento-XOR (<< 9, << 1, >> 5) y combinación de términos cruzados. El keystream resultante se combina con XOR contra el bloque de texto cifrado para producir el texto plano. La salida descifrada se descomprime opcionalmente con LZX y luego se carga de forma reflectiva mediante Assembly.Load.
Se accede al recurso incorporado a través de un gancho ResourceResolve registrado en el AppDomain. Luego el recurso pasa por dos transformaciones: descifrado TripleDES en modo CBC con relleno PKCS7, seguido de descompresión GZip. Los parámetros de TripleDES:
- Clave: lHxoBf+5Ft6YrDx4FyDWwg==
- IV: nFukcUaa5h0=
El payload descifrado es un ensamblado .NET que a su vez está protegido por otra instancia del mismo cifrado XOR/LZX para proteger su dependencia protobuf-net incorporada.
El loader empaqueta toda la configuración en un solo mensaje serializado con protobuf, lo codifica en base64 y lo guarda como un único literal de cadena. Al iniciarse, el blob de configuración se decodifica de base64, se deserializa mediante protobuf y se carga como la configuración activa:
Figura 8: Configuración del loader
El loader se comunica mediante sockets TCP en bruto usando mensajes cifrados con AES y serializados con protobuf. La clave AES se deriva de la contraseña P@55w0rd! usando Rfc2898DeriveBytes (PBKDF2). Se usa RSA para el intercambio de claves inicial, y el loader fija la identidad del servidor usando cinco hashes SHA-256 de certificados incorporados:
- D5B7247C497788CF0031CEB06E3DF77A45FEF59F1E49633DC7159816D64759B5
- C61B1941CF756EB7551F7C661743802362728B785ADC22E860D269713DFB01A6
- C356AFF1A01C2B0DA472E584C8E3C8F875B9A24280435D42836A77B19F5A8C18
- F1C3EBE78BD8C38559BF3CFCC9A9FA37D221E31780774A3787E26160A61F5348
- E91FB249AA97BE5C7931E430781167EDFE7BA804720B5F643E6AB70B7E6E74DD
Reconocimiento del sistema
En el beacon inicial, el cargador recopila una huella digital de hardware mediante consultas WMI para Win32_Processor (ProcessorId), Win32_DiskDrive (SerialNumber), Win32_BaseBoard, Win32_VideoController y Win32_OperatingSystem. Detecta específicamente GPU NVIDIA (GTX/RTX) y AMD (Radeon) tanto por WMI como por lecturas directas del registro (HardwareInformation.AdapterString, HardwareInformation.qwMemorySize). Enumera los productos antivirus instalados mediante WMI (Select * from AntivirusProduct contra el espacio de nombres SecurityCenter2).
Inyección de procesos
El cargador usa su propia implementación de RunPE (independiente del inyector de 01.txt), y resuelve las API de Win32 en tiempo de ejecución mediante una técnica de ofuscación: cada nombre de API se codifica en base64 con caracteres @ insertados en posiciones aleatorias para romper las firmas estáticas. En tiempo de ejecución, se eliminan los caracteres @ y la cadena se decodifica de base64 para recuperar el nombre real de la función, que luego se resuelve mediante GetProcAddress:
- CreateProcessA
- ZwUnmapViewOfSection
- VirtualAllocEx
- WriteProcessMemory
- GetThreadContext
- SetThreadContext
- Wow64SetThreadContext
- ResumeThread
- ReadProcessMemory
- CloseHandle
Figura 9: Cada nombre de función Win32 se almacena como una cadena en base64 con caracteres @ insertados en posiciones aleatorias
Tanto MSBuild.exe como InstallUtil.exe están codificados de forma fija en la tabla de cadenas del cargador como objetivos de inyección. Cuál se usa en una víctima determinada es configurable por el operador: el token de autenticación %yzXVM% de la plantilla 02.txt se completa en tiempo de compilación mediante ClassLibrary3.
Instalación y persistencia
Antes de establecer persistencia, el cargador ejecuta una rutina de autoinstalación. Primero verifica si ya se está ejecutando desde su ubicación de instalación prevista bajo LocalApplicationData. Si no es así, verifica si tiene privilegios administrativos. Si se ejecuta como usuario estándar, se vuelve a lanzar con privilegios elevados mediante cmd /k START “” “<self>” & EXIT con Verb = “runas”, lo que activa el aviso de elevación de UAC y luego cierra la instancia actual. Una vez elevado, espera un retraso aleatorio (de 4 a 7 segundos), agrega exclusiones de Defender (descritas más abajo), se copia a sí mismo en la ruta de instalación con un nombre de archivo .exe generado aleatoriamente, registra la persistencia y finaliza. La nueva copia se inicia en el próximo arranque o inicio de sesión mediante el mecanismo de persistencia registrado.
La persistencia se establece mediante las claves de registro Run y RunOnce (HKCU\Software\Microsoft\Windows\CurrentVersion\Run y \RunOnce) y dos tipos de tareas programadas. La primera es un lanzador de un solo uso: crea una tarea que se activa 5 segundos después de su creación y expira después de 20 segundos, un mecanismo de ejecución única para iniciar la copia recién instalada. La segunda es la persistencia real: una tarea repetitiva con un intervalo aleatorio de 8 a 11 minutos (PT{8-11}M) y sin vencimiento, lo que garantiza que el loader se reinicie si se termina. Ambas tareas se crean escribiendo una definición de tarea XML completa en un archivo temporal ({GUID}.xml), ejecutando schtasks /Create /TN “<name>” /XML “<path>” /F, esperando hasta 5 segundos a que se complete y luego eliminando el archivo XML. El nivel de privilegio se establece dinámicamente: HighestAvailable si el loader se ejecuta como administrador, LeastPrivilege en caso contrario. Un mutex (configurable mediante la configuración) evita que se ejecuten varias instancias simultáneamente, con un tiempo de espera de adquisición de 30 segundos.
Evasión de defensas
Durante la rutina de instalación, el loader arma un comando de PowerShell que agrega varias exclusiones de Defender en una sola ejecución: exclusiones de ruta para LocalApplicationData, el directorio temporal del sistema y la propia ruta de instalación del loader, además de una exclusión de proceso para el ejecutable del loader. El comando se ejecuta mediante powershell.exe -ExecutionPolicy Bypass -WindowStyle Hidden -NoProfile.
El loader también parchea AMSI y ETW a nivel de la API nativa. Primero verifica el número de compilación de Windows desde el registro; si la compilación es 26100 o superior (Windows 11 24H2), resuelve NtManageHotPatch desde ntdll.dll y sobrescribe su prólogo con un stub que devuelve STATUS_NOT_SUPPORTED (0xC00000BB). En sistemas de 64 bits, el parche es mov eax, 0xC00000BB; ret; en sistemas de 32 bits, mov eax, 0xC00000BB; ret 0x10. Antes de escribir, verifica que el prólogo de la función esté intacto comprobando que los primeros bytes sean 0x4C 0x8B (64 bits) o 0xB8 (32 bits). Esto apunta al mecanismo de carga de AMSI basado en hot-patch introducido en Windows 11 24H2.
Por separado, en sistemas de 64 bits, el loader parchea EtwEventWrite escaneando patrones en ntdll.dll en busca de la firma de su prólogo de función (con bytes comodín para los desplazamientos reubicables) y sobrescribiendo el punto de entrada con xor rax, rax; ret, lo que convierte todo el registro de eventos de ETW en una operación nula. Ambos parches usan VirtualProtectEx para cambiar la protección de memoria, WriteProcessMemory para aplicar el parche y FlushInstructionCache para asegurar que el código modificado surta efecto.
Entrega de payload adicional
El loader puede recibir payloads adicionales del C2 y ejecutarlos en tres modos distintos según las marcas (flags) que defina el operador.
El modo de inyección en memoria descarga los bytes del payload directamente en la memoria, elige al azar entre MSBuild.exe e InstallUtil.exe como objetivo de inyección, y realiza un vaciado de proceso (RunPE) para ejecutar el payload sin escribirlo en el disco. Se registra el PID del proceso inyectado para que el loader pueda administrarlo o terminarlo más adelante.
El modo PowerShell descarga el payload a un archivo temporal con extensión .ps1, espera 2 segundos y luego lo ejecuta mediante powershell.exe -NoProfile -ExecutionPolicy ByPass -File y lo registra como una tarea programada de un solo uso.
El modo de colocación de archivos descarga el payload, lo descifra (AES), lo escribe en el disco y lo registra como una tarea programada para su ejecución.
Autolimpieza
El loader puede finalizar procesos por PID con un retraso configurable (cmd /c timeout 10 && taskkill /F /PID), eliminarse a sí mismo (cmd /c timeout 15 && del /a /q /f) y eliminar sus tareas programadas (schtasks /Delete /TN).
Conclusión
Lo que comenzó como un correo de malspam evolucionó rápidamente hacia un ataque más deliberado, probablemente con la exfiltración de datos como objetivo final y, si no se detiene a tiempo, una intrusión manual completa.
Esto es un fuerte recordatorio de por qué importa la defensa en profundidad. Configurar un objeto de directiva de grupo (GPO) en Active Directory para forzar que los archivos de script como .vbs, .hta y .js se abran en el Bloc de notas de forma predeterminada puede detener a un actor de amenazas en la primera etapa, evitando que se lleguen a descargar payloads adicionales.
En cuanto a la seguridad del correo electrónico, las organizaciones deberían considerar implementar registros DMARC, DKIM y SPF para reducir la probabilidad de que correos falsificados o maliciosos lleguen a los usuarios finales. Más allá de eso, una solución de puerta de enlace de correo capaz de aplicar análisis en un entorno aislado a los archivos adjuntos y enlaces antes de la entrega agrega otra capa importante de protección. Bloquear o poner en cuarentena los documentos de Office con macros habilitadas y los archivos adjuntos basados en scripts en la puerta de enlace de correo es un control de bajo esfuerzo que vale la pena. Cuando sea posible, habilitar Safe Links y Safe Attachments (o su equivalente) garantiza que las URL y los archivos se inspeccionen en el momento del clic en lugar de en el momento de la entrega.
Recomendaciones
- Configura una GPO que obligue a los archivos .js, .vbs y .hta a abrirse en el Bloc de notas o Notepad++ de forma predeterminada, neutralizando su capacidad de ejecutarse sin esfuerzo adicional del usuario.
- Realiza capacitaciones regulares de concientización sobre phishing y lleva a cabo campañas internas simuladas de phishing para fortalecer a los empleados. La capa humana sigue siendo el punto de entrada más explotado de forma constante.
- Implementa controles de autenticación de correo, incluidos SPF, DKIM y DMARC, para reforzar el perímetro del correo electrónico y reducir la exposición a ataques de suplantación e impersonación.
- Implementa una puerta de enlace de seguridad de correo con capacidad de análisis en un entorno aislado y considera bloquear o poner en cuarentena los archivos adjuntos basados en scripts y con macros habilitadas en la capa de correo antes de que lleguen al endpoint.
Detecciones
- Alerta sobre archivos .js, .vbs y .hta como proceso secundario de explorer.exe.
- Monitorea que wscript.exe invoque el siguiente directorio "C:\Users\Public\ y genere PowerShell codificado y ofuscado.
Huntress Labs publicó indicadores de compromiso para esta amenaza en nuestro Github.
Indicadores de compromiso (IoC)
|
Indicador |
Descripción |
|
Bestellung_2026.html |
Adjunto HTML malicioso |
|
fostercareintheus.optimizationprime[.]com |
Etapa de redirector |
|
bth.startthewave[.]org |
Kit de entrega |
|
pengajian.muliastudy[.]com/images/edu/u.php |
Esto entrega el archivo ZIP; es la entrega del payload |
|
A021185521S210008-11521.zip |
Archivo ZIP de entrega distribuido por el kit de malspam (u.php) |
|
A021185521S210008-11521.js |
Cargador en JavaScript |
|
ktncm.js |
Cargador en JavaScript |
|
zkrbx.txt |
Archivo de preparación |
|
gglhn.txt |
Archivo de preparación |
|
nlbzl.ps1 |
Dropper en PowerShell |
|
shmvg_01.ps1 |
Stager en PowerShell |
|
andrefelipedonascime1778799406970.2241107.meusitehostgator[.]com[.]br |
Distribuye los archivos de preparación 01.txt, 02.txt, 03.txt |
|
%USERPROFILE%\AppData\LocalLow\LocalLow Windows\Program Rules\Program Rules NVIDEO\Program Rules\Program Rules NVIDEO |
Directorio de preparación del cargador: ruta anidada con temática de NVIDIA usada para imitar una ruta de instalación de controlador legítima |
|
catalogo.castrouria[.]com |
Distribuye bl.txt (el cargador empaquetado), entrega del payload |
|
SHA256: D5B7247C497788CF0031CEB06E3DF77A45FEF59F1E49633DC7159816D64759B5 C61B1941CF756EB7551F7C661743802362728B785ADC22E860D269713DFB01A6 C356AFF1A01C2B0DA472E584C8E3C8F875B9A24280435D42836A77B19F5A8C18 F1C3EBE78BD8C38559BF3CFCC9A9FA37D221E31780774A3787E26160A61F5348 E91FB249AA97BE5C7931E430781167EDFE7BA804720B5F643E6AB70B7E6E74DD |
Pin de certificado C2 codificado de forma fija en el cargador |
|
P@55w0rd! |
Contraseña AES codificada de forma fija usada por el cargador para la derivación de comunicaciones C2 mediante PBKDF2 |
|
xtadts.ddns[.]net |
Servidor C2 del cargador |
|
afxwd.ddns[.]net |
Servidor C2 del cargador |
|
Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.0; WOW64; Trident/4.0; SLCC1; .NET CLR 2.0.50727; Media Center PC 5.0; .NET CLR 3.5.21022; .NET CLR 3.5.30729; .NET CLR 3.0.30618; InfoPath.2; OfficeLiveConnector.1.3; OfficeLivePatch.0.0) |
User-Agent de IE8 codificado de forma fija, usado por el cargador para obtener el payload |