Puppy Writeup (HackTheBox Medium Machine)
As is common in real life pentests, you will start the Puppy box with credentials for the following account: levi.james / KingofAkron2025!
Overview
Puppy is a medium Windows machine from HackTheBox. This is Active Directory type of challenge, tackling some of the basic/intermediate cybersecurity skills.
We start by discovering Keepass password database on SMB server. After decrypting it, we gain access to other user with “GenericAll” privilege over disabled user. We re-enable this user via LDAP and get another user’s credentials from website backup ZIP file.
Next, we abuse DPAPI and get a masterkey from the DC, which we use to decrypt admin user’s credentials. This admin user has “DCSync” privilege over the domain, so we abuse it and dump a hash for Administrator user, compromising the Domain Controller and entire domain.
Nmap scan
Starting with the Nmap scan.
┌──(kali㉿kali)-[~]
└─$ sudo nmap -Pn -A -p 1-10000 10.10.11.70 -T5
Starting Nmap 7.95 ( https://nmap.org ) at 2025-05-18 10:29 EDT
Nmap scan report for 10.10.11.70
Host is up (0.034s latency).
Not shown: 9984 filtered tcp ports (no-response)
Bug in iscsi-info: no string output.
PORT STATE SERVICE VERSION
53/tcp open domain Simple DNS Plus
88/tcp open kerberos-sec Microsoft Windows Kerberos (server time: 2025-05-18 20:53:10Z)
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
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)
|_http-server-header: Microsoft-HTTPAPI/2.0
|_http-title: Not Found
9389/tcp open mc-nmf .NET Message Framing
Warning: OSScan results may be unreliable because we could not find at least 1 open and 1 closed port
Device type: general purpose
Running (JUST GUESSING): Microsoft Windows 2022|2012|2016 (89%)
OS CPE: cpe:/o:microsoft:windows_server_2022 cpe:/o:microsoft:windows_server_2012:r2 cpe:/o:microsoft:windows_server_2016
Aggressive OS guesses: Microsoft Windows Server 2022 (89%), Microsoft Windows Server 2012 R2 (85%), Microsoft Windows Server 2016 (85%)
No exact OS matches for host (test conditions non-ideal).
Network Distance: 2 hops
Service Info: Host: DC; OS: Windows; CPE: cpe:/o:microsoft:windows
Host script results:
| smb2-time:
| date: 2025-05-18T20:55:14
|_ start_date: N/A
| smb2-security-mode:
| 3:1:1:
|_ Message signing enabled and required
|_clock-skew: 6h23m10s
TRACEROUTE (using port 445/tcp)
HOP RTT ADDRESS
1 31.65 ms 10.10.14.1
2 31.64 ms 10.10.11.70
OS and Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 200.52 seconds
The Nmap scan showed 16 open ports. Based on ports like 88, 389, 135, 445 (Kerberos, LDAP, RPC, SMB), we can assume that we are dealing with a Domain Controller in Active Directory environment. Don’t forget to add the “puppy.htb” domain to your “/etc/hosts” file.
Domain enumeration
We have a lot of active services to enumerate. Luckily, we have tools like “enum4linux-ng”, which is an automated enumeration tool for Windows and Samba systems, perfect for Active Directories too.
I ran “enum4linux-ng”, which gave me a lot of information about the domain. First of all, it gave me the computer name “dc.puppy.htb”, which we should add to our “/etc/hosts” file too.
┌──(kali㉿kali)-[~]
└─$ enum4linux-ng -A puppy.htb -u PUPPY/levi.james -p "KingofAkron2025\!"

Next, it listed and tested all SMB shares available to my user “levi.james”.

It also printed all domain users enumerated via RPC. We can validate these users with tools like “lookupsid.py” (RID brute-forcing) or “kerbrute” (tested via Kerberos).


Seeing port 5985 open (WinRM), I also tried to get an interactive shell with “evil-winrm”. Unfortunately, our user doesn’t have necessary privileges. We have to keep enumerating from afar.
┌──(kali㉿kali)-[~]
└─$ crackmapexec winrm dc.puppy.htb -u levi.james -p "KingofAkron2025\!"
SMB puppy.htb 5985 DC [*] Windows Server 2022 Build 20348 (name:DC) (domain:PUPPY.HTB)
HTTP puppy.htb 5985 DC [*] http://puppy.htb:5985/wsman
WINRM puppy.htb 5985 DC [-] PUPPY.HTB\levi.james:KingofAkron2025!
We can also perform very deep enumeration of LDAP with tool like “ldapsearch”. I saved all of it’s output to a file, as this gives us a LOT of information.
┌──(kali㉿kali)-[~]
└─$ ldapsearch -H ldap://dc.puppy.htb -D 'levi.james@puppy.htb' -w "KingofAkron2025\!" -b "dc=puppy,dc=htb" > ldap.txt
![]() |
output of ”ldapsearch” |
Another way to enumerate LDAP is to use “Bloodhound-Python”, which is mostly used in scenarios when we don’t have access to interactive shell, just like now. If you’re getting “clock skew” error, just update your date and time according to the machine with “ntpdate”.


Now we can visualize all relations between users and groups in Bloodhound, which uses Neo4j graph database. It’s definitely more intuitive approach than figuring things out from a blob of text.
Decrypting Keepass password database & getting password for user “ant.edwards”
Now we have some basic enumeration out of the way. But before we go any further, let’s check all the low hanging fruit we found during our enumeration. Remember, that our user has access to several SMB shares. The most interesting and non-default share is “DEV”.
I used “smbclient” to list the share. There were couple files inside, most interesting one being file “recovery.kdbx” (Keepass password database).
┌──(kali㉿kali)-[~]
└─$ smbclient \\\\puppy.htb\\DEV -U PUPPY/levi.james
Password for [PUPPY\levi.james]:
Try "help" to get a list of possible commands.
smb: \> ls
. DR 0 Mon May 19 15:51:01 2025
.. D 0 Sat Mar 8 11:52:57 2025
KeePassXC-2.7.9-Win64.msi A 34394112 Sun Mar 23 03:09:12 2025
Projects D 0 Sat Mar 8 11:53:36 2025
recovery.kdbx A 2677 Tue Mar 11 22:25:46 2025
5080575 blocks of size 4096. 1541970 blocks available
smb: \> get recovery.kdbx
getting file \recovery.kdbx of size 2677 as recovery.kdbx (19.1 KiloBytes/sec) (average 19.1 KiloBytes/sec)
smb: \> get KeePassXC-2.7.9-Win64.msi
getting file \KeePassXC-2.7.9-Win64.msi of size 34394112 as KeePassXC-2.7.9-Win64.msi (2735.2 KiloBytes/sec) (average 2705.2 KiloBytes/sec)
smb: \> cd Projects\
smb: \Projects\> ls
. D 0 Sat Mar 8 11:53:36 2025
.. DR 0 Mon May 19 15:51:01 2025
5080575 blocks of size 4096. 1541970 blocks available
KeePass is a free, open-source password manager that helps users securely store and manage their passwords. Instead of remembering dozens (or hundreds) of passwords, you remember one master password to unlock the KeePass database file, which is usually a “.kdbx” file. (ChatGPT)
I tried to open it, but it was password protected. It had to be cracked.

I used “keepass2john” to convert the password into John The Ripper format, so it can be cracked. At first, I was getting this error, saying that the file version is not supported:
┌──(kali㉿kali)-[~]
└─$ keepass2john recovery.kdbx > hash.txt
! recovery.kdbx : File version '40000' is currently not supported!
If you’re seeing this as well, you have to get newer version of John off of Github. ChatGPT can give you step-by-step guide to update John The Ripper if you need one, just like I did.

After you’re done with the installation, you can convert the hash with new “keepass2john”.

After that, you can try to crack it with John. Luckily, John was able to crack the password.

Now we can look into the “recovery.kdbx” file, which contains passwords for several domain users.

Looking at all the entries, we can collect passwords for all of the listed users. Now that we have all those passwords for all of the domain users, we can perform some extra enumeration.
Reactivating and compromising user “adam.silver” & getting user flag
Once again, we can confirm which password belongs to which user with tools like “crackmapexec” on services like SMB, LDAP or WinRM. To switch things up a little bit, I used tool “netexec”.
┌──(kali㉿kali)-[~]
└─$ netexec smb dc.puppy.htb -u users -p pass --continue-on-success

I got one matching pair of credentials on SMB for user “ant.edwards”. After this, I performed the same tests against LDAP and WMI and got the same results, but still couldn’t login via WinRM.
So I checked what SMB shares are available to user “ant.edwards” and with what permissions. It turned out that this user has READ,WRITE permissions over the “DEV” share.

I booted Bloodhound and imported all the JSON files we collected from LDAP with “bloodhound-python” script. Looking at our owned user “ant.edwards”, we can see that this user has “GenericAll” privilege over another user “adam.silver”.
The “GenericAll” privilege is also known as full control. This privilege allows the trustee to manipulate the target object however they wish. (Bloodhound)

I investigated a bit further and found out that user “adam.silver” has “CanPSRemote” privilege over the Domain Controller Computer. This means that we might have access via WinRM.
If a user has “CanPSRemote” over a machine, you can often run remote PowerShell commands (e.g., using WinRM). This usually indicates that the user has been delegated access via Group Policy or local permission to use WinRM. (ChatGPT)

If we look into the help menu on “GenericAll” privilege, Bloodhound shows us couple ways to abuse this privilege. The simplest one being the “Force Password Change” method.

I’ve ran the “net” command and changed Adam’s password successfully.
┌──(kali㉿kali)-[~]
└─$ net rpc password adam.silver "Pass1234" -U PUPPY/ant.edwards%"Antman2025\!" -S dc.puppy.htb
But I still couldn’t login neither via WinRM or SMB, because the account was disabled.
![]() |
failed SMB login due to disabled account |
But since we have “GenericAll” privileges over Adam Silver’s account, we should be able to modify his account settings and enable it. We can do so by modifying the LDAP entries.
To do this, we have tool like “ldapmodify” at our disposal (should be pre-installed on Kali Linux). We can create an LDIF (LDAP Data Interchange Format) script, which overwrites the current config with our custom one. With a little help from ChatGPT, I cooked this script:
![]() |
LDIF script that overwrites Adam’s account settings |
To get the proper CN and DC attributes, we can use our notes about LDAP, which we got from our deep enumeration that we had done earlier with “ldapsearch”.

Or we can find the same information in Bloodhound. Choose your preferable way.

With the LDIF script ready, we can run the “ldapmodify” tool.
┌──(kali㉿kali)-[~]
└─$ ldapmodify -x -H ldap://dc.puppy.htb -D ant.edwards@puppy.htb -w "Antman2025\!" -f enable_adam.ldif
modifying entry "CN=Adam D. Silver,CN=Users,DC=PUPPY,DC=HTB"
Now, if we check the credentials against SMB or WinRM, we should see a successful login.

If you’re seeing failed login message, try to re-run the password change command. This machine has some cleanup scripts running occasionally, so don’t let that startle you.
Finally, we are able to login into the machine via WinRM and get an interactive shell. I used my favourite tool “evil-winrm” for this, mainly because it comes with some additional functionality.

The user flag sits patiently in Adam’s Desktop directory. Now onto the root flag.

Getting password for user “steph.cooper” from website backup ZIP file
At first, I looked at all the privileges and groups my user “adam.silver” is part of. I found nothing interesting there, though.

Next, I traversed the entire filesystem, checked what else I had access to. In the root of the C drive, I found a suspicious directory named “Backups”. Inside, I found a “site backup” ZIP file. I downloaded the ZIP file immediately using the download capability of “evil-winrm”.

I inspected the content of this downloaded ZIP file. The files inside looked like typical website source code, with some HTML, CSS, JavaScript and XML.

I looked around the website for a while and found out that Puppy is a company with software development environment, with developers whose accounts we had compromised. Just a fan fact, not really important for the completion of the box.
One file named “nms-auth-config” stood out to me. It was an XML file, which specified the auth configuration on the website (probably), exposing a password for user “steph.cooper”.
![]() |
credentials for user “steph.cooper” exposed in auth config XML file |
I tried to login as “steph.cooper” via “evil-winrm”. And it was a success!
Abusing DPAPI to get password for user “steph.cooper_adm”
I looked at Steph’s privileges with “whoami /all” command, but saw nothing interesting or helpful. So I booted Bloodhound again to see Steph’s relations with the rest of the domain. If you still remember all the domain users, you know that there’s a similar account on the domain named “steph.cooper_adm”. There is an obvious connection between these two accounts.
To be honest, I wasn’t able to figure out the correct path to root here. After I ran out of ideas, I went on HackTheBox Reddit page and asked for help. One kind soul advised me to take a look on DPAPI and suggested to read this Medium article: https://z3r0th.medium.com/abusing-dpapi-40b76d3ff5eb. Fun fact: Title of this box PUPPY is an indirect hint to (D)PAPI.

DPAPI, or Data Protection API, is a Windows data protection service provided by Microsoft that allows developers to securely store and retrieve sensitive data, such as passwords, cryptographic keys, or other confidential information. It provides encryption and decryption services tied to a user or machine account, so only the intended user or machine can access the protected data. (ChatGPT)
TL;DR, we can encrypt files via DPAPI. These files can contain anything, including credentials. These so-called credential blobs are commonly stored as hidden files in “C:\Users\<user>\AppData\Roaming\Microsoft\Credentials” directory. Our user Steph has such blob too.
![]() |
encrypted credential blob |
These credential blobs are encrypted symmetrically with so-called masterkey. Most of the time, every user has a unique masterkey, which is derived from the hash of this user’s password. These encrypted masterkeys (not cleartext!) are commonly stored as hidden files in “C:\Users\<user>\AppData\Roaming\Microsoft\Protect\<SID>” directory.
![]() |
encrypted masterkey |
Our plan here is to get the cleartext masterkey and decrypt the credential blob, where we will, hopefully, find some useful credentials and make some more progress.
We need some tools for this attack. Luckily, Mimikatz has good DPAPI functionality, so that should be enough. So I transferred Mimikatz onto the machine. During this attack, I’ll be following step-by-step guide from the Medium article I mentioned earlier.
*Evil-WinRM* PS C:\Users\steph.cooper\Documents> upload mimikatz.exe
Info: Uploading /home/kali/mimikatz.exe to C:\Users\steph.cooper\Documents\mimikatz.exe
Info: Upload successful!
Firstly, let’s verify that the credential blob actually has some items stored in it. Also, I’m passing my commands as arguments to Mimikatz, because I’ve been experiencing the infinite prompt bug.
.\mimikatz.exe "dpapi::cred /in:C:\users\steph.cooper\appdata\roaming\microsoft\credentials\C8D69EBE9A43E9DEBF6B5FBD48B521B9" "exit"
![]() |
encrypted masterkey and encrypted data |
We can see 2 interesting entries in the output. The “guidMasterKey” is the encrypted masterkey we saw earlier (those should match) and the “pbData” is the actual encrypted credential data.
Secondly, we can request a backup masterkey for our user “steph.cooper” from the Domain Controller (this machine) over RPC and decrypt the credential blob with it. Once again, Mimikatz offers this functionality with one simple command.
.\mimikatz.exe "dpapi::masterkey /in:C:\users\steph.cooper\appdata\roaming\microsoft\protect\S-1-5-21-1487982659-1829050783-2281216199-1107\556a2412-1275-4ccf-b721-e6a0b4f90407 /rpc" "exit"
At the bottom of the output, there is our cleartext masterkey. Ready to be used/abused.
![]() |
cleartext masterkey |
Thirdly and finally, we can once again print out the content of Steph’s credential blob. But now, armed with the cleartext masterkey, we can see decrypted password for user “steph.cooper_adm”.
.\mimikatz.exe "dpapi::cred /in:C:\users\steph.cooper\appdata\roaming\microsoft\credentials\C8D69EBE9A43E9DEBF6B5FBD48B521B9 /masterkey:d9a570722fbaf7149f9f9d691b0e137b7413c1414c452f9c77d6d8a8ed9efe3ecae990e047debe4ab8cc879e8ba99b31cdb7abad28408d8d9cbfdcaf319e9c84" "exit"
![]() |
cleartext credentials for user “steph.cooper_adm” |
And now we can easily login as “steph.cooper_adm” via “evil-winrm”.
With this done, the question I asked myself was: How would I know that abusing DPAPI is a correct path to pwning a machine in the future? Well, only 2 things come to my mind. Either you have to realize that your user can/should have someone else’s credentials encrypted with his own masterkey (hint can be similar account names like in this scenario), or you have to check for the blobs and masterkeys in their common paths yourself. That’s just my theory. If anyone knows more, be sure to leave a comment down below.
Abusing DCSync privilege to dump Administrator’s password & getting root flag
After compromising “steph.cooper_adm” account, I went back to Bloodhound and checked this user’s privileges and relations with other objects within the domain. Amazingly, there was a glaring DCSync privilege over the domain.
DCSync is a technique used by attackers or administrators to simulate the behavior of a Domain Controller (DC) in order to request password hashes from Active Directory (AD) using standard replication mechanisms. (ChatGPT)

Bloodhound is such an awesome tool, that it also provides you with multiple paths to exploit this privilege. In this example, we can request an NTLM hash for “Administrator” user from the Domain Controller, while acting as another DC. The DC will sent us Administrator’s password hash from a file called “NTDS.dit” via replication protocols. This file stores credentials of all domain users on the DC.

Mimikatz can help us with this once again. With a simple command, we are able to perform a DCSync attack and request Administrator’s NTLM hash.
.\mimikatz.exe "lsadump::dcsync /domain:puppy.htb /user:Administrator" "exit"
![]() |
dumped Administrator’s NTLM hash |
The best part is that we don’t even have to crack the hash. The NTLM hashes are infamously known for their lacking security, because they allow the Pass-The-Hash attack. This means that we can simply pass-the-hash instead of password and get logged in via “evil-winrm” for example. This issue is resolved in the newer and modern NTLMv2 hashes.

The root flag waits on Administrator’s desktop. And that wraps up the Puppy machine.
Summary & final thoughts
Puppy is a medium machine from HackTheBox. This box is all about our beloved Active Directory, testing common cybersecurity skills and pivoting between several users. During completion, you will learn account settings modification via LDAP, Keepass database decryption, DPAPI exploitation etc. The box starts with a Keepass password database on SMB share. We use John The Ripper cracking suite and decrypt it. Then, we abuse the “GenericAll” privilege and change password for another user, who’s account was disabled. We re-enable it by performing account settings modification over LDAP with dedicated tool. While scrutinizing the filesystem, we find a website backup ZIP file. Inside, we find a pair of credentials in the source code. After that, we abuse DPAPI using Mimikatz and request a masterkey from the DC, decrypting a credential blob for admin user. At last, we abuse the “DCSync” privilege over the domain and dump Administrator’s NTLM password hash with Mimikatz, which leads to full compromise. This machine earned extremely positive reviews on HackTheBox site. In my opinion, this box has good flow, it has multiple challenges that are not that difficult. They only require some research. Amongst fairly simple Keepass database decryption and account settings editing, there was the DPAPI service exploitation, which I was completely new to at the time. Luckily, Reddit and good community helped me a lot with it. I also learnt a lot of tools for Active Directory enumeration and exploitation during the process. Recommending to anyone, who’s hungry for some intermediate and well-structured Active Directory CTF machine. Beginners shouldn’t be discouraged, because although it’s not easy, there’s still a lot to learn from this one.
Comments
Post a Comment