About the Machine
Puppy is an easy-difficulty Linux machine.
The first step in pwning the Puppy machine like I have always done in my previous writeups is to connect my Kali Linux terminal with Hack the Box server. To establish this connection, I ran the following command in the terminal:
Once the connection was successful, I started the target machine and I was assigned an IP address 10.10.11.70.
The next step was adding puppy.htb to my /etc/hosts by running the following command:
After adding puppy.htb
to my /etc/hosts
file, I kicked things off with a version scan to see what services the target was running:
Nmap quickly confirmed the host was alive and responding, but most of its ports were filtered — only 15 TCP ports came back as open.
Right away, a few stood out. Port 88 (Kerberos) and port 389 (LDAP) screamed Active Directory. Combined with 445 (SMB) and 5985 (WinRM), this was starting to look like a Windows Domain Controller. Nmap even revealed the hostname as DC
, confirming my suspicion. The reported domain name PUPPY.HTB0.
was a dead giveaway that this box was running in an AD environment.
Port 53 (DNS) was open and running Simple DNS Plus, meaning I might be able to attempt a DNS zone transfer to enumerate domain records. Kerberos on port 88 opened the door to potential AS-REP roasting or Kerberoasting attacks later on.
Another interesting find was port 2049 (NFS) — pretty rare to see alongside Windows services. If accessible, it could expose mounted directories without authentication.
On top of that, the scan showed 135 (MSRPC), 139 (NetBIOS), and 445 (SMB), all of which are prime for SMB enumeration. LDAP’s global catalog service was also available on 3268, which could allow pulling information about users, groups, and computers across the domain.
Finally, port 5985 was running Microsoft HTTPAPI 2.0, which is typically used by WinRM. If I get valid domain credentials later, this port could be my remote shell entry point.
In short, the target is almost certainly the Domain Controller for the PUPPY.HTB
environment, and the mix of Kerberos, LDAP, SMB, DNS, and WinRM gives me plenty of avenues for further enumeration and potential exploitation.
Machine Information
On the Puppy machine page, there are two credentials which are likely to be a username and password (levi.james / KingofAkron2025!) I am going to be using this credentials going forward in my writeup.
With the domain services mapped out from the earlier Nmap scan, I decided to probe SMB more closely. Since anonymous access didn’t yield anything useful, I tried authenticating with the credentials I had for levi.james
using rpcclient
- a tool that allows direct interaction with Windows RPC services over SMB:
After entering the password, I was dropped into the rpcclient prompt. One of my first moves here was to run enumdomusers, which lists all the user accounts in the domain. The result was a tidy list of domain users, complete with their relative IDs (RIDs):
The first three accounts - Administrator
, Guest
, and krbtgt
are standard in any Active Directory environment. The rest are real domain users, which means they’re potential targets for password spraying, Kerberos attacks, or other credential-based enumeration later on.
One account in particular caught my eye: steph.cooper_adm
. The _adm
suffix is often used to denote administrative accounts, and having this username could be extremely useful for privilege escalation down the line.
At this point, I had a clear list of domain usernames, giving me a solid foundation for further attacks such as AS-REP roasting, Kerberoasting, or targeted password guessing against high-value accounts.
Armed with the valid domain credentials for levi.james
, I moved on to enumerating SMB shares. Since SMB often hides some juicy files in domain environments, I ran smbmap
to quickly see what was available to me:
The connection was successful — I was authenticated against the host puppy.htb
. The results came back with several shares, but permissions were a mixed bag:
- ADMIN$ and C$ — Both are administrative shares that require higher privileges, so no access here.
- DEV — This one caught my eye because of its name (
DEV-SHARE for PUPPY-DEVS
), but unfortunately, I didn’t have permissions to access it either. - IPC$ — Accessible in read-only mode. This is typically used for inter-process communication but can sometimes leak useful information.
- NETLOGON — Read-only. This is the domain logon share, often containing logon scripts or policy files.
- SYSVOL — Read-only. Another critical Active Directory share that stores domain-wide public files, including Group Policy objects.
Even though most shares were locked down, the read-only access to SYSVOL and NETLOGON could still be valuable — especially if they contain scripts or policy files that store credentials in plaintext. I made a mental note to come back and explore these further, as they can often lead to credential harvesting in real-world and CTF scenarios.
For now, I had confirmed that my domain credentials were working over SMB, and I knew exactly which shares were accessible for deeper enumeration.
Before moving forward with domain enumeration, I needed to make sure my system clock was in sync with the Domain Controller. Kerberos authentication is notoriously sensitive to time drift — even a few minutes off can break ticket requests. My local clock was way out of sync, so I disabled automatic NTP sync and manually set it to match the DC:
The output confirmed that my clock was stepped forward by several hours, now matching the Domain Controller’s time. This ensured that Kerberos-based tools would work without throwing authentication errors.
With time in sync, I moved on to a more comprehensive Active Directory enumeration using BloodHound. Instead of the GUI collector, I used bloodhound-python
, which works well from a Linux attack box and doesn’t require a Windows agent. Running it with the -c All
flag meant I was collecting all available data:
-u
and -p
→ Domain username and password-d
→ Target domain-ns
→ Name server to use for lookups (the DC in this case)-c All
→ Collect all available enumeration data--zip
→ Output a ready-to-import .zip
file for the BloodHound GUI
The script authenticated to the DC, queried LDAP, and quickly mapped out the domain. The results were promising:
- 1 domain found:
puppy.htb
- 1 computer — the Domain Controller itself
- 10 users
- 56 groups
- 3 Group Policy Objects (possible sources of misconfigurations)
- 3 Organizational Units
- 19 containers (Active Directory objects)
- 0 trusts — meaning this is a single-domain environment
The scan finished in 25 seconds and generated a ZIP file containing the collected data. With this in hand, I could import it into the BloodHound GUI and start visualizing attack paths — especially any routes that could take me from levi.james
to a high-privilege account like steph.cooper_adm
.
This step was critical — not only did I now have a full AD map, but I could also start planning my privilege escalation strategy based on real relationships and permissions in the environment.
I upload the file to Bloodhound to find the relationship and found out that user james has write permission for the group developers. While reviewing the BloodHound output, I noticed that the DEVELOPERS group had some interesting privileges over the DEV
SMB share I’d seen earlier during enumeration — the same one I couldn’t access before. If I could get levi.james
added to that group, I’d likely gain access to files that were previously off-limits.
For this, I turned to bloodyAD, a Python toolkit for manipulating Active Directory objects over LDAP. Since my account already had the necessary permissions to modify group memberships, I ran the following command:
Within seconds, the tool confirmed:
This meant that my user levi.james
was now officially a member of the DEVELOPERS group. The plan was simple — reconnect to the SMB service, try the DEV
share again, and see what new doors had just been unlocked.
After adding levi.james
to the DEVELOPERS group, it was time to see if my hunch about the DEV
share was correct. I connected to it using smbclient
with my newly elevated group membership:
This time, authentication succeeded and I was dropped into the SMB shell. A quick ls
command revealed several interesting files and directories:
The KeePassXC installer caught my attention, but the real gem here was the recovery.kdbx
file. KeePass databases often store sensitive credentials, and if I could crack it, I might find domain passwords or other useful secrets.
I immediately downloaded the .kdbx
file to my attack box for offline analysis:
With the file in hand, the next step would be to extract its hash and attempt to crack the master password, potentially unlocking a trove of credentials for further exploitation.
With the recovery.kdbx
file safely on my attack box, my first instinct was to see if it could be opened directly in KeePassXC. I launched the application and pointed it at the file:
The database loaded into the KeePassXC GUI without issue, but as expected, it immediately prompted me for the master password. Without that password, the database contents remained encrypted and inaccessible.
This confirmed two things:
-
The file was intact and recognized as a valid KeePass database.
-
I would need to crack the master password before I could extract any credentials from it.
At this point, the plan was clear — convert the .kdbx
file into a hash format that could be attacked with a password-cracking tool like John the Ripper or Hashcat, and then attempt to recover the password.
With the recovery.kdbx
file confirmed as a valid KeePass database, the next step was to recover its master password. Instead of manually extracting the hash and setting up John the Ripper, I opted to use keepass4brute — a dedicated KeePass password brute-forcing tool available on GitHub.
I cloned the repository from the author’s page and navigated into the project directory:
Armed with the rockyou.txt wordlist from SecLists
, I launched the attack against the database:
The tool began cycling through potential passwords, testing 166 attempts per minute. Barely a few dozen words into the list, it struck gold:
The master password was liverpool
. With this in hand, I could now open the KeePass database in KeePassXC and extract whatever credentials it contained — potentially giving me the keys to further compromise the domain.
liverpool
and it was successful.With the KeePass master password liverpool
in hand, I reopened the recovery.kdbx
file in KeePassXC. This time, the database unlocked without resistance, revealing a neatly organized list of stored credentials.
Inside, I found multiple domain user accounts, each with their corresponding passwords:
- Adam Silver —
HJKL2025!
- Antony C. Edwards —
Antman2025!
- Jamie William —
JamieLove2025!
- Samuel Blake —
ILY2025!
- Steve Tucker —
Steve2025!
This was a goldmine — not just one set of credentials, but several, potentially belonging to users with varying privilege levels in the domain. With these accounts, I could attempt Kerberos attacks, SMB enumeration, WinRM access, or even privilege escalation depending on the rights each user had.
The next step would be to test these credentials against the domain to determine which accounts could provide me with more access, and ideally, a path toward administrative control.
With multiple usernames and passwords recovered from the KeePass database, the next step was to identify which combinations were valid in the domain. To do this efficiently, I split the data into two separate files — one for usernames and one for passwords:
I then used netexec (formerly CrackMapExec) to spray all possible combinations against the SMB service on the Domain Controller:
The tool began testing each username/password pair. Most attempts returned STATUS_LOGON_FAILURE
, confirming invalid combinations. However, one stood out:
ant.edwards
could access, potentially leading to privilege escalation.With valid credentials for ant.edwards in hand, my next move was to map out the domain from this new user’s perspective. I launched another round of enumeration with bloodhound-python, this time authenticating as ant.edwards
:
This collected every possible piece of Active Directory relationship data (-c All
), queried the Domain Controller (-ns 10.10.11.70
), and packaged the results in a .zip
ready for import into the BloodHound GUI.
The tool successfully authenticated, retrieved a Kerberos TGT, and queried LDAP without errors. The output confirmed:
- 1 domain (
puppy.htb
) - 1 computer — the Domain Controller (
DC.PUPPY.HTB
) - 10 user accounts
- 56 security groups
- 3 Group Policy Objects
- 3 Organizational Units
- 19 containers
- 0 trusts — a standalone domain environment
In just under 30 seconds, the enumeration was complete and a new dataset was saved as 20250809010554_bloodhound.zip
.
By importing this into BloodHound, I could visualize the AD structure from ant.edwards
’s vantage point, looking for privilege escalation paths or group memberships that could get me closer to domain admin.
(bloodhound GUI image here)
Armed with the knowledge that ant.edwards had FullControl over adam.silver, I decided to put it to use. Using the bloodyAD
tool, I connected to the domain controller and issued a password reset for Adam’s account — no need for his old credentials.
With Adam’s credentials now in hand, I wanted to enumerate more details about his account to understand potential privileges or group memberships. Using ldapsearch
, I queried the domain controller for adam.silver’s LDAP record:
While reviewing Adam’s LDAP attributes, I noticed that his userAccountControl
value was set to 66050. In Active Directory, this integer is actually a bitmask — a combination of flags that control account behavior. Breaking it down, 66050 represented:
- 0x10200 (66048) → Normal account
- + 0x0002 (2) → Account disabled
In other words, Adam’s account existed, had a home directory, and was in interesting groups… but it was currently disabled.
To enable the account, I simply needed to remove the disabled flag. This meant changing userAccountControl
from 66050 to 66048. Using ldapmodify
, I replaced the attribute with the new value:
The modification succeeded, effectively re-enabling Adam’s account and making it ready for interactive logins or remote access. With Adam’s userAccountControl
modified to 66048, I wanted to make sure the change actually stuck. Using ldapsearch
, I queried the DC for Adam’s account entry:
The output confirmed that Adam’s account was no longer disabled — the userAccountControl
field now read 66048, matching the “normal account” flag. This meant I had successfully re-enabled Adam’s domain account.
Looking further down the LDAP entry, I noticed some other interesting details:
- memberOf showed Adam was part of the
DEVELOPERS
group andRemote Management Users
— the latter hinting at possible RDP or WinRM access. - homeDirectory pointed to
C:\Users\adam.silver
, confirming a local profile existed. - logonCount was non-zero, showing the account had been used before and wasn’t just a dormant placeholder.
At this stage, I had:
- Reset Adam’s password to something I knew.
- Re-enabled his account so it could be used.
The stage was now set to pivot into Adam’s account and explore what kind of privileges or network access it could offer.
With Adam’s account re-enabled and the password reset to something I controlled, it was time to see if his membership in Remote Management Users would actually pay off. I fired up Evil-WinRM and attempted a direct connection to the target:
The connection went through without a hitch, dropping me into a PowerShell session on the DC as adam.silver. Navigating to the desktop directory, I quickly spotted something promising:
The listing revealed two files — a Microsoft Edge shortcut and, more importantly, a user.txt
file. The -ar---
permissions confirmed it was readable, and the timestamp suggested it was the CTF’s intended user flag.
At this point, I had gone from gaining control over Adam’s account to remote shell access and was now just one step away from capturing my first flag.
With the user.txt
file in sight, I used PowerShell’s cat
command to reveal its contents:
z
a:z
a:z
0 Comments