<menu id="guoca"></menu>
<nav id="guoca"></nav><xmp id="guoca">
  • <xmp id="guoca">
  • <nav id="guoca"><code id="guoca"></code></nav>
  • <nav id="guoca"><code id="guoca"></code></nav>

    Windows內核模糊測試之IoControl Fuzz

    VSole2023-01-31 10:31:28

    一、測試原理

    在Windows中,用戶層和內核層通過DeviceIoControl來實現通信得,該函數定義如下:

    BOOL WINAPI DeviceIoControl(  __in         HANDLE hDevice,  __in         DWORD dwIoControlCode,  __in_opt     LPVOID lpInBuffer,  __in         DWORD nInBufferSize,  __out_opt    LPVOID lpOutBuffer,  __in         DWORD nOutBufferSize,  __out_opt    LPDWORD lpBytesReturned,  __inout_opt  LPOVERLAPPED lpOverlapped);
    

    DeviceIoControl函數最后會通過內核函數NtDeviceIoControlFile來實現通信,該函數定義如下:

    NTSTATUS   NtDeviceIoControlFile(    IN HANDLE  FileHandle,    IN HANDLE  Event,    IN PIO_APC_ROUTINE  ApcRoutine,    IN PVOID  ApcContext,    OUT PIO_STATUS_BLOCK  IoStatusBlock,    IN ULONG  IoControlCode,    IN PVOID  InputBuffer,    IN ULONG  InputBufferLength,    OUT PVOID  OutputBuffer,    IN ULONG  OutputBufferLength    );
    

    由于這兩個函數包含了用戶層和內核層通信的所有需要的數據,因此可以通過對這兩個函數進行操作來實現驅動程序的模糊測試。根據實現模糊測試器實現的方法不同,可以分為以下兩類:

    • IoControl MITM(Man-in-the-Middle) Fuzz
    • IoControl Driver Fuzz

    二、IoControl MITM(Man-in-the-Middle) Fuzz

    下圖是用戶層與內核層實現通信的過程,可以看到,最后是通過NtDeviceIoControlFile來分發給相應驅動對象的派遣函數的,因此,可以通過對該函數進行HOOK操作。

    這樣,當用戶層與內核層發生通信的時候,我們可以監控到這個操作并且可以對其中的輸入輸出數據進行修改,在將修改以后的數據傳遞給原始的NtDeviceIoControlFile函數。

    如果將修改以后的數據發送給NtDeviceIoControlFile函數以后,發生了內核崩潰或藍屏,往往預示著該驅動程序可能存在內核漏洞。

    下圖則是該方法的數據變異策略,主要是對輸入地址,輸入數據,輸入長度,輸出地址和輸出長度進行變異。

    三、IoControl Driver Fuzz

    上述方法無法主動對驅動進行測試,只能被動地等待相應地通信發生,然后在對數據進行變異。想要主動地對相應派遣函數進行調用,就需要首先通過逆向地手段獲得驅動的設備名稱以及派遣函數對應的IoControlCode,接著對數據進行變異以后通過主動調用DeviceIoControl函數來完成測試。

    根據IoControlCode中Method值的不同,數據變異策略有以下兩種情況:

    • Method != METHOD_NEITHER:由于輸入輸出都有系統保護,因此修改地址沒有意義,需要變異的數據只有:輸入數據,輸入長度,輸出長度。
    • Method == NMETHOD_NEITHER:驅動中可能直接訪問輸入輸出地址,而沒有探測是否可寫,因此需要變異的數據有:輸入地址,輸入數據,輸出地址,輸出長度。

    以下是數據的變異策略:

    四、IoctlFuzzer部分源碼介紹

    這個模糊測試器的源碼在:https://github.com/Cr4sh/ioctlfuzzer/,該模糊測試器就是通過HOOK NtDeviceIoControlFile函數來構建的IoControl MITM Fuzzer,HOOK代碼如下:

    old_NtDeviceIoControlFile = (NT_DEVICE_IO_CONTROL_FILE)InterlockedExchange(            (PLONG)&SYSTEM_SERVICE(m_SDT_NtDeviceIoControlFile),             (LONG)new_NtDeviceIoControlFile        );
    

    在new_NtDeviceIoControlFile中會首先判斷先前模式是否是用戶模式,如果是內核模式就直接調用原函數繼續運行。

    NTSTATUS NTAPI new_NtDeviceIoControlFile(    HANDLE FileHandle,    HANDLE Event,    PIO_APC_ROUTINE ApcRoutine,    PVOID ApcContext,    PIO_STATUS_BLOCK IoStatusBlock,    ULONG IoControlCode,    PVOID InputBuffer,    ULONG InputBufferLength,    PVOID OutputBuffer,    ULONG OutputBufferLength){        KPROCESSOR_MODE PrevMode = ExGetPreviousMode();    BOOLEAN bLogOutputBuffer = FALSE;      // handle only user mode calls    if (PrevMode != KernelMode)    {        // 省略部分代碼    }     // 內核模式,則調用原函數    NTSTATUS status = old_NtDeviceIoControlFile(        FileHandle,         Event,         ApcRoutine,         ApcContext,         IoStatusBlock,         IoControlCode,         InputBuffer,         InputBufferLength,         OutputBuffer,         OutputBufferLength    );         return status;}
    

    如果先前模式是用戶模式,調用ObReferenceObjectByHandle獲取句柄的對象:

            POBJECT_NAME_INFORMATION DeviceObjectName = NULL, DriverObjectName = NULL;            PFILE_OBJECT pFileObject = NULL;         // get device object by handle        NTSTATUS ns = ObReferenceObjectByHandle(            FileHandle,             0, 0,             KernelMode,             (PVOID *)&pFileObject,             NULL        );
    

    接著驗證一波設備對象和驅動對象的有效性:

                PVOID pDeviceObject = NULL;             // validate pointer to device object            if (MmIsAddressValid(pFileObject->DeviceObject))            {                pDeviceObject = pFileObject->DeviceObject;            }            else            {                goto end;            }             if (pDeviceObject == m_DeviceObject)            {                // don't handle requests to our driver                goto end;            }             // validate pointer to driver object            if (!MmIsAddressValid(pFileObject->DeviceObject->DriverObject))            {                goto end;            }             // get loader information entry for the driver module            PLDR_DATA_TABLE_ENTRY pModuleEntry = (PLDR_DATA_TABLE_ENTRY)                pFileObject->DeviceObject->DriverObject->DriverSection;             if (pModuleEntry == NULL)            {                goto end;            }             // validate pointer to loader's table and data from it            if (!MmIsAddressValid(pModuleEntry) ||                !ValidateUnicodeString(&pModuleEntry->FullDllName))            {                goto end;            }
    

    在輸入滿足條件的時候,會繼續調用Fuzz_NtDeviceIoControlFile函數來進行測試。

                        if (InputBuffer != NULL && InputBufferLength > 0 &&                        (m_FuzzOptions & FUZZ_OPT_FUZZ) && bProcessEvent)                    {                           // fuzz this request                        Fuzz_NtDeviceIoControlFile(                            PrevMode,                            &DeviceObjectName->Name,                            FileHandle,                                IoStatusBlock,                            IoControlCode,                            InputBuffer,                            InputBufferLength,                            OutputBuffer,                            OutputBufferLength                        );                    }
    

    對于Fuzz_NtDeviceIoControlFile函數,該函數首先判斷全局變量m_FuzzIotions是否有FUZZ_OPT_FUZZ_FAIR標志,如果沒有就會調用FuzzContinue_NtDeviceIoControlFile函數完成測試。

    void Fuzz_NtDeviceIoControlFile(    KPROCESSOR_MODE PrevMode,    PUNICODE_STRING usDeviceName,    HANDLE FileHandle,    PIO_STATUS_BLOCK IoStatusBlock,    ULONG IoControlCode,    PVOID InputBuffer,    ULONG InputBufferLength,    PVOID OutputBuffer,    ULONG OutputBufferLength){        MAPPED_MDL InBuffMapped, OutBuffMapped;    KAPC_STATE ApcState;    BOOLEAN bInBuffMapped = FALSE, bOutBuffMapped = FALSE, bNeedToDetach = FALSE;    PVOID TmpInputBuffer = NULL, TmpOutputBuffer = NULL;     // save original parameters from the nt!NtDeviceIoControlFile()    FUZZ_THREAD_PARAMS ThreadParams;    ThreadParams.PrevMode = PrevMode;    ThreadParams.hFuzzHandle = FileHandle;    ThreadParams.cIoStatusBlock = IoStatusBlock;    ThreadParams.IoControlCode = IoControlCode;    ThreadParams.cInputBuffer = InputBuffer;    ThreadParams.cOutputBuffer = OutputBuffer;    ThreadParams.cInputBufferLength = InputBufferLength;    ThreadParams.cOutputBufferLength = OutputBufferLength;         if (m_FuzzOptions & FUZZ_OPT_FUZZ_FAIR)    {        // 省略部分代碼    }    else    {        /**         * Sending IOCTL's from context of the original process.         */        FuzzContinue_NtDeviceIoControlFile(            PrevMode,            ThreadParams.hFuzzHandle,            NULL, NULL, NULL,            ThreadParams.cIoStatusBlock,            IoControlCode,            ThreadParams.cInputBuffer,            ThreadParams.cInputBufferLength,            ThreadParams.cOutputBuffer,            ThreadParams.cOutputBufferLength        );    }         // 省略部分代碼}
    

    如果帶有FUZZ_OPT_FUZZ_FAIR標記,那么程序附加到模糊測試器的進程中,并通過APC機制來完成模糊測試。

            if (m_FuzzProcess && m_FuzzThreadId && m_UserModeData)        {                        if (InputBuffer != NULL && InputBufferLength > 0)            {                // 保存輸入數據                if (TmpInputBuffer = M_ALLOC(InputBufferLength))                {                    memcpy(TmpInputBuffer, InputBuffer, InputBufferLength);                }            }             if (OutputBuffer != NULL && OutputBufferLength > 0)            {                // 保存輸出數據                if (TmpOutputBuffer = M_ALLOC(OutputBufferLength))                {                    memcpy(TmpOutputBuffer, OutputBuffer, OutputBufferLength);                }            }             // 附加到模糊測試器進程            KeStackAttachProcess(m_FuzzProcess, &ApcState);             if (InputBuffer != NULL && InputBufferLength > 0)            {                // 在傳遞的參數中保存輸入數據                if (bInBuffMapped = AllocateUserMemory(InputBufferLength, &InBuffMapped))                {                    ThreadParams.cInputBuffer = InBuffMapped.MappedBuffer;                    memcpy(ThreadParams.cInputBuffer, TmpInputBuffer, InputBufferLength);                }            }             if (OutputBuffer != NULL && OutputBufferLength > 0)            {            // 在傳遞的參數中保存輸出數據                if (bOutBuffMapped = AllocateUserMemory(OutputBufferLength, &OutBuffMapped))                {                    ThreadParams.cOutputBuffer = OutBuffMapped.MappedBuffer;                    memcpy(ThreadParams.cOutputBuffer, TmpOutputBuffer, OutputBufferLength);                }            }                         PETHREAD Thread = NULL;            KAPC Apc;             // 獲取模糊測試器線程對象            ns = PsLookupThreadByThreadId(m_FuzzThreadId, &Thread);             if (NT_SUCCESS(ns))            {                // 初始化事件對象                KeInitializeEvent(                    &ThreadParams.OperationComplete,                    NotificationEvent,                    FALSE                );                 // 初始化APC對象                  KeInitializeApc(                    &Apc,                     (PKTHREAD)Thread,                     OriginalApcEnvironment,                     ApcKernelRoutine,                     NULL,                     ApcNormalRoutine,                     KernelMode,                     &ThreadParams                );                 // 插入APC隊列                if (KeInsertQueueApc(&Apc, NULL, NULL, 0))                {                    // waiting for APC execution                    KeWaitForSingleObject(                        &ThreadParams.OperationComplete,                        Executive,                        KernelMode,                        FALSE, NULL                    );                }            }          }
    

    而要執行的APC函數也是通過FuzzContinue_NtDeviceIoControlFile函數完成測試。

    VOID ApcNormalRoutine(    PVOID NormalContext,    PVOID SystemArgument1,    PVOID SystemArgument2){    PFUZZ_THREAD_PARAMS ThreadParams = (PFUZZ_THREAD_PARAMS)NormalContext;     // continue fuzzing    FuzzContinue_NtDeviceIoControlFile(        ThreadParams->PrevMode,        ThreadParams->hFuzzHandle,        NULL, NULL, NULL,        ThreadParams->cIoStatusBlock,        ThreadParams->IoControlCode,        ThreadParams->cInputBuffer,        ThreadParams->cInputBufferLength,        ThreadParams->cOutputBuffer,        ThreadParams->cOutputBufferLength    );         KeSetEvent(&ThreadParams->OperationComplete, 0, FALSE); } VOID ApcKernelRoutine(    struct _KAPC *Apc,    PKNORMAL_ROUTINE *NormalRoutine,    PVOID *NormalContext,    PVOID *SystemArgument1,    PVOID *SystemArgument2) {    /**     * This code exeuting in context of the fuzzer's process at APC_LEVEL.     * Nothing to do here...     */}
    

    真正完成測試的是在FuzzContinue_NtDeviceIoControlFile函數中完成的,變異策略步驟如下:

    1. 對輸入數據按1字節或4字節為單位進行變異,其中按一字節進行變異的時候,也會變異輸入數據的長度;
    2. 對輸出數據的地址進行變異;
    3. 如果通信方式不是METHOD_BUFFERED,則會對輸入輸出的地址與長度進行變異。

    具體代碼如下:

    void FuzzContinue_NtDeviceIoControlFile(    KPROCESSOR_MODE PrevMode,    HANDLE FileHandle,    HANDLE Event,    PIO_APC_ROUTINE ApcRoutine,    PVOID ApcContext,    PIO_STATUS_BLOCK IoStatusBlock,    ULONG IoControlCode,    PVOID InputBuffer,    ULONG InputBufferLength,    PVOID OutputBuffer,    ULONG OutputBufferLength){                           // 分配輸入數據長度的內存空間    PUCHAR NewBuff = (PUCHAR)M_ALLOC(InputBufferLength);         // 分配成功,則先對輸入數據進行測試    if (NewBuff)    {    // 保存輸入數據        RtlCopyMemory(NewBuff, InputBuffer, InputBufferLength);         // 對輸入數據每個字節進行隨機化        if (m_FuzzingType == FuzzingType_Random)        {                         for (int i = 0; i < RANDOM_FUZZING_ITERATIONS; i++)            {                ULONG TmpInputLength = InputBufferLength;                 // 對輸入數據長度進行變異                if (m_FuzzOptions & FUZZ_OPT_FUZZ_SIZE)                {                    TmpInputLength = getrand(1, TmpInputLength * 4);                }                               // 對輸入數據的每個字節進行變異                for (ULONG s = 0; s < InputBufferLength; s++)                {                    *((PUCHAR)InputBuffer + s) = (UCHAR)getrand(1, 0xff);                }                               // 調用原函數                NTSTATUS status = old_NtDeviceIoControlFile(                    FileHandle,                     Event, ApcRoutine,                     ApcContext,                     IoStatusBlock,                     IoControlCode,                     InputBuffer,                     TmpInputLength,                     OutputBuffer,                     OutputBufferLength                );            }        }        else if (m_FuzzingType == FuzzingType_Dword)        {                         // 以4字節為單位變異輸入數據             // 對其輸入數據長度             ULONG FuzzingLength = XALIGN_DOWN(InputBufferLength, sizeof(ULONG));             if (FuzzingLength <= DWORD_FUZZING_MAX_LENGTH && FuzzingLength >= sizeof(ULONG))            {                // fuzz each dword value in input buffer                for (ULONG i = 0; i < FuzzingLength; i += DWORD_FUZZING_DELTA)                {                    for (ULONG i_v = 0; i_v < sizeof(m_DwordFuzzingConstants) / sizeof(ULONG); i_v++)                    {                                                // 變異輸入數據                        ULONG OldBuffVal = *(PULONG)((PUCHAR)InputBuffer + i);                        *(PULONG)((PUCHAR)InputBuffer + i) = m_DwordFuzzingConstants[i_v];                           // 調用原函數                        NTSTATUS status = old_NtDeviceIoControlFile(                            FileHandle,                             Event, ApcRoutine,                             ApcContext,                             IoStatusBlock,                             IoControlCode,                             InputBuffer,                             InputBufferLength,                             OutputBuffer,                             OutputBufferLength                        );                         // 恢復原來的數據                        *(PULONG)((PUCHAR)InputBuffer + i) = OldBuffVal;                                            }                }            }        }                  // 恢復輸入數據        RtlCopyMemory(InputBuffer, NewBuff, InputBufferLength);                ExFreePool(NewBuff);    }     // 對輸出數據進行變異    if (OutputBufferLength > 0)    {                // 將輸出數據地址改成用戶空間的一個地址        PVOID TmpOutputBuffer = USER_BUFFER_ADDRESS;         // 調用原函數        NTSTATUS status = old_NtDeviceIoControlFile(            FileHandle,             Event, ApcRoutine,             ApcContext,             IoStatusBlock,             IoControlCode,             InputBuffer,             InputBufferLength,             TmpOutputBuffer, 0        );                 // 將輸出數據地址改成內核空間的一個地址        TmpOutputBuffer = KERNEL_BUFFER_ADDRESS;          // 調用原函數        status = old_NtDeviceIoControlFile(            FileHandle,             Event, ApcRoutine,             ApcContext,             IoStatusBlock,             IoControlCode,             InputBuffer,             InputBufferLength,             TmpOutputBuffer, 0        );    }         // 判斷通信方式    ULONG Method = IoControlCode & 3;    if (Method != METHOD_BUFFERED)    {        // try to fuzz buffer addresses, if method is not buffered        for (int i = 0; i < BUFFERED_FUZZING_ITERATIONS; i++)        {            // 變異輸入,輸出地址與長度,其中地址變異為用戶空間的地址            PVOID TmpInputBuffer  = USER_BUFFER_ADDRESS;            PVOID TmpOutputBuffer = USER_BUFFER_ADDRESS;            ULONG TmpInputBufferLength  = getrand(0, 0x100);            ULONG TmpOutputBufferLength = getrand(0, 0x100);                          // 調用原函數            NTSTATUS status = old_NtDeviceIoControlFile(                FileHandle,                 Event, ApcRoutine,                 ApcContext,                 IoStatusBlock,                 IoControlCode,                 TmpInputBuffer,                 TmpInputBufferLength,                 TmpOutputBuffer,                 TmpOutputBufferLength            );        }                  for (int i = 0; i < BUFFERED_FUZZING_ITERATIONS; i++)        {            // 變異輸入,輸出地址與長度,其中地址變異為內核空間的地址            PVOID TmpInputBuffer  = KERNEL_BUFFER_ADDRESS;            PVOID TmpOutputBuffer = KERNEL_BUFFER_ADDRESS;            ULONG TmpInputBufferLength  = getrand(0, 0x100);            ULONG TmpOutputBufferLength = getrand(0, 0x100);             // 調用原函數            NTSTATUS status = old_NtDeviceIoControlFile(                FileHandle,                 Event,                 ApcRoutine,                 ApcContext,                 IoStatusBlock,                 IoControlCode,                 TmpInputBuffer,                 TmpInputBufferLength,                 TmpOutputBuffer,                 TmpOutputBufferLength            );        }    }}
    

    其中的一些關鍵的宏定義如下:

    #define RANDOM_FUZZING_ITERATIONS   10#define BUFFERED_FUZZING_ITERATIONS 5#define DWORD_FUZZING_MAX_LENGTH    0x200#define DWORD_FUZZING_DELTA         4 #ifdef _X86_ #define KERNEL_BUFFER_ADDRESS (PVOID)(0xFFFF0000)#define USER_BUFFER_ADDRESS   (PVOID)(0x00001000) #elif _AMD64_ #define KERNEL_BUFFER_ADDRESS (PVOID)(0xFFFFFFFFFFFF0000)#define USER_BUFFER_ADDRESS   (PVOID)(0x0000000000001000) #endif // constants for dword fuzzingULONG m_DwordFuzzingConstants[] ={    0x00000000,    0x00001000,    0xFFFF0000,    0xFFFFFFFF};
    

    五、IoctlBf部分源碼

    這個模糊測試器源碼在:https://github.com/koutto/ioctlbf,該模糊測試器則是IoControl Driver Fuzzer。因此,該模糊測試器會首先獲取驅動的合法IoControlCode,然后在對這些合法的IoControlCode進行測試。

    保存合法IoControl的結構體定義如下:

    typedef struct IOCTLlist_ {    DWORD IOCTL;                    // 保存的IoControlCode    DWORD errorCode;    size_t minBufferLength;         // 輸入輸出數據最小長度     size_t maxBufferLength;         // 輸入輸出數據最大長度    struct IOCTLlist_ *previous;    // 指向上一個IOCTLlist_結構,用來連接所有的合法IoControlCode} IOCTLlist, *pIOCTLlist;
    

    在獲取合法IoControlCode的代碼中,beginIoControl和endIoCtl是由用戶指定的,用來指定要測試的IoControlCode范圍,然后從該范圍中一一測試其對應的IoControlCode一一 測試。

    測試步驟如下:

    1. 指定輸入輸出地址為NULL,調用DeviceIoControl。如果函數返回值為0,且GetLastError()的結果為ERROR_ACCESS_DENIED或ERROR_NOT_SUPPORT中的一個,則該IoControlCode不合法,結束本次循環;
    2. 判斷是否指定了filteralwaysok標志,如果指定了,則測試輸入輸出的數據是否大于4,如果是則結束本次循環;
    3. 將輸入輸出長度從0到MAX_BUFSIZE一一測試,來查找輸入輸出數據最小長度,成功查找,則將其作為一個合法的IoControlCode,加入listIoctls中;
    4. 從輸入輸出數據的最小長度+1開始測試,獲取輸入輸出數據的最大長度。

    具體代碼實現如下:

    for(currentIoctl = beginIoctl; currentIoctl<=endIoctl; currentIoctl++) {         // 發送IoControl    status = DeviceIoControl(deviceHandle,                              currentIoctl,                              NULL,                             0,                             NULL,                              0,                              &nbBytes,                              NULL);          // 如果函數調用失敗    if(status == 0) {        errorCode = GetLastError();                 // 如果GetLastError的值為ERROR_ACCESS_DENIED或ERROR_NOT_SUPPORTED        // 則沒有此IoControlCode,結束本次循環        if(errorCode == ERROR_ACCESS_DENIED    ||            errorCode == ERROR_NOT_SUPPORTED)        {            continue;        }      }         // 是否指定了filteralwaysok標志    if(filteralwaysok) {        // 指定輸入輸出緩沖區為最大長度,發送IoControlCode        status = DeviceIoControl(deviceHandle,                                 currentIoctl,                                 &bufInput,                                 MAX_BUFSIZE,                                 &bufOutput,                                 MAX_BUFSIZE,                                 &nbBytes,                                 NULL);        if(status != 0) {            cont   = TRUE;            status = 1;             for(j = 0; j < 4 && status != 0 && cont; j++)             {                status = DeviceIoControl(deviceHandle,                                        currentIoctl,                                        &bufInput,                                        j,                                        &bufOutput,                                        j,                                       &nbBytes,                                        NULL);                              }             if(j == 4) {                // 輸入輸出長度均>4 則結束本次循環                continue;            }         }    }                                     // 查找最小輸入輸出長度    cont = TRUE;    for(j = 0; j < MAX_BUFSIZE && cont; j++) {        status = DeviceIoControl(deviceHandle,                                  currentIoctl,                                  &bufInput,                                  j,                                  &bufOutput,                                  j,                                 &nbBytes,                                  NULL);         if(status != 0) {            // 如果發送成功,則將其加入到listIoctls鏈表            listIoctls = addIoctlList(listIoctls,                                       currentIoctl,                                       0,                                       j,                                       MAX_BUFSIZE);            cont = FALSE;            i++;        }             }     // 找到了最小數據長度,接下來查找最長數據產犢    if(!cont) {        cont = TRUE;         // 發送限制的最長的數據長度        status = DeviceIoControl(deviceHandle,                                  currentIoctl,                                  &bufInput,                                  MAX_BUFSIZE,                                  &bufOutput,                                  MAX_BUFSIZE,                                  &nbBytes,                                  NULL);        if(status != 0)         {            // 如果發送成功,則指定最長數據長度            listIoctls->maxBufferLength = MAX_BUFSIZE;            cont = FALSE;        }                 // 查找最長數據長度        for(j = listIoctls->minBufferLength + 1; j < MAX_BUFSIZE && cont; j++)         {            // 發送IoControlCode            status = DeviceIoControl(deviceHandle,                                      currentIoctl,                                      &bufInput,                                      j,                                      &bufOutput,                                      j,                                      &nbBytes,                                      NULL);            // 發送成功,則更新最長數據長度            if(status == 0) {                listIoctls->maxBufferLength = j - 1;                cont = FALSE;            }        }         if(cont)         {            listIoctls->maxBufferLength = MAX_BUFSIZE;        }    }}
    

    其中的最大長度MAX_BUFSIZE定義如下:

    #define MAX_BUFSIZE 4096      // Max length for input buffer
    

    有了合法的IoControlCode,就可以進行測試,測試步驟如下:

    1. 如果IoControlCode的method不為METHOD_BUFFERED,則將輸入輸出數據地址指定為整型數組invalidAddress中保存的不合法的地址完成測試;
    2. 將輸入數據初始化為0x41,并指定不同的數據長度測試是否存在緩沖區溢出漏洞;
    3. 以4字節為單位,變異輸入數據挖出測試;
    4. 隨機化輸入數據,完成測試。

    具體的代碼實現如下:

    while(1) {     // 從鏈表中獲取IoControl,choice由用戶輸入    posListIoctls = getIoctlListElement(listIoctls, choice);              // 如果method != METHOD_BUFFERED,則將輸入輸出數據地址賦值為非法的地址進行測試    if((posListIoctls->IOCTL & 0x00000003) != 0)     {        cont = TRUE;        for(i = 0; cont && i < INVALID_BUF_ADDR_ATTEMPTS; i++)         {            for(j = 0; cont && j < (sizeof(invalidAddresses) / 4); j++)             {                // 隨機化輸入輸出長度                randomLength = getrand(posListIoctls->minBufferLength,                                        posListIoctls->maxBufferLength);                                 // 將輸入輸出地址指定為不合法的地址                status = DeviceIoControl(deviceHandle,                                          posListIoctls->IOCTL,                                          (LPVOID)invalidAddresses[j],                                          randomLength,                                         (LPVOID)invalidAddresses[j],                                          randomLength,                                          &nbBytes,                                          NULL);            }        }    }              // 測試是否存在緩沖區溢出漏洞    cont = TRUE;    // 初始化輸入數據    memset(bufInput, 0x41, 0x10000);    // 指定不同輸入數據長度,調用函數完成測試    for(i = 0x100; i <= 0x10000; i += 0x100)     {        status = DeviceIoControl(deviceHandle,                                  posListIoctls->IOCTL,                                  &bufInput,                                  i,                                  &bufOutput,                                  i,                                  &nbBytes,                                  NULL);    }              // 以4字節為單位,變異輸入數據    cont = TRUE;    if(SetConsoleCtrlHandler((PHANDLER_ROUTINE) CtrlHandler, TRUE))     {        // 初始化輸入數據        memset(bufInput, 0x00, MAX_BUFSIZE);         // 以4字節為單位,將輸入數據修改為FuzzConstants整型數組中的某一元素        for(i = 0; cont && i < posListIoctls->maxBufferLength; i = i + 4)        {            // 隨機化輸入數據            for(j = 0; cont && j < posListIoctls->maxBufferLength; j++)             {                bufInput[j] = (BYTE)getrand(0x00, 0xff);            }                         for(j = 0; cont && j < (sizeof(FuzzConstants) / 4); j++)             {                // 指定將輸入數據變異為FuzzConstants中的某一元素                fuzzData = FuzzConstants[j];                                 bufInput[i]   = fuzzData & 0x000000ff;                bufInput[i + 1] = (fuzzData & 0x0000ff00) >> 8;                bufInput[i + 2] = (fuzzData & 0x00ff0000) >> 16;                bufInput[i + 3] = (fuzzData & 0xff000000) >> 24;                                 // 調用函數,開始測試                status = DeviceIoControl(deviceHandle,                                          posListIoctls->IOCTL,                                          &bufInput,                                          posListIoctls->maxBufferLength,                                         &bufOutput,                                          posListIoctls->maxBufferLength,                                          &nbBytes,                                          NULL);        }                 // 將輸入數據隨機賦值為FuzzConstants整型數組中的元素        while(cont)         {            // 隨機選取輸入數據長度            randomLength = getrand(posListIoctls->minBufferLength,                                    posListIoctls->maxBufferLength);                         // 初始化輸入數據            memset(bufInput, 0x00, MAX_BUFSIZE);                         // 為輸入數據賦值            for(i = 0; i < randomLength; i = i + 4)            {                // 從FuzzConstants隨機選擇元素賦值輸入數據                fuzzData = FuzzConstants[getrand(0, (sizeof(FuzzConstants) / 4) - 1)];                                                                     bufInput[i]   = fuzzData & 0x000000ff;                bufInput[i + 1] = (fuzzData & 0x0000ff00) >> 8;                bufInput[i + 2] = (fuzzData & 0x00ff0000) >> 16;                bufInput[i + 3] = (fuzzData & 0xff000000) >> 24;            }                   // 調用函數完成測試            status = DeviceIoControl(deviceHandle,                                      posListIoctls->IOCTL,                                      &bufInput,                                      randomLength,                                     &bufOutput,                                      randomLength,                                      &nbBytes,                                      NULL);        }    }              // 將輸入數據隨機初始完成測試    cont = TRUE;    if(SetConsoleCtrlHandler((PHANDLER_ROUTINE) CtrlHandler, TRUE))     {        while(cont)         {            // 隨機選取輸入輸出數據長度            randomLength = getrand(posListIoctls->minBufferLength,                                    posListIoctls->maxBufferLength);                         // 隨機化輸入數據            memset(bufInput, 0x00, MAX_BUFSIZE);            for(i = 0; i < randomLength; i++)             {                bufInput[i] = (BYTE)getrand(0x00, 0xff);            }                                 // 調用函數完成測試            status = DeviceIoControl(deviceHandle,                                      posListIoctls->IOCTL,                                      &bufInput,                                      randomLength,                                     &bufOutput,                                      randomLength,                                      &nbBytes,                                      NULL);         }    }}
    

    其中的幾個全局變量定義如下:

    // Junk data used for fuzzing -------------------------------------------------CHAR asciiString10[0x10];CHAR asciiString100[0x100];CHAR asciiString1000[0x1000]; WCHAR unicodeString10[0x10];WCHAR unicodeString100[0x100];WCHAR unicodeString1000[0x1000];     DWORD tableDwords[0x100];     DWORD FuzzConstants[] = {    0x00000000, 0x00000001, 0x00000004, 0xFFFFFFFF,                            0x00001000, 0xFFFF0000, 0xFFFFFFFE, 0xFFFFFFF0,                             0xFFFFFFFC, 0x70000000, 0x7FFEFFFF, 0x7FFFFFFF,                             0x80000000,                             (DWORD)asciiString10,                             (DWORD)asciiString100,                             (DWORD)asciiString1000,                            (DWORD)unicodeString10,                             (DWORD)unicodeString100,                             (DWORD)unicodeString1000,                            (DWORD)tableDwords };                              DWORD invalidAddresses[] = { 0xFFFF0000, 0x00001000 };
    
    函數調用dword
    本作品采用《CC 協議》,轉載必須注明作者和本文鏈接
    近期有使用手機投屏的需求,用過幾個小工具感覺效果不是很理想,所以想著著手分析下。
    概述在windows系統上,涉及到內核對象的功能函數,都需要從應用層權限轉換到內核層權限,然后再執行想要的內核函數,最終將函數結果返回給應用層。本文就是用OpenProcess函數來觀察函數從應用層到內核層的整體調用流程。OpenProcess函數,根據指定的進程ID,返回進程句柄。NTSTATUS Status; //保存函數執行狀態。OBJECT_ATTRIBUTES Obja; //待打開對象的對象屬性。HANDLE Handle; //存儲打開的句柄。CLIENT_ID ClientId; //進程、線程ID. dwDesiredAccess, //預打開進程并獲取對應的權限。ObjectNamePresent = ARGUMENT_PRESENT ; //判斷對象名稱是否為空
    關于堆棧ShellCode操作:基礎理論002-利用fs寄存器尋找當前程序dll的入口:從動態運行的程序中定位所需dll003-尋找大兵LoadLibraryA:從定位到的dll中尋找所需函數地址004-被截斷的shellCode:加解密,解決shellCode的零字截斷問題
    EXP編寫學習之繞過GS
    2023-02-20 09:58:16
    棧中的守護天使 :GSGS原理向棧內壓入一個隨機的DWORD值,這個隨機數被稱為canary ,IDA稱為 Security Cookie。Security Cookie 放入 ebp前,并且data節中存放一個 Security Cookie的副本。棧中發生溢出時,Security Cookie首先被淹沒,之后才是ebp和返回地址。函數返回之前,會添加一個Security Cookie驗證操作,稱為Security Check。檢測到溢出時,系統將進入異常處理流程,函數不會正常返回,ret也不會被執行。函數使用無保護的關鍵字標記。緩沖區不是8字節類型 且 大小不大于4個字節。可以為函數強制啟用GS。
    該漏洞發生的位置是在驅動文件Win32k.sys中的xxxHandleMenuMessage函數,產生的原因是沒有對該函數中調用的xxxMNFindWindowFromPoint函數的返回值進行合法性驗證,直接將其作為參數傳遞給后面的xxxSendMessage函數調用,從而造成了提權漏洞。
    反射式DLL注入實現
    2022-05-13 15:59:21
    反射式dll注入與常規dll注入類似,而不同的地方在于反射式dll注入技術自己實現了一個reflective loader()函數來代替LoadLibaryA()函數去加載dll,示意圖如下圖所示。藍色的線表示與用常規dll注入相同的步驟,紅框中的是reflective loader()函數行為,也是下面重點描述的地方。
    在所有函數調用發生時,向棧幀內壓入一個額外的隨機 DWORD,隨機數標注為“SecurityCookie”。在函數返回之前,系統將執行一個額外的安全驗證操作,被稱做 Security check。
    MITM Fuzz下圖是用戶層與內核層實現通信的過程,可以看到,最后是通過NtDeviceIoControlFile來分發給相應驅動對象的派遣函數的,因此,可以通過對該函數進行HOOK操作。如果將修改以后的數據發送給NtDeviceIoControlFile函數以后,發生了內核崩潰或藍屏,往往預示著該驅動程序可能存在內核漏洞。
    當線程從等待狀態蘇醒后,會自動檢測自己得APC隊列中是否存在APC過程。所以只需要將目標進程的線程的APC隊列里面添加APC過程,當然為了提高命中率可以向進程的所有線程中添加APC過程。然后促使線程從休眠中恢復就可以實現APC注入。往線程APC隊列添加APC,系統會產生一個軟中斷。第二個參數表示插入APC的線程句柄,要求線程句柄必須包含THREAD_SET_CONTEXT訪問權限。第三個參數表示傳遞給執行函數的參數。如果直接傳入shellcode不設置第三個函數,可以直接執行shellcode。
    shellcode編寫探究
    2022-06-09 15:34:57
    前言shellcode是不依賴環境,放到任何地方都可以執行的機器碼。shellcode的應用場景很多,本文不研究shellcode的具體應用,而只是研究編寫一個shellcode需要掌握哪些知識。要使用字符串,需要使用字符數組。所以我們需要用到 LoadLibrary 和 GetProcAddress 這兩個函數,來動態獲取系統API的函數指針。
    VSole
    網絡安全專家
      亚洲 欧美 自拍 唯美 另类