El 29 de agosto de 2025, analistas de Huntress encontraron una variante de ransomware nunca antes vista llamada "Obscura". Este nombre se tomó de la nota de rescate (README_Obscura.txt), que también hacía varias referencias a Obscura en su contenido.
Mientras investigaban esta variante de ransomware, los analistas no encontraron ninguna referencia pública a una variante de ransomware llamada Obscura.
El archivo ejecutable de ransomware se vio por primera vez ejecutándose en varios endpoints de la organización víctima. Esta red tenía un despliegue limitado del Huntress Agent, lo que afectó tanto la detección como la respuesta, e inhibió la capacidad del SOC para responder de forma efectiva. Esto también limitó nuestra visibilidad sobre ciertos aspectos del ataque, incluyendo el vector de acceso inicial.
Sin embargo, lo que sí pudimos ver fue que el archivo ejecutable de ransomware se encontraba en el controlador de dominio, en la ruta:
C:\WINDOWS\sysvol\sysvol\[domain].local\scripts\
En el incidente observado por el Huntress SOC, el archivo ejecutable de ransomware llevaba el nombre del dominio en el que se encontró, en un aparente intento de pasar desapercibido (por esta razón, no identificamos públicamente el nombre de este ejecutable). El ejecutable es un binario de Go (incluye un ID de compilación de Go) y contiene varias rutas de archivos, como:
/run/media/veracrypt1/Backups/Obscura/Locker/windows/locker/
/run/media/veracrypt1/Locker Deps/go1.15.linux-amd64/go/src/os/exec
La ubicación del binario en el controlador de dominio se compartió como la carpeta NETLOGON, que pone a disposición de los usuarios los scripts y los objetos de política de grupo (GPO). Además, el contenido de la carpeta se replica automáticamente en todos los controladores de dominio para mantener la consistencia. Sin embargo, esto también significó que el archivo ejecutable de ransomware se desplegó automáticamente en toda la infraestructura.
Se creó una tarea programada llamada SystemUpdate en varios endpoints de la red, incluyendo el controlador de dominio, para ejecutar el binario de ransomware desde el recurso compartido NETLOGON.
En una de las máquinas del usuario, el actor de amenazas creó una tarea programada llamada "iJHcEkAG". La tarea ejecuta el comando cmd.exe /C netsh firewall set service type = remotedesktop mode = enable > \Windows\Temp\SJYfXB 2>&1 para habilitar el acceso por protocolo de escritorio remoto a través del firewall de Windows.
Al ejecutarse, el archivo ejecutable de ransomware ejecuta el siguiente comando incrustado en un intento de deshabilitar la recuperación en el endpoint:
cmd.exe /c vssadmin delete shadows /all /quiet
La nota de rescate en sí está contenida en el binario de ransomware como una cadena codificada en base64.
Contenido de la nota de ransomware
Análisis técnico
Cuando se ejecuta el binario, este verifica el estado de una variable de entorno llamada DAEMON. Si el valor es 1, el binario deja la nota de rescate y continúa con el cifrado. Si no está presente o tiene el valor 0, ejecuta una serie de funciones para preparar el equipo para el cifrado. La función main_run() se ejecuta en modo daemon con DAEMON=1 configurado. Esta obtiene la clave pública de 32 bytes del actor de amenazas al decodificar una cadena base64 fija incrustada en el ejecutable, y luego realiza un reconocimiento del sistema enumerando todos los dispositivos de almacenamiento y calculando sus capacidades para crear un mapa completo de todas las unidades disponibles y sus tamaños de almacenamiento para el cifrado.
- El ransomware decodifica una nota de rescate codificada en base64 a partir de los datos incrustados y la escribe en C:\README-OBSCURA.txt. Si la decodificación falla, imprime “failed to decode note: %s” y se cierra.
Figura 1: Decodificación de la nota de rescate en base64
La función main_windows_api_IsRunAsAdmin() realiza una verificación de privilegios de Windows mediante dos llamadas secuenciales a la API de Windows para determinar si el proceso actual posee derechos administrativos. La función primero llama a AllocateAndInitializeSid() para crear un identificador de seguridad para el grupo local de Administradores, usando SECURITY_BUILTIN_DOMAIN_RID (32) como autoridad, DOMAIN_ALIAS_RID_ADMINS (544) como subautoridad, y un conteo de autoridad de 2. Después de crear el SID correctamente, la función llama a CheckTokenMembership() para verificar si el token de autenticación del proceso actual pertenece al grupo de Administradores, y devuelve un valor booleano que indica el estado administrativo. Si alguna de las llamadas a la API falla, la función devuelve mensajes de error descriptivos como "AllocateAndInitializeSid failed: %v" o "CheckTokenMembership failed: %v". Cuando la verificación de privilegios determina que el proceso carece de derechos administrativos, el ransomware imprime "[!!!] user not admin. exit [!!!]" y termina de inmediato su ejecución. Esto representa un requisito estricto sin mecanismo de omisión, ya que el ransomware necesita privilegios administrativos para terminar procesos del sistema, eliminar copias de volumen en la sombra con cmd.exe /c vssadmin delete shadows /all /quiet, y acceder a las API del sistema necesarias para la detección de dominio y la creación de procesos daemon.
Figura 2: Fragmento de main_windows_api_IsRunAsAdmin que configura las constantes de seguridad de Windows (2, 32, 544) para crear el SID del grupo de Administradores con fines de verificación de privilegios
Después de confirmar los privilegios administrativos, el ransomware recopila información crítica del sistema llamando a GetSystemInfo() a través de la API de Windows. Específicamente extrae el valor dwNumberOfProcessors, que indica la cantidad de núcleos de CPU disponibles en el sistema y se usa para optimizar la estrategia de subprocesos durante la fase de cifrado. La fase de preparación del sistema continúa con una terminación agresiva de procesos dirigida a aplicaciones de seguridad y bases de datos que podrían interferir con el proceso de cifrado. El ransomware llama a main_windows_api_KillProcesses(), que recorre una lista predefinida de 120 procesos objetivo. El ‘*’ que aparece en algunos nombres de procesos se usa para indicar un comodín en la coincidencia de cadenas.
|
WinDefend |
MsMpEng |
MpCmdRun |
CSFalconService |
SentinelAgent |
|
bdagent |
McAfee |
Avp |
SymCorpUI |
ccSvcHst |
|
AMService |
Emsisoft* |
csrss_guard |
traps* |
cyserver |
|
cytray |
esensor* |
elastic-endpoint* |
f-secure* |
fsav* |
|
360tray |
360sd |
ksafe |
avguard |
avgnt |
|
avast* |
Crowdstrike* |
falcon-sensor |
glasswire* |
ZoneAlarm |
|
comodo* |
Veeam* |
VeeamTransportSvc |
VeeamBackupSvc |
AcrSch2Svc |
|
Afcdpsrv |
AcronisAgent |
AcronsiBackupAgent |
Altaro* |
Nakivo* |
|
Iperius* |
MacriumService |
EaseUS* |
CrashPlanService |
veritas* |
|
NetBackup* |
BackupExec |
BEDatabase |
BETracker |
CommVault* |
|
Cvd |
Galaxy* |
Snapman |
StorageCraft* |
druva* |
|
rubrik* |
synmedia* |
cloudberry* |
Dbagent |
Datto* |
|
SIRAgent |
MSSQL* |
SQLSERVERAGENT |
SQLWriter |
SQLBrowser |
|
OracleService* |
OracleVSSWriter |
OracleXETNSListener |
postgresql* |
pg_ctl |
|
mysql |
mysqld |
MariaDB |
mariadb |
percona* |
|
ccbackup* |
cbrestore* |
ABBService |
Splunkd |
SplunkForwarder |
|
ossec* |
wazuh* |
agent_m* |
Zabbix* |
nagios |
|
Nrpe |
prtg* |
SolarWinds* |
greylog* |
Nxlog |
|
Winlogon |
EventLog |
Sysmon* |
VMwareHostd |
VMwareAuthdService |
|
VMwareNatService |
VMwareUSBArbZService |
vmware-hostd |
VBoxSDS |
VBoxHeadless |
|
VBox* |
vmms |
Vmicheartbeat |
Vmickvpexchange |
Vmicrdv |
|
vmicshutdown |
com.docker.service |
gitlab-runner |
jenkins* |
TeamCity* |
|
bamboo* |
octopus* |
rundeck* |
ansible* |
salt-minion |
|
ActiveBackup* |
Syno* |
SynologyDrive |
SynologyQuickConnect |
Cuando un nombre de proceso coincide con el patrón objetivo anterior, la función ejecuta la secuencia de terminación llamando a OpenProcess(PROCESS_TERMINATE, FALSE, processID) para obtener un handle hacia el proceso objetivo con privilegios de terminación. Si el handle se obtiene con éxito, llama a TerminateProcess(process_handle, 1) para forzar la terminación del proceso con código de salida 1 e imprime un mensaje de éxito que muestra el ID y el nombre del proceso en el formato “[+] killed pid %d (%s)”. Si la terminación falla, la función devuelve un mensaje de error que indica “failed to terminate process” pero continúa intentando eliminar otros procesos objetivo.
El ransomware usa la API de Windows DsRoleGetPrimaryDomainInformation para determinar el rol del equipo dentro de un dominio. Esto se hace en la función main_windows_api_GetPCRole(), que asigna los roles de dominio de Windows a valores internos.
Sin importar el rol de dominio detectado, cada rama ejecuta la misma secuencia: carga un mensaje de texto específico del rol y muestra los mensajes de estado correspondientes antes de pasar de inmediato a la fase de creación del daemon. Estos mensajes sugieren capacidades de propagación en red que se tenían previstas, pero que nunca se implementaron por completo o que representan un desarrollo incompleto, ya que el código real no contiene ninguna funcionalidad de movimiento lateral más allá de la rutina de cifrado local.
- PC independiente: muestra [+] detect standalone pc, lo que indica que el sistema no está conectado a un dominio
- PC en dominio: muestra [+] detect pc in domain. run transfer to dc., lo que sugiere una transferencia hacia controladores de dominio
- Controlador de dominio de respaldo: muestra [+] detect BDC. run transfer to PDC., lo que implica propagación hacia el controlador de dominio primario
- Controlador de dominio primario: muestra [+] detect PDC. run transfer to all pc in domain. lo que indica propagación hacia todos los equipos del dominio
Hay algunas estrategias de cifrado entre las que elige el binario: EncryptFull o EncryptPart. Ambas funciones usan la función encryptFileRange() con argumentos diferentes.
Tienen una clave pública del par (Curve25519) y, durante el cifrado, generan una clave privada efímera usando main_windows_api_generateEphemeralKeyPair(). Estas se usan para generar la clave XChaCha20 que luego se usa para cifrar archivos. Para lograrlo, usan multiplicación escalar (X25519) entre la clave privada y su clave pública para generar un secreto compartido de 32 bytes. Ese secreto compartido, junto con un nonce aleatorio de 24 bytes, se usan como parámetros para el cifrado ChaCha del archivo. Antes de volver a escribir el archivo cifrado en el disco, agregan un pie de página de 64 bytes compuesto por:
- OBSCURA!
- Clave pública de 32 bytes
- Nonce de 24 bytes
Figura 3: Muestra del archivo cifrado
Dado que poseen la clave privada del par, pueden usar este pie de página para rederivar la clave ChaCha20 que se usó para cifrar el archivo.
El ransomware Obscura implementa un mecanismo de filtrado de archivos diseñado para maximizar el daño a los datos del usuario mientras preserva la funcionalidad del sistema.
El sistema de filtrado funciona a través de la función main_hasExcludedExtension(), que realiza una comparación de extensiones sin distinguir mayúsculas y minúsculas contra una lista de exclusión codificada. La función extrae la extensión del archivo y la compara con 15 extensiones predefinidas:
Ejecutables y bibliotecas del sistema:
- .exe - Aplicaciones ejecutables
- .dll - Bibliotecas de enlace dinámico
- .msi - Paquetes de instalación de Microsoft
- .sys - Archivos de controladores del sistema
Componentes de arranque y firmware:
- .efi - Archivos de firmware UEFI
- .boot - Archivos de configuración de arranque
- .iso - Archivos de imagen de disco ISO
- .rom - Archivos de firmware ROM
- .bin - Archivos binarios del sistema
Configuración y utilidades del sistema:
- .ini - Archivos de configuración
- .cfg - Archivos de configuración
- .lnk - Archivos de acceso directo de Windows
- .hosts - Archivos de configuración de red
- .swapfile - Archivos de memoria virtual de Windows
Autoprotección del ransomware:
- .obscura - archivos cifrados con la extensión del ransomware
Obscura y otras nuevas variantes de ransomware
Obscura es una de varias variantes nuevas de ransomware que Huntress ha visto surgir en los últimos meses, incluyendo Crux ransomware y Cephalus ransomware. Esto podría deberse a varios factores. Los actores de amenazas se rebautizan constantemente y lanzan nuevas variantes de ransomware después de que las interrupciones de las fuerzas del orden afectan el ecosistema. Además, a medida que nuestra base de clientes sigue creciendo, obtenemos más visibilidad de más variantes de ransomware.
De cualquier forma, lo presentado en esta publicación es solo un medio para desplegar ransomware. Las organizaciones deben monitorear de cerca sus controladores de dominio y estar atentas a la adición de archivos nuevos, así como a la modificación de archivos existentes, incluyendo las GPO. Los administradores también deben monitorear los controladores de dominio, así como otros endpoints (servidores, estaciones de trabajo) en busca de accesos inusuales o sospechosos.
IOC
|
Indicador |
Descripción |
|
[company name].exe |
Ejecutable del ransomware |
|
README_Obscura.txt |
Nota de rescate (contenido a continuación) |
|
C:\WINDOWS\sysvol\sysvol\[domain].local\scripts\ |
Carpeta de operaciones del actor de amenazas |
|
DESKTOP-XNBSHKJ2 |
Posible nombre de la estación de trabajo del actor de amenazas |