David Rosas — investigación
13 oct 2026·WannaCry·Ingeniería inversa

Un salto que no salta y un ZIP al que le faltan 177 KB.

Alguien cogió el gusano de WannaCry y le quitó el interruptor de apagado con un editor hexadecimal: la URL del dominio a ceros y el salto que la comprobaba anulado. Dos parches, y el sinkhole que frenó la epidemia de mayo de 2017 deja de servir. Pero en el mismo paquete, al recurso ZIP que lleva el motor de cifrado le faltan los últimos 177 KB y con ellos el directorio central, así que la propia librería ZIP del malware no puede abrirlo. El resultado es un gusano que se propaga para siempre y no cifra nada.

Muestra d724d8cc6420f06e8a48752f0da11c66 Vista por primera vez 14 may 2017 Familia WannaCry · mssecsvc.exe Objetivo Windows · PE32 nativo

Qué fue WannaCry, y qué dicen las fuentes

Conviene empezar por el contexto, porque es lo que da sentido a todo lo demás. WannaCry fue el ransomware que se comportó como un gusano: en vez de esperar a que alguien abriera un adjunto, se copiaba por la red él solo, aprovechando un fallo de SMBv1. El 12 de mayo de 2017 eso bastó para parar hospitales, fábricas y ferrocarriles en un fin de semana.

Lo que está documentado públicamente, con las fuentes al lado:

Lo que las fuentes no dicen. Esta muestra no es el WannaCry del 12 de mayo: es una variante parcheada, y las fuentes públicas no establecen quién la parcheó. Mandiant describió el 14 de mayo de 2017 una variante «que no contiene la funcionalidad de killswitch por contacto con el dominio» y añadió literalmente que ese cambio «puede haber sido realizado por un tercero después de compilar el malware, en lugar de por los operadores». De la misma variante dijo que «el componente de ransomware parece corrupto y no funciona en entornos de prueba». Las dos observaciones coinciden con lo que tengo delante.

Al día siguiente, Bitdefender sostuvo en prensa que las variantes con y sin interruptor las operaba el mismo grupo, porque los monederos de rescate eran los mismos. Ese argumento no distingue nada, y se ve en el binario: las tres direcciones de Bitcoin están fijas dentro de tasksche.exe, que es justo la parte que el parche no toca. Que los monederos coincidan es lo que cabe esperar sea quien sea quien edite el dropper. Así que lo dejo donde lo dejan las fuentes: no se sabe.

Una URL a ceros y un salto anulado

El fichero es el componente gusano, mssecsvc.exe: PE32 nativo de 3.723.264 bytes, cuatro secciones, base 0x400000. El 93 % de su tamaño es la sección de recursos, porque lleva dentro el instalador del ransomware como un MZ en claro.

El comportamiento original, en 0x408140, es este:

mov  esi, 0x4313d0        ; "http://www.iuqerfsodp9ifjaposdfjhgosurijfaewrwergwea.com"
rep  movsd                ; copia la URL a la pila
call InternetOpenUrlA
test edi, edi
jne  0x4081bc             ; el dominio responde -> cerrar handles y SALIR
call sub_408090           ; no responde -> infectar

En esta muestra hay dos cambios, y los dos se ven directamente en el fichero:

DóndeOriginalEsta muestraEfecto
0x4313d0
(offset 0x313d0)
57 bytes con la URL00 00 00 …InternetOpenUrlA("") falla siempre; no hay tráfico HTTP ni consulta DNS
0x4081a5
(offset 0x81a5)
75 15 — jne 0x4081bc75 00 — jne +0las dos ramas caen en la infección
ORIGINAL ESTA MUESTRA mov esi, 0x4313d0 57 B con la URL del dominio mov esi, 0x4313d0 57 B a cero InternetOpenUrlA(url) el dominio responde o no InternetOpenUrlA("") siempre falla jne 0x4081bc si responde: salir jne +0 (75 00) no salta nunca sub_408090: infectar solo si no responde sub_408090: infectar siempre
Cualquiera de los dos parches bastaba por separado. Con los dos, restaurar la URL tampoco devuelve el interruptor: el salto ya no existe.

La rama de salida original sigue ahí, como código muerto justo después del ret, en 0x4081bc: ff d6 57 ff d6 5f 33 c0 5e 83 c4 50 c2 10 00. Nadie la borró, simplemente ya no se llega.

Y el decompilador lo confirma eliminando la comparación entera, porque una condición cuyas dos ramas van al mismo sitio no es una condición:

uVar1 = InternetOpenA(0,1);
InternetOpenUrlA(uVar1, &uStack_64, 0, 0, 0x84000000, 0);
InternetCloseHandle(uVar1);
InternetCloseHandle(0);
FUN_00408090();          // infección incondicional
return 0;

Un número que no cuadra. La prensa técnica de mayo de 2017 recogió que la variante «parcheada» se diferenciaba de la original en solo 2 bytes. En esta muestra los bytes cambiados son 58: uno en el salto y 57 en la ranura de la URL. Las dos cosas pueden ser ciertas a la vez, porque circularon varias variantes y el parche del salto por sí solo ya basta —de hecho es el mínimo posible—. Lo que no puedo hacer es dar por descrita esta muestra con una cifra que no es la suya.

Dónde estaba el interruptor, exactamente

La forma en que se cuenta normalmente es «el interruptor impedía el cifrado». Es impreciso, y esta muestra lo demuestra por construcción.

La comprobación vive en el WinMain del gusano, antes de crear el servicio y antes de escribir el instalador en disco. Lo que había detrás del salto no era el cifrado: era todo. Crear el servicio mssecsvc2.0, arrancar la propagación por SMB y dejar caer tasksche.exe en C:\WINDOWS para ejecutarlo con /i. Si el dominio respondía, no pasaba nada de eso. Por eso Microsoft habla del dropper y no del cifrador.

Al anular el salto, lo que se desbloquea es la cadena completa: el equipo se instala, se propaga y suelta el payload siempre. Que el cifrado no llegue a ocurrir, en esta muestra, no tiene nada que ver con el interruptor — es un accidente distinto, y es la sección 05.

Dos cosas más que el parche deja a la vista. La primera es para qué servía probablemente la comprobación: un freno antisandbox. Muchos entornos de análisis responden a cualquier dominio para no dejar ir tráfico fuera, así que en un sandbox el dominio «resuelve» y el malware se queda quieto. El efecto de sinkhole del 12 de mayo fue, en ese caso, el mismo mecanismo usado al revés. La segunda es más incómoda: ese freno global no tenía ninguna protección de integridad. Ni una suma de verificación sobre su propio código, ni una comprobación de la longitud de la URL, ni una segunda comprobación en otro sitio. Un byte.

Un hilo para la LAN y 128 para internet

Con el freno quitado, lo que queda operativo es la parte que de verdad hizo daño en 2017. Arranca en 0x407bd0 y reparte el trabajo en dos frentes: un hilo para la red local y 128 para internet, lanzados de uno en uno cada dos segundos.

La lista de objetivos internos la construye sub_409160 con GetAdaptersInfo y GetPerAdapterInfo, y por cada adaptador agrega tres cosas:

Luego ordena, quita duplicados y ataca con un máximo de 10 hilos a la vez y 50 ms entre lanzamientos. Ese detalle de la puerta de enlace y el DNS es el que explica por qué una máscara pequeña no contenía nada: a través del gateway y del DNS alcanza otras VLAN aunque el segmento local sea diminuto.

Los 128 hilos de internet son más sencillos y más ruidosos. Generan una IPv4 aleatoria y solo descartan el primer octeto 127 y los ≥ 224. No excluyen RFC1918, así que también apuntan a rangos privados que estén enrutados por VPN o MPLS. Si el 445 responde, barren el /24 entero, de .1 a .254. Cada intento de explotación tiene un minuto de gracia de una hora —0x36ee80 ms— y después un TerminateThread.

El volumen que eso genera es medible, y está registrado públicamente para este mismo hash: 18.506 direcciones IP distintas contactadas en las ejecuciones en sandbox agregadas por VirusTotal, con firmas de Suricata y Snort de barrido SMB y de EternalBlue. No es sigiloso y no pretende serlo.

De la explotación en sí digo lo que puedo: los búferes que se entregan a cada host con el 445 abierto se preparan en 0x407a20 y son una DLL x86 de 0x4060 bytes, una x64 de 0xc8a4 y una copia del propio ejecutable. Las cadenas __TREEID__PLACEHOLDER__ y __USERID__PLACEHOLDER__ son las ranuras que se rellenan en la plantilla SMB. No decompilé el interior de los hilos de explotación: que la cadena sea EternalBlue → DoublePulsar → ejecución remota es comportamiento documentado de la familia, no algo que haya verificado instrucción a instrucción aquí.

El ZIP al que le faltan 177 KB

Aquí está la otra mitad de la historia, y es la que convierte esta muestra en una curiosidad en vez de en una emergencia.

Lo primero que hay que deshacer es una confusión habitual: tasksche.exe no cifra. Es un instalador y un cargador. El cifrado vive en t.wnry, una DLL cifrada que tasksche descifra en memoria y cuya exportación TaskStart es la que hace el trabajo. Y t.wnry no está en el binario: viene dentro de un recurso ZIP —tipo XIA, ID 2058— protegido con la contraseña WNcry@2ol7, fija y pública desde 2017.

Ese ZIP está roto. No «sospechosamente raro»: roto, y de una forma concreta.

PruebaResultado
Cabeceras locales PK 03 0432, todas antes del daño
Directorio central PK 01 020 apariciones
Fin de directorio PK 05 060 apariciones
Inicio del dañooffset 0x31e2b7 (3.269.303), el 94,9 % del fichero
Cola dañada177.022 bytes, entropía 7,9986 bits/byte, sin una sola cabecera
Contraseña WNcry@2ol7correcta: 31 entradas descifran con CRC válido
t.wnry, u.wnry, taskdl.exe, taskse.exeno existen en el fichero
ZIP XIA/2058 — 3.446.325 B íntegro hasta 0x31e2b7 177.022 B · entropía 7,9986 · sin cabeceras Se recupera, cabecera a cabecera b.wnry · c.wnry · r.wnry msg/*.wnry ×28 s.wnry hasta el 99 % No existe en el fichero t.wnry · u.wnry taskdl.exe · taskse.exe directorio central y EOCD minizip busca PK 05 06 en los últimos 64 KB. No lo encuentra: unzOpen devuelve NULL. Cero ficheros extraídos, ni las notas. tasksche sigue, y falla al abrir t.wnry.
El contenido recuperable está casi todo ahí. Lo que falta son las cuatro piezas ejecutables y la tabla que la librería del malware necesita para encontrarlas.

La cadena de consecuencias es corta y mecánica. La función que abre el recurso llama a minizip —la cadena "unzip 0.15 Copyright 1998 Gilles Vollant" está en el binario—, y minizip empieza por SearchCentralDir, en 0x405edf, que busca 'P' 'K' 0x05 0x06 en los últimos 64 KB del fichero. Al no encontrarlo devuelve -1, unzOpen devuelve NULL y no se extrae ni un fichero, ni siquiera las notas de rescate. El WinMain de tasksche ignora ese valor de retorno y continúa tan tranquilo hasta que falla al abrir t.wnry, momento en el que la función devuelve NULL y la llamada a TaskStart nunca se produce.

El detalle que más me gusta de todo el análisis es este: mis extractores sí recuperan 31 ficheros, porque recorren las cabeceras locales una a una y no necesitan el índice. El malware no puede, porque depende del directorio central. La diferencia entre un ransomware y una molestia, en esta muestra, es una decisión de diseño de una librería de 1998.

Lo que no se puede determinar desde el estático es la causa del daño. Que sea un bloque continuo de datos de máxima entropía hasta el final del fichero apunta a una sobrescritura y no a un truncamiento, pero eso es una inferencia sobre la forma, no una prueba del motivo. Accidente al parchear, accidente al transferir o neutralización deliberada son las tres hipótesis, y el fichero no las separa.

Dos matices que importan antes de dar la muestra por inofensiva. El primero: hay un caso límite. Si el directorio de trabajo ya contuviera un t.wnry válido de una infección anterior con una variante funcional, tasksche lo cargaría y cifraría. Seguiría sin poder mostrar la pantalla de rescate, porque u.wnry tampoco se extrae: la víctima se quedaría con los ficheros cifrados y sin instrucciones. El segundo: el riesgo real de esta muestra no es el cifrado, es el exploit. EternalBlue contra un equipo sin parchear puede tumbarlo con un pantallazo azul, y 128 hilos buscando el 445 por internet siguen haciendo exactamente eso.

Las ejecuciones públicas del mismo hash concuerdan punto por punto: el servicio mssecsvc2.0 se crea, C:\WINDOWS\tasksche.exe /i se lanza como hijo de la muestra, la alerta de barrido SMB salta, y tasksche no deja casi rastro — ni un fichero .WNCRY, ni @WanaDecryptor@.exe, ni taskdl.exe, ni taskse.exe, ni fondo de pantalla cambiado.

Una llave por fichero, un monedero para todos

Aunque esta variante no cifre, el esquema criptográfico sigue siendo legible, porque tasksche lo usa para protegerse a sí mismo. Eso es lo único verificado aquí, y conviene separarlo de lo que es documentación de la familia.

Verificado en esta muestra. La función que lee t.wnry, en 0x4014a6, importa una llave RSA embebida con CryptImportKey, descifra con ella un bloque de 256 bytes —el tamaño de RSA-2048— que contiene una llave AES de 16 bytes, y con esa llave descifra la DLL. El contenedor tiene esta forma:

WANACRY!   0x100   clave AES cifrada con RSA   tipo   tamaño plano   DLL cifrada
  8 B      4 B               256 B              4 B      8 B          AES-128

Es decir, un esquema híbrido de manual: AES porque es rápido, RSA para que la llave AES solo la pueda recuperar quien tenga la privada. Lo interesante es que el malware se aplica a sí mismo el mismo tratamiento que a las víctimas, y que el formato de los ficheros .WNCRY es este mismo contenedor, cabecera WANACRY! incluida. Los autores reutilizaron su propio formato.

Documentación pública, no verificado aquí —porque t.wnry falta—: el cifrado de los ficheros de la víctima encadena tres niveles.

RSA-2048 del atacante igual en todas las víctimas par RSA de la víctima .pky y .eky · distinto por equipo AES-128 por fichero distinta en cada archivo archivo.ext.WNCRY WANACRY! | clave | tipo | tamaño Y el cobro no sigue el esquema Tres monederos BTC, fijos dentro de tasksche.exe, compartidos por todas las víctimas. Ningún identificador por equipo: nadie podía ligar un pago con una .eky.
La criptografía es por equipo y por fichero. La caja es una sola, para todo el mundo. Las dos mitades del negocio no encajan.

De ahí salen cuatro consecuencias que explican bastante bien la historia de 2017:

Y queda el contraste que más dice del conjunto. La criptografía está pensada por equipo; el cobro, no. Las tres direcciones de Bitcoin están fijas en tasksche.exe, en los offsets 0xf440, 0xf464 y 0xf488, una detrás de otra con 36 bytes de paso, y la nota las referencia con un %s que el descifrador rellena en ejecución. No hay identificador por víctima en ninguna parte. El operador no tenía forma fiable de saber qué pago correspondía a qué .eky, lo que explica a la vez por qué pagar casi nunca devolvía los ficheros y por qué el total se quedó en el entorno de los 109.000–140.000 dólares con más de 200.000 máquinas infectadas.

En esta variante, por supuesto, nada de esto se ejecuta: sin t.wnry no hay generación de llaves ni cifrado.

Una vacuna que es un servicio que no hace nada

Hay un detalle en la instalación que se puede usar en contra del gusano. En 0x407c40, el malware solo arranca el servicio que propaga si CreateServiceA tiene éxito:

eax = CreateServiceA(scm, "mssecsvc2.0", "Microsoft Security Center (2.0) Service",
                     0xf01ff, 0x10, 0x2, 0x1, "<ruta> -m security", …);
if (eax != 0) {
    StartServiceA(eax, 0, 0);     // solo así entra en modo servicio -> propagación
    CloseServiceHandle(eax);
}

Si ya existe un servicio con ese nombre, CreateServiceA falla con ERROR_SERVICE_EXISTS, el malware no lo inicia y ese equipo no propaga. La rama de servicio solo se alcanza cuando lo arranca el gestor de servicios con -m security, y eso no ocurre si el servicio nunca se creó. Así que ocupar el nombre con un servicio señuelo deshabilitado es suficiente:

sc create mssecsvc2.0 binPath= "C:\Windows\System32\svchost.exe -k vaccine_placeholder" start= disabled DisplayName= "WannaCry vaccine placeholder"
sc description mssecsvc2.0 "Placeholder defensivo: bloquea la creacion del servicio de WannaCry (mssecsvc2.0). No eliminar."

Y ahora las limitaciones, que son más importantes que el truco.

No protege contra la explotación. El equipo sigue siendo vulnerable a EternalBlue y el exploit puede provocar un pantallazo azul. Lo único que impide es que ese equipo infecte a otros.

El drop de tasksche.exe ocurre igual, porque está en otra rama. En esta variante es inofensivo; en una funcional, no.

Es específica de esta familia y de este nombre de servicio: una variante que cambie el nombre la esquiva sin esfuerzo. Y es una medida temporal y complementaria, nunca un sustituto de aplicar MS17-010 y deshabilitar SMBv1.

Está razonada sobre el decompilado, no probada en ejecución. Antes de desplegarla en ningún sitio hay que confirmarla en un entorno aislado: crear el servicio, ejecutar la muestra y verificar que no aparecen ni la alerta de barrido SMB ni las conexiones masivas al 445.

Procedencia: tres fechas y un compilador de 2010

Son dos preguntas distintas y mezclarlas es el error fácil: quién escribió el kit y quién parcheó esta build. De la primera habla el contexto público de la sección 01. De la segunda no hay nada publicado, y las fuentes que la tocan dicen expresamente que no se sabe.

Lo que sí aporta el fichero son tres fechas que encajan en una secuencia:

Compilado
2010-11-20 09:03:08 UTC — el valor del build original, intacto
Payload interno
tasksche.exe visto públicamente el 12 may 2017
Este dropper
visto por primera vez el 14 may 2017, 08:05 UTC

La marca de compilación es la pieza que cierra el método: no cambió. Quien tocó esto no recompiló nada, editó bytes sobre un binario ya construido —y la marca de 2010 es, a su vez, casi con seguridad falsa en el original, así que no dice nada sobre cuándo se escribió el código. El instalador que va dentro es del día del brote; el dropper parcheado aparece dos días después, el mismo 14 de mayo en que Europol dio la cifra de las 200.000 víctimas.

Del origen del kit, lo que llama la atención es cómo se reparten las pistas:

Dónde suele haber una pista de origenQué hay aquí
Idioma de los recursos PETodos 0x0409, el valor por defecto. No informa
Idioma de Office en las notas de rescate28 de 28 llevan \deflangfe1042 y \themelangfe1042: coreano como idioma de Asia oriental de Word
Fuentes declaradas en las notas28 de 28 declaran fuentes de juego de caracteres coreano (fcharset129), con el nombre escrito en bytes CP949: 맑은 고딕 (Malgun Gothic) en 28 de 28, 굴림체 en 5 y 바탕 (Batang) en 3
Calidad del inglés de la plantillaErrores de hablante no nativo: «But you have not so enough time.»
Notas en chinoDe calidad nativa; el resto traducidas automáticamente del inglés (Flashpoint, 2017)

Eso es consistente con un entorno de Office configurado en coreano y con la atribución oficial de diciembre de 2017. Pero hay que ponerle dos límites, y los dos son importantes.

Primero: los artefactos de idioma son triviales de plantar. Un bloque \deflangfe y un nombre de fuente en CP949 son dos cadenas de un fichero RTF. Quien quiera desorientar las pone en diez minutos. Sirven como indicio acumulativo, nunca como prueba por sí solos.

Segundo, y más concreto: esta evidencia no habla de quien parcheó la muestra. Las 28 notas viven dentro del ZIP, que está aguas arriba del parche — es contenido heredado del build original. Del autor de los dos parches, el binario no lleva ni una huella lingüística: no hay cadenas nuevas, no hay recursos añadidos, no hay sección nueva. Solo 58 bytes distintos y una cola de 177 KB que ya no sirve para nada.

Calidad como pieza de software

Merece la pena juzgarlo como lo que es, porque explica a la vez por qué paró medio mundo en un fin de semana y por qué recaudó menos que una tienda de barrio.

Lo que está bien hechoDónde se le ven las costuras
Propagación autocontenida: no necesita C2 para extenderse; la única llamada HTTP del gusano es el freno (0x408140)Ese freno es un punto único de falla sin ninguna protección de integridad: un byte en 0x4081a5 cambia el comportamiento global
Reparto limpio de componentes: gusano, instalador, motor de cifrado y interfaz de rescate, cada uno en su piezaSin ofuscación ni empaquetado: el payload viaja como un MZ en claro en el recurso R/1831
Selección de objetivos que salta segmentos por el /24 del gateway y del DNS, no solo por la máscara local (0x409160)Contraseña del ZIP fija y en claro: WNcry@2ol7, la misma para todo el mundo
Control de concurrencia y timeouts en el escaneo: 10 hilos LAN como máximo, 50 ms entre lanzamientos, una hora por intento de explotaciónEl valor de retorno del descompresor no se comprueba. unzOpen devuelve NULL y WinMain sigue como si nada hasta estrellarse contra un fichero que no existe
El motor de cifrado, cifrado con RSA-2048 + AES-128 y cargado solo en memoria (0x4014a6): el dropper nunca muestra código de cifrado en estáticoTres monederos compartidos y fijos (0xf440 en tasksche), sin identificador por víctima: la capa de cobro, que es el objetivo del producto, no está construida
Cadena de reserva para el directorio de trabajo: ProgramData, luego Intel, luego el directorio de Windows, luego temporales (0x401b5f)Escaneo ruidosísimo: 128 hilos y barridos de /24 completos encienden cualquier IDS a los pocos segundos
Dos vías para arrancarse, servicio con nombre aleatorio y, si falla, CreateProcessA, cada una con su espera (0x401ce8, 0x401eff)Esas esperas son de 60 s + 60 s sobre un mutex que en esta variante no puede existir: no hay detección de error, solo tiempo transcurrido
Reutiliza su propio contenedor WANACRY! para el motor y para los ficheros de la víctima, en vez de inventar dos formatosIOCs en claro: mssecsvc2.0, "Microsoft Security Center (2.0) Service", C:\%s\qeriuwjhrf, tasksche.exe

El veredicto: un exploit de nivel estatal con medio negocio montado encima. La parte que de verdad funcionó —la propagación— es prestada: EternalBlue, filtrado un mes antes. Lo propio es desigual: hay decisiones de ingeniería de red bien pensadas, como alcanzar otras VLAN por el gateway, junto a un gestor de errores que consiste en esperar un minuto a ver qué pasa. Y la capa de cobro, que es la razón de existir de un ransomware, nunca se diseñó: no se puede facturar sin saber quién pagó.

Sobre esta build en particular hay poco que elogiar y algo que señalar: el parche de un tercero dejó el gusano operativo y la extorsión rota, que es la peor combinación posible para quien defiende. No hay nada que pagar, no hay nota que avise, y aun así sigue escaneando el 445 de medio internet.

Detección y contención

Lo primero, porque es la trampa de esta variante concreta: no confiar en el sinkhole del interruptor. Esta muestra no hace ninguna consulta DNS ni ninguna petición HTTP al dominio. Una detección anclada en ese dominio no la ve en absoluto.

Contención:

Ideas para cazarlo:

Indicadores. Todo lo que sigue sale del binario o de sus recursos:

TipoValor
SHA-256 del dropper07c44729e2c570b37db695323249474831f5861d45318bf49ccf5d2f5c8ea1cd
SHA-256 de tasksche.exe2584e1521065e45ec3c17767c065429038fc6291c091097ea8b22c8a502c41dd
Serviciomssecsvc2.0 — "Microsoft Security Center (2.0) Service", ImagePath = <ruta> -m security
ServicioNombre aleatorio, ImagePath = cmd.exe /c "<dir>\tasksche.exe"
FicherosC:\WINDOWS\tasksche.exe, C:\WINDOWS\qeriuwjhrf
DirectorioC:\ProgramData\<aleatorio>\ o …\Intel\<aleatorio>\, oculto y con permisos Everyone:F
Líneas de comandostasksche.exe /i, attrib +h ., icacls . /grant Everyone:F /T /C /Q
MutexGlobal\MsWinZonesCacheCounterMutexA0 — lo crea t.wnry, así que con esta variante nunca aparece
Recursos PEDropper: tipo R, ID 1831. tasksche: tipo XIA, ID 2058
RedTCP/445 saliente de alto volumen; barrido secuencial de la subred local y de los /24 del gateway y el DNS; firma Suricata de barrido SMB
Servicios ocultos Tor (de c.wnry)gx7ekbenv2riucmf.onion, 57g7spgrzlojinas.onion, xxlvbrloxvriy2c5.onion, 76jdd2ir2embyv47.onion, cwwnhwhlz52maqm7.onion
Descarga de Tor (de c.wnry)https://dist.torproject.org/torbrowser/6.5.1/tor-win32-0.2.9.10.zip
Monederos BTC (en tasksche)115p7UMMngoj1pMvkpHijcRdfJNXj6LrLn, 12t9YDPgwueZ9NyMgw519p7AA8isjr6SMw, 13AM4VW2dhxYgXeQepoHkHSQuy6NgaEb94

Lo que no se puede afirmar desde aquí

Todo el análisis es estático: nada se ejecutó, y los artefactos extraídos quedaron en solo lectura. Esto es lo que sostiene cada afirmación, y lo que no:

AfirmaciónConfianzaBase
El interruptor está neutralizado por dos parchesAltaBytes del fichero y decompilado coincidente en dos herramientas independientes
Instalación del servicio, drop de tasksche y lógica de escaneoAltaDecompilado, y concordancia con ejecuciones públicas del mismo hash
El ZIP está dañado, minizip no lo abre y t.wnry no existeAltaVerificación byte a byte del recurso más el decompilado de la librería
Esta variante no cifraAltaLo anterior más la ausencia de cifrado en varias ejecuciones públicas
Esquema híbrido RSA-2048 + AES-128 para proteger t.wnryAltaDecompilado de 0x4014a6
La vacuna del servicio señuelo bloquea la propagación desde el hostMedia-altaDecompilado de 0x407c40; pendiente de prueba dinámica
Esquema de llaves para cifrar los ficheros de la víctimaMediaDocumentación pública de la familia. No verificable aquí: t.wnry falta
Interior de los hilos de explotación (EternalBlue, DoublePulsar)MediaComportamiento conocido de la familia; no decompilado en este trabajo
Quién aplicó los parchesBajaNada en el binario. Las fuentes públicas dicen que no está claro
Causa del daño en el ZIP: accidente o neutralizaciónBajaLa forma del daño sugiere sobrescritura, pero el motivo no es determinable en estático

Queda un cabo que preferí dejar abierto: en el hilo de internet hay temporizadores de 20 y 40 minutos —0x124f80 y 0x249f00— que parecen controlar la renovación del prefijo aleatorio. Encaja con esa función, pero no lo verifiqué instrucción a instrucción, así que lo dejo descrito y sin etiqueta.

¿Merece la pena un dinámico? Sí, pero estrecho y por dos cosas concretas. Una: probar la vacuna, porque desplegar en producción una medida razonada solo sobre el decompilado es precisamente lo que no se debe hacer. Dos: observar tasksche con todos los procesos a la vista, no solo los «importantes», para fijar el paso exacto en que muere — si llega a lanzar cmd.exe, attrib e icacls antes de estrellarse contra t.wnry, o si se queda en las dos esperas de 60 segundos. Para entender el cifrado, en cambio, un dinámico no aporta nada: aquí no hay cifrado que ejecutar.

Fuentes del contexto público