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:
- La vulnerabilidad estaba parcheada desde dos meses antes. Microsoft publicó
MS17-010 el 14 de marzo de 2017; el exploit que WannaCry usó —EternalBlue, contra
CVE-2017-0145— lo filtró el grupo Shadow Brokers el 14 de abril. El brote de mayo ocurrió sobre equipos que llevaban dos meses sin aplicar una actualización disponible. - La escala está en disputa entre fuentes. El 14 de mayo el director de Europol, Rob Wainwright, habló de más de 200.000 víctimas en al menos 150 países. Otras estimaciones de los mismos días subían la cifra a unas 300.000. No hay un recuento único, y la diferencia no es un detalle: nadie contó máquinas, se contaron conexiones al sinkhole y avisos de los afectados.
- El caso británico es el mejor documentado. La Oficina Nacional de Auditoría contabilizó, en su informe del 27 de octubre de 2017, al menos 81 de los 236 trusts del NHS en Inglaterra afectados, 603 organizaciones infectadas —entre ellas 595 consultorios de atención primaria— y una estimación de más de 19.000 citas canceladas. NHS Digital declaró que no cree que se robaran datos de pacientes: WannaCry cifra, no exfiltra.
- El interruptor de apagado era una comprobación de dominio. El binario intentaba una petición HTTP a un dominio fijo; si la petición tenía éxito, se detenía. Un investigador registró ese dominio el 12 de mayo y la propagación se frenó. Microsoft lo describió en su propio aviso del mismo día: «si la conexión a los dominios tiene éxito, el dropper no infecta más el sistema» —y recomendó expresamente no bloquear esos dominios en los cortafuegos.
- El negocio fue un fracaso. El 23 de mayo de 2017 el total acumulado en los monederos de rescate era de poco más de 109.000 dólares; en agosto se hablaba de unos 140.000. Contra 200.000 máquinas o más, eso son unos pocos cientos de pagos.
- La atribución pública es estatal. En diciembre de 2017 la Casa Blanca señaló oficialmente a Corea del Norte, y dijo que Reino Unido, Australia, Canadá, Nueva Zelanda y Japón compartían el análisis. En septiembre de 2018 el Departamento de Justicia de Estados Unidos presentó una denuncia penal —no un indictment— contra Park Jin Hyok, al que sitúa en la empresa pantalla Chosun Expo y al servicio de la Oficina General de Reconocimiento, con dos cargos de conspiración. Pyongyang lo negó.
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ónde | Original | Esta muestra | Efecto |
|---|---|---|---|
0x4313d0(offset 0x313d0) | 57 bytes con la URL | 00 00 00 … | InternetOpenUrlA("") falla siempre; no hay tráfico HTTP ni consulta DNS |
0x4081a5(offset 0x81a5) | 75 15 — jne 0x4081bc | 75 00 — jne +0 | las dos ramas caen en la infección |
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:
- la subred completa según su máscara, de
ip & maskhasta el broadcast; - el /24 de cada puerta de enlace, si no cae ya dentro de la subred local;
- el /24 de cada servidor DNS, con el mismo filtro.
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.
| Prueba | Resultado |
|---|---|
Cabeceras locales PK 03 04 | 32, todas antes del daño |
Directorio central PK 01 02 | 0 apariciones |
Fin de directorio PK 05 06 | 0 apariciones |
| Inicio del daño | offset 0x31e2b7 (3.269.303), el 94,9 % del fichero |
| Cola dañada | 177.022 bytes, entropía 7,9986 bits/byte, sin una sola cabecera |
Contraseña WNcry@2ol7 | correcta: 31 entradas descifran con CRC válido |
t.wnry, u.wnry, taskdl.exe, taskse.exe | no existen en el fichero |
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.
De ahí salen cuatro consecuencias que explican bastante bien la historia de 2017:
- Recuperar la llave privada de un equipo solo sirve para ese equipo. El mismo fichero cifrado en dos máquinas da resultados distintos, porque cada archivo lleva su propia AES.
- El punto único de falla es la privada maestra del atacante, que descifraría la
.ekyde cualquier víctima. Nunca se publicó. - La llave de
t.wnryes idéntica en todas las copias, porque viaja en el binario. Protege contra una mirada superficial, no contra el análisis: por eso el motor de cifrado se puede extraer y estudiar. - Hubo una debilidad aprovechable, la de WannaKey y wanakiwi en 2017: en Windows XP y 7 la CryptoAPI no borraba de memoria los primos de la llave RSA de la víctima, así que si el equipo no se había reiniciado se podía reconstruir y descifrar sin pagar.
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:
tasksche.exe visto públicamente el 12 may 2017La 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 origen | Qué hay aquí |
|---|---|
| Idioma de los recursos PE | Todos 0x0409, el valor por defecto. No informa |
| Idioma de Office en las notas de rescate | 28 de 28 llevan \deflangfe1042 y \themelangfe1042: coreano como idioma de Asia oriental de Word |
| Fuentes declaradas en las notas | 28 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 plantilla | Errores de hablante no nativo: «But you have not so enough time.» |
| Notas en chino | De 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 hecho | Dó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 pieza | Sin 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ón | El 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ático | Tres 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 formatos | IOCs 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:
- Aplicar MS17-010 y deshabilitar SMBv1, que es lo único que resuelve el problema de verdad.
- Bloquear TCP/445 en el perímetro y entre segmentos y VLAN. La propagación interna no se detiene en el perímetro, y el truco del gateway y el DNS está diseñado precisamente para cruzar segmentos.
- Aislar los hosts que generen ráfagas de conexiones al 445.
Ideas para cazarlo:
- Un host con muchas conexiones al 445 hacia muchos destinos únicos en pocos minutos, internos y externos a la vez.
- Creación del servicio
mssecsvc2.0, o de cualquier servicio cuyoImagePathcontengacmd.exe /cytasksche.exe. - Procesos
tasksche.exefuera de rutas legítimas o con el argumento/i. icacls . /grant Everyone:F /T /C /Qyattrib +h .lanzados por un proceso no administrativo.
Indicadores. Todo lo que sigue sale del binario o de sus recursos:
| Tipo | Valor |
|---|---|
| SHA-256 del dropper | 07c44729e2c570b37db695323249474831f5861d45318bf49ccf5d2f5c8ea1cd |
SHA-256 de tasksche.exe | 2584e1521065e45ec3c17767c065429038fc6291c091097ea8b22c8a502c41dd |
| Servicio | mssecsvc2.0 — "Microsoft Security Center (2.0) Service", ImagePath = <ruta> -m security |
| Servicio | Nombre aleatorio, ImagePath = cmd.exe /c "<dir>\tasksche.exe" |
| Ficheros | C:\WINDOWS\tasksche.exe, C:\WINDOWS\qeriuwjhrf |
| Directorio | C:\ProgramData\<aleatorio>\ o …\Intel\<aleatorio>\, oculto y con permisos Everyone:F |
| Líneas de comandos | tasksche.exe /i, attrib +h ., icacls . /grant Everyone:F /T /C /Q |
| Mutex | Global\MsWinZonesCacheCounterMutexA0 — lo crea t.wnry, así que con esta variante nunca aparece |
| Recursos PE | Dropper: tipo R, ID 1831. tasksche: tipo XIA, ID 2058 |
| Red | TCP/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ón | Confianza | Base |
|---|---|---|
| El interruptor está neutralizado por dos parches | Alta | Bytes del fichero y decompilado coincidente en dos herramientas independientes |
Instalación del servicio, drop de tasksche y lógica de escaneo | Alta | Decompilado, y concordancia con ejecuciones públicas del mismo hash |
El ZIP está dañado, minizip no lo abre y t.wnry no existe | Alta | Verificación byte a byte del recurso más el decompilado de la librería |
| Esta variante no cifra | Alta | Lo anterior más la ausencia de cifrado en varias ejecuciones públicas |
Esquema híbrido RSA-2048 + AES-128 para proteger t.wnry | Alta | Decompilado de 0x4014a6 |
| La vacuna del servicio señuelo bloquea la propagación desde el host | Media-alta | Decompilado de 0x407c40; pendiente de prueba dinámica |
| Esquema de llaves para cifrar los ficheros de la víctima | Media | Documentación pública de la familia. No verificable aquí: t.wnry falta |
| Interior de los hilos de explotación (EternalBlue, DoublePulsar) | Media | Comportamiento conocido de la familia; no decompilado en este trabajo |
| Quién aplicó los parches | Baja | Nada en el binario. Las fuentes públicas dicen que no está claro |
| Causa del daño en el ZIP: accidente o neutralización | Baja | La 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
- Microsoft, 12 de mayo de 2017 — la comprobación de dominio en el dropper, el servicio
mssecsvc2.0, las rutas detasksche.exe, MS17-010 yCVE-2017-0145 - Mandiant, 15–17 de mayo de 2017 — las variantes con dominio cambiado, la del 14 de mayo sin killswitch y con el componente de ransomware corrupto, y que no está claro si el cambio fue de los operadores o de un tercero
- SecurityWeek, 15 de mayo de 2017 — la variante parcheada con editor hexadecimal, la cifra de «solo 2 bytes» y el argumento de Bitdefender sobre los monederos
- Al Jazeera, 14 de mayo de 2017 — la cifra de Europol: más de 200.000 víctimas en al menos 150 países
- National Audit Office, 27 de octubre de 2017 — 81 de 236 trusts, 603 organizaciones, 595 consultorios, más de 19.000 citas canceladas y ningún dato de paciente robado
- Infosecurity Magazine, 23 de mayo de 2017 — los 109.000 dólares acumulados y la comparación con el número de víctimas
- The Global Treasurer, 3 de agosto de 2017 — el total en torno a los 140.000 dólares y el seguimiento de Elliptic sobre las retiradas
- Foreign Policy, 19 de diciembre de 2017 — la atribución oficial a Corea del Norte y los países que la respaldaron
- CyberScoop, 6 de septiembre de 2018 — la denuncia penal contra Park Jin Hyok, Chosun Expo, la Oficina General de Reconocimiento y los dos cargos de conspiración
- Threatpost, mayo de 2017 — la filtración de EternalBlue por Shadow Brokers el 14 de abril y su uso en el brote