• [x] <font color=red>想要理解整个认证过程,必须要动手走一轮代码。</font>

  • [x] <font color=#008000>想要理解整个认证过程,必须要动手走一轮代码。</font>

  • [x] <font color=Blue>想要理解整个认证过程,必须要动手走一轮代码。</font>

  • [x] <font face="STCAIYUN">想要理解整个认证过程,必须要动手走一轮代码。</font>

  • [x] <font face="微软雅黑">想要理解整个认证过程,必须要动手走一轮代码。</font>

  • [x] <font face="黑体">想要理解整个认证过程,必须要动手走一轮代码。</font>

0x00 前言

这里提供了 CS 4.0 的认证过程,个人认为非常详细,文中配备认证的流程图,可以结合文中的代码注释,外加自己的 IDEA 调试,可以完整理解整个过程。因为 4.0 与 4.1 差了一个关键 key(前期的处理方式也多了一个步骤),因此这里就只针对 4.0 版本的认证进行说明。 附件中提供了 CSHook.jar,是针对 CS 4.1 版本的,并且文章中也明确提供了适用于 CS 4.1 的完整 key(使用该 key 需要删除多余的步骤,直接使用 4.0 的验证)。

很多人拿到原版之所以没有搞破解,是因为缺少了最重要的 Sleeved 解密 key。 其实,到了 4.X 版本,是没有办法进行"破解"的,因为 AES 的密钥是无法进行破译,所以 Sleeved 解密 key 只能等好心人提供。

CobaltStrike 4.X 的认证,如果对 Java 及密码学相关有所了解,理解起来并不难。但是对于它的破解来说,需要一个针对 Sleeved 模块的认证 key,这个 key 是无法进行穷举的,除非想不开了。因此对于破解来说,与其说破解,还不如说是将 key 进行补全了。

0x01 准备工作

1.1、必备知识

1.1.1、RAS 算法之加密与签名的区别

加密和签名都是为了安全性考虑,但略有不同。常有人问加密和签名是用私钥还是公钥?其实都是对加密和签名的作用有所混淆。简单的说,加密是为了防止信息被泄露,而签名是为了防止信息被篡改。这里举 2 个例子说明。

  • 第一个场景:战场上,B 要给 A 传递一条消息,内容为某一指令。

RSA 的加密过程如下:

1A 生成一对密钥公钥和私钥),私钥不公开A 自己保留公钥为公开的任何人可以获取
2A 传递自己的公钥给 BB  A 的公钥对消息进行加密
3A 接收到 B 加密的消息利用 A 自己的私钥对消息进行解密

  在这个过程中,只有 2 次传递过程,第一次是 A 传递公钥给 B,第二次是 B 传递加密消息给 A,即使都被敌方截获,也没有危险性,因为只有A的私钥才能对消息进行解密,防止了消息内容的泄露。

  • 第二个场景:A 收到 B 发的消息后,需要进行回复“收到”。

RSA 签名的过程如下:

1A 生成一对密钥公钥和私钥),私钥不公开A 自己保留公钥为公开的任何人可以获取
2A 用自己的私钥对消息加签形成签名并将加签的消息和消息本身一起传递给 B
3B 收到消息后在获取 A 的公钥进行验签如果验签出来的内容与消息本身一致证明消息是 A 回复的

  在这个过程中,只有 2 次传递过程,第一次是 A 传递加签的消息和消息本身给 B,第二次是 B 获取 A 的公钥,即使都被敌方截获,也没有危险性,因为只有 A 的私钥才能对消息进行签名,即使知道了消息内容,也无法伪造带签名的回复给 B,防止了消息内容的篡改。

  但是,综合两个场景你会发现,第一个场景虽然被截获的消息没有泄露,但是可以利用截获的公钥,将假指令进行加密,然后传递给 A。第二个场景虽然截获的消息不能被篡改,但是消息的内容可以利用公钥验签来获得,并不能防止泄露。所以在实际应用中,要根据情况使用,也可以同时使用加密和签名,比如 A 和 B 都有一套自己的公钥和私钥,当 A 要给 B 发送消息时,先用 B 的公钥对消息加密,再对加密的消息使用 A 的私钥加签名,达到既不泄露也不被篡改,更能保证消息的安全性。

  总结:公钥加密、私钥解密;私钥签名、公钥验签。

但是,有一个要注意的是:

当你用公钥加密的时候,需要用私钥解密。
当你用私钥加密的时候,需要用公钥解密。
1.1.2、HMAC 消息摘要算法

MAC,全称 Message Authentication Code,也称为消息认证码(带密钥的Hash函数),通信实体双方使用的一种验证机制,保证消息数据完整性的一种工具。

在发送数据之前,发送方首先使用通信双方协商好的散列函数计算其摘要值。在双方共享的会话密钥作用下,由摘要值获得消息验证码。之后,它和数据一起被发送。接收方收到报文后,首先利用会话密钥还原摘要值,同时利用散列函数在本地计算所收到数据的摘要值,并将这两个数据进行比对。若两者相等,则报文通过认证。

说白了就是计算摘要的时候,需要一个秘钥 key,没有秘钥 key 就无法计算

1.1.3、AES
  • 破解 AES 算法需要多长时间?

以 AES-128 算法为例,平均需要尝试 2^127 ≈ 1.7*10^38 个 128bit 的随机数作为密钥进行加解密运算,方能找到正确的密钥。

常言道,“天下武功,唯快不破”;反之,天下密码,快必可破。问题是,那得有多快?我们知道,比特币网络在全球范围内调用了非常庞大的硬件资源以达到极高的运算效率,每秒钟操作的 Hash 运算(SHA-256)可高达 2.5644*10^19次。虽然 AES 和 SHA-256 算法并不相同,运算量也有所差异,但我们不妨近似地用该数据估算全球人民众志成城破解 AES 算法所需要的时间。

假设 AES 的运算效率为 2.564410^19 ≈ 2^64.4753 次/秒,则进行 2^127 次 AES 运算所需要的时间为:
2^127 / 2^64.4753 ≈ 2^62.5247秒 ≈ 6.6345
10^18 秒 ≈ 1.8429 10^15 小时 ≈ 7.6789 10^13 天 ≈ 2.104 * 10^11年 ≈ 210,400,000,000 年

1.2、运行环境

此次破解测试使用的工具及文件为:

  • IntelliJ IDEA Community Edition 2020.1.4
  • Feb 22, 2020 - Cobalt Strike 4.0

使用过 IDEA 的朋友都知道,它具备反编译 Jar 包的能力。

首先,我们使用 IDEA 新建一个工程,将原始 Jar 包作为依赖进行导入,如下图所示:

此时 IDEA 将调用反编译模块,因此我们可以直接查看 jar 的源码,如图所示:

但由于单个文件点击,并不利于我们的有效查看,因此可以提取 IDEA 的反编译功能,用于对原始 Jar 包的反编译。

下面我们进行测试,IDEA 的反编译功能依赖于 java-decompiler.jar ,该文件存在于以下路径当中:

%IDEA安装目录%\plugins\java-decompiler\lib\java-decompiler.jar

其使用方法为:

java -cp java-decompiler.jar org.jetbrains.java.decompiler.main.decompiler.ConsoleDecompiler -dgs=true c:\my.jar d:\decompiled\

将反编译后的 Jar 包进行解压,将解压的文件(带文件夹)放入 src(该步骤仅将需要更改的文件放入即可,当然,全部放也没关系),文件夹内,如下图所示:

然后就是设置编译生成 Jar 的步骤。

Main Class 中填写 aggressor.Aggressor,其余默认即可;然后尝试 Build Artifacts...,正常情况下,则生成一个新的 Jar 包。

最后,为了方便实时预览及调试,我们需要对 Run 进行简单设置。

1.新建一个 run 配置
2.添加 JAR Application
3.选择运行的 Jar 包路径
4.配置启动该 Jar 包的虚拟选项
5.选择一个在执行 Run 操作时附带的操作
6.此处选择重新 Build Artiface

注意如果不选择 5-6 步骤则在点击 Run 需要手动 Build Artiface

实践一下,是否配置都正常。出现以下信息就说明可行。

这部分内容不理解的朋友,可以去看看红队学院(知识星球)相关视频:RedCore 红队学院 CSTips

0x02 CS 3.X 版本的认证过程

其实,我们可以从头开始走一轮认证代码,3.X 相对简单,走下来其实不难。

主要涉及的文件:

common/License  // license 检查逻辑
common/Authorization // 检查的细节实现
common/AuthCrypto   // RSA 解密和解压
common/CommonUtils  // 相关数据类型转换辅助

先粗略说一下 3.X 的 .auth 整个加密过程是:

先对文本进行压缩,转换为 byte
添加特征头 0xca, 0xfe, 0xc0, 0xbb,0x00, 0x43
使用 RSA 进行加密

故此解密的话只需要逆向此流程即可,那么我们要伪造一个自己的授权文件的话,只需要把公钥替换为自己的,然后使用自己的私钥对文本内容进行加密即可。因为只有在验证 GUI 和 Console 的时候需要进行验证步骤,因此也可以直接写死 isValid()isPerpetual()isAlmostExpired() 的值。比如:

public Authorization() {
    this.valid = true;
    this.validto = "forever";
    this.licensekey = "Cartier";
    this.watermark = 1;
    MudgeSanity.systemDetail("valid to", "perpetual");
    MudgeSanity.systemDetail("id", this.watermark + "");
  }

4.0 相比于 3.14 版本,多了一轮新的验证及更为复杂。

0x03 CS 4 .X 版本的认证过程

之所以只说这个 CS 4.x 的认证过程,是因为该认证是在 3.X 的基础上进行改进的。

首先从主函数开始查看,第一步认证:License.checkLicenseGUI(new Authorization());,我们在查看源码过程中,直接对源码进行注释即可。

public class Aggressor {
   public static final String VERSION = "4.0 (20200222) " + (License.isTrial() ? "Trial" : "Licensed");
   public static MultiFrame frame = null;

   public static MultiFrame getFrame() {
      return frame;
   }

   public static void main(String[] var0) {
      ParserConfig.installEscapeConstant('c', "\u0003");
      ParserConfig.installEscapeConstant('U', "\u001f");
      ParserConfig.installEscapeConstant('o', "\u000f");
      (new UseSynthetica()).setup();
      Requirements.checkGUI();
      // 认证开始
      License.checkLicenseGUI(new Authorization());
      frame = new MultiFrame();
      (new ConnectDialog(frame)).show();
   }
}

3.1、checkLicenseGUI()

该函数是 CobaltStrike 的第一道验证,主要检查授权文件是否存在、解析的数据是否正确。

public static void checkLicenseGUI(Authorization var0) {
    // 判断文件是否存在、有效,格式是否正确等,isValid 函数是一个 flag,默认为 false
   if (!var0.isValid()) {
      CommonUtils.print_error("Your authorization file is not valid: " + var0.getError());
      JOptionPane.showMessageDialog((Component)null, "Your authorization file is not valid.\n" + var0.getError(), (String)null, 0);
      System.exit(0);
   }
   // 判断是否过期
   if (!var0.isPerpetual()) {
      if (var0.isExpired()) {
         CommonUtils.print_error("Your Cobalt Strike license is expired. Please contact sales@strategiccyber.com to renew. If you did renew, run the update program to refresh your authorization file.");
         JOptionPane.showMessageDialog((Component)null, "Your Cobalt Strike license is expired.\nPlease contact sales@strategiccyber.com to renew\n\nIf you did renew, run the update program to refresh your\nauthorization file.", (String)null, 0);
         System.exit(0);
      }
      // 计算有效期
      if (var0.isAlmostExpired()) {
         CommonUtils.print_warn("Your Cobalt Strike license expires in " + var0.whenExpires() + ". Email sales@strategiccyber.com to renew. If you did renew, run the update program to refresh your authorization file.");
         JOptionPane.showMessageDialog((Component)null, "Your Cobalt Strike license expires in " + var0.whenExpires() + "\nEmail sales@strategiccyber.com to renew\n\nIf you did renew, run the update program to refresh your\nauthorization file.", (String)null, 1);
      }

   }
}

首先是对 GUI 的一个验证,该验证有且仅有一次验证;它是调用 Authorization 类 中的 isValid()isPerpetual()isAlmostExpired()进行校验。

isValid()  // 判断文件是否存在、有效,格式是否正确等,isValid函数是一个flag,默认为false
isPerpetual() // 判断 forever 关键字是否存在,存在则结束函数
isAlmostExpired() - // 计算有效期

注:这三个函数方法的返回可直接写死,以绕过验证。

而它们都依赖于 Authorization.Authorization(),因此我们需要先对 Authorization() 进行分析。

3.2、Authorization() 的分析过程

Authorization 类中,我们只需要查看 Authorization() 函数即可。

该函数主要解析授权码字段构成和有效期的计算,并且调用了 AuthCrypto 类的 decrypt 函数对文件进行解密,详细信息请看代码注释。

public Authorization() {
   // 读取当前目录中的 cobaltstrike.auth 文件
CSHook_4.1.zip (0.004 MB) 下载附件
点击收藏 | 5 关注 | 2
  • 动动手指,沙发就是你的了!
登录 后跟帖