cobalt strike https stageless 生成分析

前言:cobalt strike https stage 生成分析笔记,这里主要学习cs如何进行构造payload

aggressor.dialogs.WindowsExecutableStageDialog#dialogAction

同样的先aggressor.dialogs.WindowsExecutableStageDialog#dialogAction进行选择payload

然后点击保存之后则来到aggressor.dialogs.WindowsExecutableStageDialog#dialogResult,主要有如下两个部分

ScListener var4 = ListenerUtils.getListener(this.client, var2);
byte[] var5 = var4.export(var3);

这里先观察ScListener var4 = ListenerUtils.getListener(this.client, var2);,该方法做了获取当前listener的名称,然后生成对应的ScListener对象

接着开始调用common.ScListener#export,因为我这里生成的是https的beacon,所以这里走的是beacon.BeaconPayload#exportBeaconStageHTTP

生成的是x86的,所以这里走的是var6 = "resources/beacon.dll";

beacon.BeaconPayload#exportBeaconStage

接着就是来到beacon.BeaconPayload#exportBeaconStage,先通过common.SleevedResource#readResource进行读取resources/beacon.dll

将resources/beacon.dll读取出来之后会进行解密操作,具体操作看dns.SleeveSecurity#decrypt

这里跟进dns.SleeveSecurity#decrypt,通过调试发现在解密之前还会通过对dll的前16个字节进行hmac哈希运算然后跟dll的末尾16个字节进行对比来验证

如果验证相同的话则继续进行解密操作

知识点:关于DLL的解密脚本,代码参考如下,只需要替换对应的密钥字节即可对dll进行解密

import common.SleevedResource;
import java.io.*;
public class DecodeDll {
public static void saveFile(String filename,byte [] data)throws Exception{
if(data != null){
String filepath =filename;
File file = new File(filepath);
if(file.exists()){
file.delete();
}
FileOutputStream fos = new FileOutputStream(file);
fos.write(data,0,data.length);
fos.flush();
fos.close();
}
}
public static byte[] toByteArray(File f) throws IOException {
ByteArrayOutputStream bos = new ByteArrayOutputStream((int) f.length());
BufferedInputStream in = null;
try {
in = new BufferedInputStream(new FileInputStream(f));
int buf_size = 1024;
byte[] buffer = new byte[buf_size];
int len = 0;
while (-1 != (len = in.read(buffer, 0, buf_size))) {
bos.write(buffer, 0, len);
}
return bos.toByteArray();
} catch (IOException e) {
e.printStackTrace();
throw e;
} finally {
try {
in.close();
} catch (IOException e) {
e.printStackTrace();
}
bos.close();
}
}
public static void main(String[] var0) throws Exception {
// aes密钥
byte[] csdecrypt = new byte[]{58,68,37,73,15,56,-102,-18,-61,18,-67,-41,88,-83,43,-103};
SleevedResource.Setup(csdecrypt);
byte[] var7=null;
File file = new File("sleeve");
File[] fs = file.listFiles();
for(File ff:fs){
if(!ff.isDirectory())
var7 = SleevedResource.readResource(ff.getPath());
saveFile("sleevedecrypt\\"+ff.getName(),var7);
System.out.println("解密成功:"+ff.getName());
}
}
}

这里为了验证是否AES128的解密操作,我自己试了下,发现解密beacon.dll出来也是可以看到MZ头的,相关的AES密钥在dns.SleeveSecurity#registerKey这里面找

这里继续回到代码中解密完可以看到PE的头已经出来了,77 90也就是对应的MZ

解密完了之后就开始获取相关c2profile中的配置,比如http-get.uri,然后随机选择一个请求URI

获取监听器listener的轮询模式,我这里是round-robin,这个选项具体什么作用我也没研究过,之后知道再来补上

接着下面获取相关的c2_profile_sleep_time和jitter信息

再接着就是将相关的监听端口以及主机地址和一系列的信息都封装到Settings对象中

其中还会看到publickey字段,这个是beacon端用来和服务端通信过程中加密来进行使用的,可以注意下

知识点:关于publickey的值是哪里来的,可以看下服务端的目录,都有一个对应的文件.cobaltstrike.beacon_keys

如果想要解密元数据的话则可以通过解密.cobaltstrike.beacon_keys获取其中的公钥和私钥来对网络中传输的元数据解密

参考文章:https://research.nccgroup.com/2020/06/15/striking-back-at-retired-cobalt-strike-a-look-at-a-legacy-vulnerability/

下面基本走基本都是一些配置项的读取和配置,就截个图说明下了

接着这里可以注意下关于beacon.BeaconPayload#beacon_obfuscate方法,这个方法决定了c2配置信息在stageless中存储方式

跟进beacon.BeaconPayload#beacon_obfuscate会发现默认加密方式通过异或46十进制来进行加密,而最终生成的异或数据会被嵌套到stageless程序中

知识点:这个特征点很容易被检测到,所以可以换个值来进行实现,同样的在beacon.dll中处理的解密的时候也需要换下对应的值即可

public static byte[] beacon_obfuscate(byte[] var0) {
byte[] var1 = new byte[var0.length];
for(int var2 = 0; var2 < var0.length; ++var2) {
var1[var2] = (byte)(var0[var2] ^ 46);
}
return var1;
}

最后将读取到的dll的字节流转换为ISO8859-1格式,然后定位到AAAABBBBCCCCDDDDEEEEFFFF特征点将其替换为Settings中存储的字节流

pe.MalleablePE#process

pe.MalleablePE#process,该process方法中有两个部分pre_process和post_process

pe.MalleablePE#pre_process(添加PE相关的字段信息)

先是从profile中读取了相关的配置项信息

然后通过PEEditor对象来编辑当前的dll文件,再接着通过pe.PEEditor#checkAssertions来检查Characteristic字段是否为dll文件

下面就是一系列的判断分支对上面读取的配置项信息来做对应的处理

pe.MalleablePE#post_process(ReflectiveLoad 和引导头)

进入到pe.MalleablePE#post_process方法,这边看到pe.BeaconRDLL#process方法

其中调用了common.ReflectiveDLL#findReflectiveLoader 该方法来进行寻找ReflectiveLoader导出函数

这里通过查看解密的dll的导出函数也可以看到,如下图所示

接着继续执行pe.BeaconRDLL#getPatch

然后继续执行pe.BeaconLoader#getDOSHeaderPatchX86,根据名字也可以才出来准备给dos头打补丁了,这里继续跟进去进行观察

可以看到在MZ标志头后面又继续添加了一段数据,实际上这一段数据就是作为引导用的,为了的就是执行反射导出函数做准备

这里的其他的不讲,主要在pe.BeaconRDLL#getReflectiveLoaderFunction方法中通过getLoaderFile会读取一个文件,这个里面的内容就是反射函数要做的操作

然后接下来的就是对读出来的.o文件进行重定位,修复PE头操作,对于.obj文件的这些操作我也不是很懂,最终就是通过setReflectiveLoader将反射函数的字节数组填充

然后再通过CommonUtils.memcpy(var1, var4, var4.length);将引导头进行填充,到了这里就是一个反射dll的操作就已经完成了

最后走出去,还会到了_patchArtifact先生成四个随机字节来进行异或加密,然后来到修复下该文件的校验和

最后借用下其他师傅的总结的图,图片来源:https://xz.aliyun.com/t/10784

posted @   zpchcbd  阅读(501)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
点击右上角即可分享
微信分享提示