CobaltStrike高免杀样本分析

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执行。
mark
mark

         第2层powershell脚本中,依旧是执行Base64加密/编码过的Powershell语句
mark

         经过解码得到解码之后的Powershell脚本。第三层Powershell脚本逻辑也很清楚,首先解码一段base64,然后进行xor解密,获得一段shellcode,然后将解密获得的shellcode复制到内存中并执行。
mark

         经过分析,转储而来的shellcode是典型的cobaltstrike生成stageless类型的shellcode。也就是所谓的分离式免杀的操作。
mark
mark

         其本质还是一个download。其会加载winnet.dll模块,然后依次调用wininet!InternetOpenA,wininet!InternetConnectA,KERNEL32!VirtualAllocStub,wininet!InternetReadFile等网络读取函数读取服务端获取的payload。最终在栈顶保存的是读取的payload的地址,待到函数返回,执行流跳转到payload处。
mark
mark

         可以看到下载的bin主要有两部分组成,一个是引导shellcode,第二部分是payload,主要用来引导执行payload。这也是cobaltstrike特征之一。
mark

         显然,熟悉beacon的一眼就看到了cobaltstrike特征,这是beacon的PE文件,在默认生成的beacon文件中,这是导出的ReflectiveLoad函数,处理完PE数据之后,最终调用DllEntry函数进行后续的处理。
mark
mark

0x02 Example2

          这个loader使用GoLang编写,该样本并没有去除符号表,首先,检查系统的CPU个数和逻辑分区个数,如果CPU个数和逻辑分区个数小于4的话,则退出。
mark

         然后调用HeapCreate开辟内存空间,Golang编译的程序在汇编层面的函数调用方式和C++编译的有些许差异,在x64下,通过[eax]进行压栈传入参数,然后将需要调用的函数传入rax寄存器中,rcx保存的是参数的个数。并调用ZwAllocateVirtualMemory修改内存属性。
mark
mark

         16个字节组成的Byte[],如果需要将shellcode转化为UUID形式的话,如果shellcode长度不足16的话,将自动补齐。在CobaltStrike免杀样本中,UUID是以字符串形式存在于内存中,其通过API函数UuidFromStringA将其转化为UUID格式。
mark

         最后,通过EnumSystemLocalesW函数通过回调的方式执行shellcode,同样的可以进行回调的还有以下函数

1
2
3
4
5
6
7
8
9
10
11
12
13
1, EnumTimeFormatsA
2, EnumWindows
3, EnumDesktopWindows
4, EnumDateFormatsA
5, EnumChildWindows
6, EnumThreadWindows
7, EnumSystemLocales
8, EnumSystemGeoID
9, EnumSystemLanguageGroupsA
10, EnumUILanguagesA
11, EnumSystemCodePagesA
12, EnumDesktopsW
13, EnumSystemCodePagesW

mark

0x03 Example3

  • 看图标和关键字显然,这是由pyinstaller打包而成的exe文件。目前常见的py打包工具主要有pyinstallerpy2exe,以及cx_Freeze
    mark

  • 针对pyinstaller打包而来的exe,可以使用pyinstxtractor.py进行解包,完成后,会在同目录生成一个extracted结尾的文件夹,在文件夹中,有两个没有后缀的文件,其中必然有一个名为struct文件。
    mark

  • 那个名字为1的文件是一个抹去了时间戳和Magic的pyc文件,而抹去的信息可以在struct文件中找到,只需要复制struct文件的前16个字节复制到1这个文件中,即可。
    mark

  • 然后使用在线的pyc2py的网站https://tool.lu/pyc/就可以得到py代码如下。使用伪随机数解密,因为随机数种子是固定的,因为产生的随机数也是固定的。解密秘钥也就是固定的。loader是shellcode装载器,value是解密的shellcode。
    mark

  • loader代码如下,很常见的加载方式

    1
    2
    3
    4
    5
    6
    shellcode = binascii.a2b_hex(value)
    ctypes.windll.kernel32.VirtualAlloc.restype=ctypes.c_uint64
    rwxpage = 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。
    mark

  • Ref:https://xz.aliyun.com/t/10450#toc-8