当前位置:网站首页 > 网络安全培训 > 正文

CobaltStrike Charset Improvement

freebuffreebuf 2022-04-15 374 0

本文来源:

CobaltStrike Charset 

Improvement

Posted on March 16, 2022 by AgeloVito@深蓝攻防实验室

0x01 场景概述

    在使用CobaltStrike的过程中,经常会配合一些第三方工具,比如一些带web探测功能的工具,这些第三方工具会将获取到的webtitle或其他内容通过 beacon console >回显给我们,而这个时候,获取到的webtitle大概率是utf-8编码格式,甚至还带中文,很多时候回显是乱码的。

或者我们再来看看以下小场景,在一个简体中文的win10系统上新建两个文件,分别以utf-8和gb2312两种编码格式存储 中文+英文 的内容,gb2312.txt 和 utf-8.txt。

然后我们从 beacon console > 读取这两个文本的内容可以发现 gb2312编码的gb2312.txt文件中的中文字符显示正常,而utf-8.txt文件中的中文字符则显示乱码。

通过以上两个小场景的简单fuzz,可以得出一个初步的大概结论,该现象的*原因是因为编码不统一*导致的,问题转变为哪里的编码不统一。

0x02 编码定位

要精确的定位问题所在并寻找到比较科学的解决方案就离不开debug,cobaltstrike属于cs架构,从*MANIFEST.MF* 中我们可以得知原作者的开发环境为 *1.7.0_80-b15 (Oracle Corporation)*,所以我们的反编译环境只需要大于该jdk版本即可。client和server的代码都在同一个jar包中,因此我们需要将client端和server端的debug环境都跑起来。

1、环境搭建

整体的环境严格来说应该分为三端,分别为 client.jar (cs使用者),server.jar(teamserver端),client.exe(被控者机器),而cobaltstike的代码量和涉及到的技术含量也不少,所以最佳的选择是增量二开从而减少工作量,我们的需求只是对cs的编码问题进行改善或者增强。

反编译源代码

使用 *IDEA* 自带的反编译插件 java-decompiler.jar 对cobalstrike.jar先进行一次整体的反编译

java -cp java-decompiler.jar org.jetbrains.java.decompiler.main.decompiler.ConsoleDecompiler -dgs=true cs_bin/cobaltstrike.jar cs_src/

反编译全部完成后的代码会被重新打包成 *cobaltstrike.jar* ,将其解压就得到了我们需要的所有源代码

构建二开环境

拿到反编译后的源代码,使用 *IDEA* 和原始 *cobalstrike.jar* 就可以构建一个增量二开的环境了,过程细节不在本文中展开,可参考 *@RedCore* 之前的公开课。

想要修改哪部分的代码就从之前反编译完得到的代码文件中copy该到相应工程目录中进行修改,最后 *Build Artifacts* 即可,经过修改的代码文件就会在依赖于原始jar包的环境下被编译然后增量替换进原始的cobalstrike.jar中。

配置调试环境

配置server端

*Main class* 填写server端的主类

server.TeamServer

*VM options* 填写teamserver文件中的启动参数

-XX:ParallelGCThreads=4 -Dcobaltstrike.server_port=50050 -Djavax.net.ssl.keyStore=./cobaltstrike.store -Djavax.net.ssl.keyStorePassword=123456 -server -XX:+AggressiveHeap -XX:+UseParallelGC -classpath ./cobaltstrike.jar server.TeamServer $*

*Program arguments* 填写我们在运行 teamserver 时给的参数 (./teamserver 172.16.119.1 123456)

172.16.119.1 123456

ip  password

在idea中以调试模式开启teamserver

配置client端

*VM options* 配置参数

-Dfile.encoding=UTF-8 -XX:ParallelGCThreads=4 -XX:+AggressiveHeap -XX:+UseParallelGC

server端和client端的调试环境到这里就成功搭建起来了

2、流程分析

每一次client.exe和server.jar的交互都会在metadata中携带当前系统编码信息,其携带的metadata将在*beacon.BeaconC2.process_beacon_metadata()* 方法中调用 commcon.*WindowsCharsets.getName()* 将其解析为对应的编码类型的值

其中 WindowsCharsets.*getName()* 通过 switch case 维护了一个解析对应表

包含了几乎所有的编码类型

随后在 beaconEntry = new BeaconEntry(var6, var9, var2, var11) 处将 beaconEntry 通过带参构造函数实例化,并将编码类型值复制给 *beaconEntry.chst* 属性

*common.BeaconEntry* 会封装关于一条会话信息的所有信息供在其他业务层级的代码中流通,一些字段的含义通过命名就能猜出个八九不离十

最后在 *this.getCharsets().register(beaconEntry.getId(), var9, var10)* 处将编码信息进行注册

其中 *register()* 的实现是在 *beacon.BeaconCharsets.register()*

public void register(Map map, String var2, String var3) {

  if (var3 != null) {

    try {

      Charset var4 = Charset.forName(var3);

      synchronized(this) {

        map.put(var2, var4);

      }

    } catch (Exception var8) {

      MudgeSanity.logException("Could not find charset '" + var3 + "' for Beacon ID " + var2, var8, false);

    }

     

  }

}

我们测试一下在这里修改被注册的编码类型是否可以起到作用,我们将其强制赋值为utf-8

*bingo!!!*

起作用了,utf-8编码的内容现在能显示正常而gb2312编码的内容变成了乱码

流程走清楚,并且找到了能实现效果的代码位置,接下来就只需要思考如何更好的二开了。

0x03 功能实现

为了更好的实用性,我们选取仿造 *Note* 功能 去实现一个可以动态修改当前编码的功能,包括以下两个部分

1、*beacon console >*

2、**Note...** 

代码分为client端和server端,其中各部分处理流程如下所诉

client.jar 端

*beacon.TaskBeacon.Note()* 处理的是 *beacon console>* 中输入的 note xxx 指令

aggressor.windows.BeaconConsole中第一次处理该请求

if (var3.is("note")) {

  if (var3.verify("Z")) {

    var4 = var3.popString();

    this.master.Note(var4);

  } else if (var3.isMissingArguments()) {

    this.master.Note("");

  }

}

判断 beacon console > 中的 note 指令,并调用 *this.master.Note()* ,其中 this.master 的定义为protected TaskBeacon master;

在 *beacon.TaskBeacon.Note()* 中处理*note xxx*指令

最终在 *common.TeamQueue.run()* 将 beacon console > 中需要执行的指令通过

*TeamQueue.this.socket.writeObject(req);*

发送给 teamserver 端,其实client和server的所有交互最终都会通过此处。

server.jar 端

server 端接收到指令后在 *server.Beacons.buildBeaconModel()*中进行处理,其中 *this.notes* 的定义为

*protected Map notes = new HashMap();*

其中维护的是 *beaconId* 和 *note* 的键值对

*this.notes.get(beaconEntry.getId())* 的返回值是*Object* 类型 Object + "" 的作用是强制类型转换(一点点coder的高级小技巧 ~ )

if (this.notes.containsKey(beaconEntry.getId())) {

  beaconEntry.setNote(this.notes.get(beaconEntry.getId()) + "");

}

组装完 *beaconEntry* 之后 流程走到 *server.Beacons.moment()* 并进行广播

*this.resources.broadcast("beacons", this.buildBeaconModel());*

后续的动作我们就不用在跟下去了,广播的具体实现在这里也不再深入跟进了。

代码实现

在分析完 *note* 命令功能的整体实现以后,仿造其代码实现一个动态修改编码功能就很容易了,本文不再累述,有兴趣的去看看 *note* 的相关代码 ctrl c +v 大法就能很容易实现。其中 *gui (Note...)* 的实现原作者使用的是在 *default.cna* 写的,我们也仿造其实现就好,这里选用的是下拉框,可供选择的编码类型给了9中,能满足大部分的需求场景,其中包括 *("UTF-8", "GBK", "GB2312", "GB18030", "ISO-8859-1", "BIG5", "UTF-16", "UTF-16LE", "UTF-16BE")*,完整代码如下所示。

item "Setchar" {

$bid = $1;

$dialog = dialog("Setchar", %(charsets => ""), &Setchar);

dialog_description($dialog, "Set the Beacon's Charset ");

drow_combobox($dialog, "charsets", "charset:", @("", "UTF-8", "GBK", "GB2312", "GB18030", "ISO-8859-1", "BIG5", "UTF-16", "UTF-16LE", "UTF-16BE"));

      dbutton_action($dialog, "Setchar");

dialog_show($dialog);

    }

    sub Setchar {

     binput($bid, "setchar $3['charsets']");

      beacon_setchar($bid, $3['charsets']);

    }

看着一般,能用就行 ~

0x04 效果演示

1、在console中的效果

添加了一个 *setchar* 命令来设置当前 *beacon* 编码,并在试图中将其展示出来,默认显示的初始值是从metadata中解析出来的。

Use: setchar [text]

e.g: setchar utf-8 | setchar

set a charset to this Beacon.

在console中使用setchar命令将其设置为utf-8

2、使用gui设置的效果

图形菜单的功能其实更实用,和 *Note...* 功能一样,它能方便操作多个beacon。

选择空值就会将编码重置会初始值

0x05 一些总结

其实调试的过程并不是那么快速,本文只是直接给出了记忆中的结论。刚开始如何实现功能也没想的太好,尝试过一下其他的粗暴实现,觉得实在是不够看,在后来调试过程中偶然想到可以借鉴note功能,并且它的功能场景完全符合需求,整个流程分析明白了,代码实现起来就很简单了,最终才做出了这比较满意的效果。

最后,一年一度的节日快到了,想获取完整修改版的朋友,带简历私我哟,We Want You !!!wechat me at Base64.decode("bnVsbC1fLTQwMw==")

转载请注明来自网盾网络安全培训,本文标题:《CobaltStrike Charset Improvement》

标签:工具

关于我

欢迎关注微信公众号

关于我们

网络安全培训,黑客培训,渗透培训,ctf,攻防

标签列表