在 CS 4.X 之后的资源文件都加密存储在sleeve文件夹中,使用网上的解密文件解密即可。将原始 cobaltstrike.jar 及 CrackSleeve.java 放到同一文件夹下(不能包含文件路径),然后运行如下命令:
javac -encoding UTF-8 -classpath cobaltstrike.jar CrackSleeve.java
java -classpath cobaltstrike.jar;./ CrackSleeve decode
内存签名虽然无法检测到未知的恶意软件,并且在检测Beacon内存签名时太容易被规避,但其误报率接近于零。规避的难易只是需要考虑的众多因素之一。性能和误报率也是衡量检测技术有效性的关键。Beacon通常是反射加载到内存中,为掩盖这些特征,cobaltstrike自带了obfuscate-and-sleep选项(启用它只需要在Profile里面配置stage->sleep_mask即可),它会在Beacon睡眠时将自身绝大部分加密,以专门避开基于签名的内存扫描。但即便是开启这个选项,Beacon也会被发现。因为该选项并不会对负责加解密的代码进行混淆,于是这些就会直接暴露在内存中,可以使用如下yara规则进行扫描:
检测代码段:
检测字符串:(因为单字节 XOR 是最老套的技巧之一,yara支持使用 xor 修饰符进行检测)
我们先来看看禁用 sleep_mask 的情况下,内存中的Beacon:运行artifact.exe,用ProcessHacker.exe打开查看
通过查找调用 SleepEx 的线程来定位内存中的Beacon
然后将关联的内存区域保存到磁盘,以供分析
发现Beacon很容易被检测到特征:(artifact.exe.bin 是禁用 sleep_mask 模式下 sleep 状态导出的内存)
代码段:
字符串:
再看启用 sleep_mask 的情况下,内存中的Beacon:
(sleepMask.exe.bin 是启用 sleep_mask 模式下 sleep 睡眠状态导出的内存)
(sleepMask.exe_sleep0.bin 是启用 sleep_mask 模式下 sleep 0 活跃状态导出的内存)
sleep_mask = true sleep 0下:
代码段:
字符串:
已经搜索不到 .yara 规则中的字符串了。这样也就绕过了争对字符串的检测:
但是代码段中,负责加解密的代码部分并未被混淆,如下图:sleep_mask = true sleep 睡眠状态下:
字符串检测Bypass sleep_mask = true 即可,sleep时间无所谓。
代码段检测Bypass 绕过思路非常简单,只需要修改一个字节就能绕过.yar特征码。IDA Patch beacon.x64.dll
mov r9d,[r10] 硬编码如下图:
mov r11d,[r10+4] 硬编码如下图:
Patch:(代码位置上下互换)
保存修改:
beacon.dll(x86)一样的操作,略。
将所有文件重新加密回去,然后替换掉项目目录下的sleeve,Rebuild 即可
java -classpath cobaltstrike.jar;./ CrackSleeve encode
测试上线及Bypass效果:
在反射执行beacon.dll时,会通过malloc申请堆内存,然后Xor解密C2 Porfile配置信息并通过嵌入格式循环读取解密后的配置信息来存放与TeamServer交互的配置(Beacon Config)。而该配置信息的嵌入格式在TeamServer端的Java代码中是写死的,beacon.dll也是按顺序获取配置。所以BeaconEye采用Yara规则将Beacon Config的内存特征做为扫描的对象(不检测beacon.dll PE文件映射的内存本身),并且可解析出对应的Beacon信息。
要想Bypass BeaconEye,首先需要了解TeamServer端对beacon.dll Patch C2 Profile的处理及beacon.dll自身解析C2 Profile到申请的堆内存的处理细节。
TeamServer端对beacon.dll Patch C2 Profile的处理
跟进 Settings 类:
将解密后的 beacon.dll 与 生成的 beacon_patch.dll 进行比较:
发现确实在dll文件中Patch了加密后的C2 Profile配置信息,我们进行 0x2E 解密查看原始的配置长啥样:
以上圈起来的二进制内容是TeamServer向beacon.dll中Patch的Sleep Time的数据(索引:3 数据类型:2 数据宽度:4 数据:00 00 EA 60即十进制为60000 ),其它数据以此类推。
beacon.dll自身解析C2 Profile到申请的堆内存的处理
IDA静态调试beacon_patch.dll,定位到入口点DllMain
byte_10032020处正好是TeamServer端Patch的加密的C2 Profile配置:
sub_100067D0()函数实现:
sub_1000682C()函数实现:
根据上述分析,在执行beacon.dll代码段时,会申请一块堆内存来存放从特定偏移处循环读取的C2 Profile配置信息。但并不是将 索引、数据类型、数据宽度、数据全部都存放到堆内存中,而仅将 数据类型及数据 存储到堆内存。存储的数据刚命中BeaconEye x86 Yara规则,Yara规则中第一行全为0是因为C2 Profile数据是从+0x08开始复制到堆内存中的。
根据以上分析,不难发现beacon.dll申请并初始化的堆内存中的一片00都是没有意义的,所以我们只需要将堆内存初始化为非0数据BeaconEye就扫描不到了。x32 beacon.dll 修改:
修改为非0值即可绕过检测:
x64 beacon.dll 修改:
补充 —— 通过伪代码定位汇编代码所在位置:
将所有文件重新加密回去,然后替换掉项目目录下的sleeve,Rebuild 即可。
此时运行artifact.exe上线后,使用BeaconEye已经扫描不到了
Bypass 卡巴斯基内存扫描
先禁用 sleep_mask,使用ProcessHacker工具将artifact.exe进程的内存提取出来,方便后续定位内存特征码。通过以下方式将dump出来的内存bin文件加载到RWX内存中,再使用卡巴查杀内存(先要保证加载器、内存bin文件可落地):
定位到内存特征码如下:(10 FE)
查看内存特征码在beacon.x64.dll中的汇编代码:Alt+B搜索 10 FE FO
这里代码上下互换也被查杀。试一波16进制加一减一法。
有个小Tips:用ProcessHacker修改内存时,修改一个字节保存一下,看是否会导致异常退出程序。这里发现只能修改 10 fe 这2个字节,修改 0f 为任意其它值将导致进程崩溃。
测试修改这2个字节后看能否Bypass
修改beacon.dll文件2字节特征码,将所有文件重新加密回去,然后替换掉项目目录下的sleeve,Rebuild。测试发现artifact.x64.exe无法上线。于是想到不对beacon.dll文件本身进行修改,转而对内存进行Patch(因为我们用ProcessHacker直接修改内存还能正常执行命令):
设置 sleep 0
本文章为免杀二期学员投稿,免杀四期即将开班,前5名预定可减免300学费,详情请咨询vx:WhoamiPriv