深度分析CobaltStrike(一)—— Beacon生成流程及Shellcode分析

 

项目框架

首先拿到网上流传的破解版CobaltStrike4.1,并针对项目进行反编译,可以看到项目目录如下

代码目录较多,由于需要分析的是Beacon的生成,因此我们暂时只关心以下几个目录

  • aggressor(主要负责构建CobaltStrike的GUI功能)
  • beacon(beacon上线以及后续交互等一系列行为的具体实现)
  • stagers(生成各类不同的stagers shellcode)
  • common(可以理解成utils,包含一些常用功能的实现)

 

上线流程

在正式开始分析之前,先要对CS(CobaltStrike,后续简称CS)的机制有一个直观的认识,这里借用gcow团队的一张图进行讲解

当我们通过TeamServer生成了beacon文件后,并在靶机上执行该文件,会产生以下行为

  • 靶机主动请求生成beacon时所选择的Listener
  • 攻击者通过TeamServer发现目标机器已上线
  • 借助TeamServer下发指令给beacon执行(异步或同步)

 

Beacon生成

Stage&Stageless

首先需要清楚的是,Beacon的生成有两种模式

  • stage (有阶段)
  • stageless (无阶段)

所谓的stage(有阶段),指的是Beacon会分段的加载shellcode(具体表现为,通过不断的向Listener发起请求,最终获取一个完整的shellcode并执行),stageless(无阶段),则是在生成时则包含完整的shellcode。

流程分析

由于stage和stageless的生成流程相似,所以接下以stage的生成为例,来跟进代码进行分析,首先是WindowsExecutableDialog这个类,其作用是处理Windows Executable这个会话框的action,有两个主要的函数,一个是dialogAction,一个是dialogResult,其中dialogResult是回调函数。

dialogAction整体的处理流程非常简单,通过调用当前ListenergetPayloadStager函数,获取对应的shellcode,跟进分析,最终调用的函数为GenericStagergenerate

可以看到GenericStager是一个抽象类,generate是接口,这里以Listener为http为例,跟进到GenericHTTPStagergenerate

通过getStagerFile获取shellcode的模板文件

跟进后发现读取的是resources/httpstager64.bin文件 (后面会单独分析该文件)

继续跟进GenericHTTPStagergenerate函数,首先new了一个Packer类,通过阅读该类的函数可知,该类用于操作二进制文件。继续往下走,可以看到反复使用Packer类替换了shellcode的端口/Header/URI等

shellcode生成后,会调用dialogResult回调函数进行处理,其中传入的var1generate生成的Shellcode,这里以64位exe为例,继续跟进patchArtifact函数

首先去resources目录下取了artifact.exe文件的模板

通过生成随机数var6,异或之前传入的shellcode,并找到1024个A所在的位置(需要替换为shellcode)的位置,将异或后的shellcode写入,最终生成完整的PE文件

Beacon外壳分析

将生成的artifact.exe拖到IDA里面分析,首先找到main函数

main函数中,先后调用了sub_402A60sub_401795两个函数,直接跟进sub_401795进行分析

可以看到先初始化了变量Buffer,格式为%c%c%c%c%c%c%c%c%cMSSE-%d-server,然后调用CreateThread执行sub_401605函数,跟进分析

先前格式化的Buffer名称,在sub_4015D0中被用于创建命名管道,然后将lpBuffer中的值写入到管道中,而传入的lpBuffer,实际指向unk_404014

回到sub_401795中,继续往下走,在return的时候调用了sub_401742

sub_4016A2中,将之前写入管道的数据读到了v0当中

将读出的数据传入到函数sub_40152E中,针对该数据进行解异或操作,然后将其作为参数传入CreateThread,走到这里可以判断,先前写入管道的数据,即为异或后的shellcode

对比先前的_patchArtifact函数以及IDA反编译后,解异或所用的数据unk_404014,可以发现是温和的,后面正好跟了两个值为0的dword

Shellcode分析

首先调用LoadLibrary加载wininet.dll,其中rbp所指向的函数sub_160001为寻找加密哈希所对应的函数,此处726774C对应的为LoadLibrary

紧接着调用IntenetOpenUrlA传入了5个NULL值

紧接着调用InternetConnectW连接先前CobaltStrike Listener中配置的IP

InternetConnectW返回的句柄,传入到HttpOpenRequestW中,对应的url为C2zn

使用HttpSendRequestA发送请求,并设置请求头

通过InternetReadFile循环读取C2的数据,并写入到用VirtualAlloc申请的内存中,读取完毕后,跳转到写入数据所在的地址并执行

通过hash调用函数

上文所有的函数调用,都是通过将hash写入到r10d中,再交由sub_160001进行判断,那sub_160001是怎么判断的呢?

将参数压入栈后,做了一系列的寻址操作。首先是gs:[rdx+60]gs这个段寄存器,代表的是TEB的地址(线程环境变量块)

TEB偏移0x60的地方,是PEB(进程环境变量块),接着寻址了PEB偏移0x18的地址

PEB偏移0x18的地方,是Ldr,而Ldr,存放了进程所加载的动态链接库的信息

PEB偏移0x20的地方是InMemoryOrderModuleList,这是一个双向链表,每个指针都指向一个_LDR_DATA_TABLE_ENTRY结构

由于InMemoryOrderModuleList指向的是_LDR_DATA_TABLE_ENTRY中的InMemoryOrderLinks而非首地址,因此 ds:[rdx+20]指向的是DllBase,也就是Dll的基地址

ds:[rdx+3C]则是IMAGE_DOS_HEADER中的e_lfanew,它指向了实际的PE头

ds:[rax+18]指向OptionalHeader,该结构首地址指向了一个魔数,代表PE文件的类型(32/64),因此cmp word ptr ds:[rax+18],20B用于判断PE文件类型

接着将ds:[rax+88]的地址存到eax中,而ds:[rax+88]实际指向DataDirectory(0x18+0x70=0x88)

DataDirectory数组的第一个元素就是导出表,因此此时eax中的地址为导出表的地址

ds:[rax+18]ds:[rax+20]分别对应函数的数量以及导出表的RVA

接着就是遍历导出表,计算函数hash,判断跟传入的hash是否一致,如果一致则调用

 

总结

本篇文章深入的剖析了Beacon生成流程及Shellcode分析,后面的文章会着重分析CS的通信流程以及Beacon的模块加载,以及基于这些深度分析的免杀思考

(完)