0x00 背景
近期,在狩猎中,捕获到一批高度免杀的CobaltStrike样本,经过分析,目前这些免杀样本具备以下几种特征,第一,不在拘泥于传统的C++作为编程语言,也会采用C#或者Golang,或者脚本语言也会在整个攻击链中使用不同的语言,使用C++可能对使用者的要求有所提高,但是免杀效果可能更好。第二,对于shellcode的加密方式,不在拘泥于传统的异或或者AES,Base等公开的加密编码算法,也会采用一些好玩的,个性化的编码算法进行shellcode加密。第三,在开辟内存空间的时候,也不在使用传统的VirtualAlloc等R3层的API函数,更多的使用基于底层的API函数,第四,在调用shellcode过程中,也不像往常一样使用常规的方式调用shellcode,而是利用一些API的机制(CreateThread),或者回调机制(EnumSystemLocalesW),以及APC。
0x01 Example1
这个loader由C#编写,通过powerShell.AddScript(@string);
加载加密过的Powershell脚本,然后通过powerShell.BeginInvoke
执行。
第2层powershell脚本中,依旧是执行Base64加密/编码过的Powershell语句
经过解码得到解码之后的Powershell脚本。第三层Powershell脚本逻辑也很清楚,首先解码一段base64,然后进行xor解密,获得一段shellcode,然后将解密获得的shellcode复制到内存中并执行。
经过分析,转储而来的shellcode是典型的cobaltstrike生成stageless类型的shellcode。也就是所谓的分离式免杀的操作。
其本质还是一个download。其会加载winnet.dll模块,然后依次调用wininet!InternetOpenA
,wininet!InternetConnectA
,KERNEL32!VirtualAllocStub
,wininet!InternetReadFile
等网络读取函数读取服务端获取的payload。最终在栈顶保存的是读取的payload的地址,待到函数返回,执行流跳转到payload处。
可以看到下载的bin主要有两部分组成,一个是引导shellcode,第二部分是payload,主要用来引导执行payload。这也是cobaltstrike特征之一。
显然,熟悉beacon的一眼就看到了cobaltstrike特征,这是beacon的PE文件,在默认生成的beacon文件中,这是导出的ReflectiveLoad函数,处理完PE数据之后,最终调用DllEntry函数进行后续的处理。
0x02 Example2
这个loader使用GoLang编写,该样本并没有去除符号表,首先,检查系统的CPU个数和逻辑分区个数,如果CPU个数和逻辑分区个数小于4的话,则退出。
然后调用HeapCreate开辟内存空间,Golang编译的程序在汇编层面的函数调用方式和C++编译的有些许差异,在x64下,通过[eax]进行压栈传入参数,然后将需要调用的函数传入rax寄存器中,rcx保存的是参数的个数。并调用ZwAllocateVirtualMemory修改内存属性。
16个字节组成的Byte[],如果需要将shellcode转化为UUID形式的话,如果shellcode长度不足16的话,将自动补齐。在CobaltStrike免杀样本中,UUID是以字符串形式存在于内存中,其通过API函数UuidFromStringA
将其转化为UUID格式。
最后,通过EnumSystemLocalesW
函数通过回调的方式执行shellcode,同样的可以进行回调的还有以下函数
0x03 Example3
看图标和关键字显然,这是由
pyinstaller
打包而成的exe文件。目前常见的py打包工具主要有pyinstaller
,py2exe
,以及cx_Freeze
针对
pyinstaller
打包而来的exe,可以使用pyinstxtractor.py
进行解包,完成后,会在同目录生成一个extracted结尾的文件夹,在文件夹中,有两个没有后缀的文件,其中必然有一个名为struct文件。
那个名字为1的文件是一个抹去了时间戳和Magic的pyc文件,而抹去的信息可以在struct文件中找到,只需要复制
struct
文件的前16个字节复制到1
这个文件中,即可。
然后使用在线的pyc2py的网站https://tool.lu/pyc/就可以得到py代码如下。使用伪随机数解密,因为随机数种子是固定的,因为产生的随机数也是固定的。解密秘钥也就是固定的。loader是shellcode装载器,value是解密的shellcode。
loader代码如下,很常见的加载方式
123456shellcode = binascii.a2b_hex(value)ctypes.windll.kernel32.VirtualAlloc.restype=ctypes.c_uint64rwxpage = ctypes.windll.kernel32.VirtualAlloc(0, len(shellcode), 0x1000, 0x40)ctypes.windll.kernel32.RtlMoveMemory(ctypes.c_uint64(rwxpage), ctypes.create_string_buffer(shellcode), len(shellcode))handle = ctypes.windll.kernel32.CreateThread(0, 0, ctypes.c_uint64(rwxpage), 0, 0, 0)ctypes.windll.kernel32.WaitForSingleObject(handle, -1)而shellcode是很明显的coabaltstrike的shellcode。