项目框架
首先拿到网上流传的破解版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(有阶段),指的是Beacon会分段的加载shellcode(具体表现为,通过不断的向Listener发起请求,最终获取一个完整的shellcode并执行),stageless(无阶段),则是在生成时则包含完整的shellcode。
流程分析
由于stage和stageless的生成流程相似,所以接下以stage的生成为例,来跟进代码进行分析,首先是这个类,其作用是处理这个会话框的action,有两个主要的函数,一个是,一个是,其中是回调函数。
WindowsExecutableDialog
Windows Executable
dialogAction
dialogResult
dialogResult
整体的处理流程非常简单,通过调用当前的函数,获取对应的shellcode,跟进分析,最终调用的函数为的
dialogAction
Listener
getPayloadStager
GenericStager
generate
可以看到是一个抽象类,是接口,这里以为http为例,跟进到的中
GenericStager
generate
Listener
GenericHTTPStager
generate
通过获取shellcode的模板文件
getStagerFile
跟进后发现读取的是文件 (后面会单独分析该文件)
resources/httpstager64.bin
继续跟进的函数,首先了一个类,通过阅读该类的函数可知,该类用于操作二进制文件。继续往下走,可以看到反复使用类替换了shellcode的端口/Header/URI等
GenericHTTPStager
generate
new
Packer
Packer
shellcode生成后,会调用回调函数进行处理,其中传入的为生成的Shellcode,这里以64位exe为例,继续跟进函数
dialogResult
var1
generate
patchArtifact
首先去目录下取了文件的模板
resources
artifact.exe
通过生成随机数,异或之前传入的shellcode,并找到1024个A所在的位置(需要替换为shellcode)的位置,将异或后的shellcode写入,最终生成完整的PE文件
var6
Beacon外壳分析
将生成的拖到IDA里面分析,首先找到main函数
artifact.exe
在函数中,先后调用了和两个函数,直接跟进进行分析
main
sub_402A60
sub_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
回到中,继续往下走,在return的时候调用了
sub_401795
sub_401742
在中,将之前写入管道的数据读到了当中
sub_4016A2
v0
将读出的数据传入到函数中,针对该数据进行解异或操作,然后将其作为参数传入,走到这里可以判断,先前写入管道的数据,即为异或后的shellcode
sub_40152E
CreateThread
对比先前的函数以及IDA反编译后,解异或所用的数据,可以发现是温和的,后面正好跟了两个值为0的
_patchArtifact
unk_404014
dword
Shellcode分析
首先调用加载,其中rbp所指向的函数为寻找加密哈希所对应的函数,此处对应的为
LoadLibrary
wininet.dll
sub_160001
726774C
LoadLibrary
紧接着调用传入了5个NULL值
IntenetOpenUrlA
紧接着调用连接先前中配置的IP
InternetConnectW
CobaltStrike Listener
返回的句柄,传入到中,对应的url为
InternetConnectW
HttpOpenRequestW
C2zn
使用发送请求,并设置请求头
HttpSendRequestA
通过循环读取C2的数据,并写入到用申请的内存中,读取完毕后,跳转到写入数据所在的地址并执行
InternetReadFile
VirtualAlloc
通过hash调用函数
上文所有的函数调用,都是通过将hash写入到中,再交由进行判断,那是怎么判断的呢?
r10d
sub_160001
sub_160001
将参数压入栈后,做了一系列的寻址操作。首先是,这个段寄存器,代表的是的地址(线程环境变量块)
gs:[rdx+60]
gs
TEB
偏移0x60的地方,是(进程环境变量块),接着寻址了偏移0x18的地址
TEB
PEB
PEB
偏移0x18的地方,是,而,存放了进程所加载的动态链接库的信息
PEB
Ldr
Ldr
偏移0x20的地方是,这是一个双向链表,每个指针都指向一个结构
PEB
InMemoryOrderModuleList
_LDR_DATA_TABLE_ENTRY
由于指向的是中的而非首地址,因此 指向的是,也就是Dll的基地址
InMemoryOrderModuleList
_LDR_DATA_TABLE_ENTRY
InMemoryOrderLinks
ds:[rdx+20]
DllBase
则是中的,它指向了实际的PE头
ds:[rdx+3C]
IMAGE_DOS_HEADER
e_lfanew
指向,该结构首地址指向了一个魔数,代表PE文件的类型(32/64),因此用于判断PE文件类型
ds:[rax+18]
OptionalHeader
cmp word ptr ds:[rax+18],20B
接着将的地址存到中,而实际指向(0x18+0x70=0x88)
ds:[rax+88]
eax
ds:[rax+88]
DataDirectory
数组的第一个元素就是导出表,因此此时中的地址为导出表的地址
DataDirectory
eax
和分别对应函数的数量以及导出表的RVA
ds:[rax+18]
ds:[rax+20]
接着就是遍历导出表,计算函数hash,判断跟传入的hash是否一致,如果一致则调用
总结
本篇文章深入的剖析了生成流程及分析,后面的文章会着重分析CS的通信流程以及的模块加载,以及基于这些深度分析的免杀思考
Beacon
Shellcode
Beacon
欢迎登录安全客 -有思想的安全新媒体www.anquanke.com/加入QQ交流群1015601496 获取更多最新资讯
原文链接: https://www.anquanke.com/post/id/237127
特别声明:以上内容(如有图片或视频亦包括在内)为自媒体平台“网易号”用户上传并发布,本平台仅提供信息存储服务。
Notice: The content above (including the pictures and videos if any) is uploaded and posted by a user of NetEase Hao, which is a social media platform and only provides information storage services.