|
原文链接:CobaltStrike4.X之License认证分析
声明
由于传播、利用此文所提供的信息而造成的任何直接或者间接的后果及损失,均由使用者本人负责,雷神众测及文章作者不为此承担任何责任。
雷神众测拥有对此文章的修改和解释权。如欲转载或传播此文章,必须保证此文章的完整性,包括版权声明等全部内容。未经雷神众测允许,不得任意修改或者增减此文章内容,不得以任何方式将其用于商业目的。
NO.1 前言
本文以cs4.3为例,进行License认证分析。
NO.2 证书认证流程
cobaltstrike.auth认证密钥文件,为rsa加密,解密内容格式类似:
- -54, -2, -64, -45, # 文件头
- 0, 77, # 后续长度
- 1, -55, -61, 127, # 证书时间限制29999999(永久)
- 0, 0, 0, 1, # watermark(水印)
- 43, # 版本限制
- 16, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20,
- 16, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20,
- 16, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20,
- 16, 58, 68, 37, 73, 15, 56, -102, -18, -61, 18, -67, -41, 88, -83, 43, -103
复制代码
aggressor/Aggressor.class中License.checkLicenseGUI(new Authorization());开始license认证: 
License.checkLicenseGUI中isValid、isPerpetual、isExpired、isAlmostExpired对授权是否有效、授权是否过期进行判断,判断的值来源Authorization类:
 
Authorization类中是cobaltstrike.auth文件的处理,读取文件内容,调用AuthCrypto().decrypt对内容进行处理:

AuthCrypto()中构造函数中调用load(),load()函数中对resources/authkey.pub进行md5判断,再获取了RSA的公钥:

decrypt()中调用_decrypt对cobaltstrike.auth文件内容用公钥进行RSA解密赋值给数组var2,再用DataParser做转换赋值给var3,readInt()方法获取var3的前四位进行文件头判断(-889274181为3.x版本;-889274157为4.x版本)。再从var3中readShort()获取两位作为长度赋值给var5,再var6 = var3.readBytes(var5)获取该长度内容赋值给var6并返回:
 
在Authorization类中得到的arrayOfByte2数组是去除前六位之后的内容,继续对arrayOfByte2数组进行处理,先获取四个数字赋值给i,再获取4个数字赋值给watermark,再获取一个数字赋值给b1,判断b1小于43,判断i是否等于29999999,在common/ListenerConfig中判断watermark为0时候会增加杀毒检测水印:
 

去除前6位后,再去除i、watermark、b1这9位,剩余的为4.0到4.3以来的key,为:16, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20结构:
- byte b2 = dataParser.readByte(); //获取1位,即16
- byte[] arrayOfByte3 = dataParser.readBytes(b2); //获取16位,为4.0的key
- byte b3 = dataParser.readByte(); //获取1位,即16
- byte[] arrayOfByte4 = dataParser.readBytes(b3); //获取16位,为4.1的key
- byte b4 = dataParser.readByte(); //获取1位,即16
- byte[] arrayOfByte5 = dataParser.readBytes(b4); //获取16位,为4.2的key
- byte b5 = dataParser.readByte(); //获取1位,即16
- byte[] arrayOfByte6 = dataParser.readBytes(b5); //获取16位,为4.3的key赋值给arrayOfByte6
复制代码
在Authorization类中调用SleevedResource.Setup方法对arrayOfByte6进行处理。在SleevedResource中把key设定为AES、HmacSHA256解密的秘钥,在_readResource中的this.data.decrypt(arrayOfByte1);进行解密调用,解密的内容为/sleeve/中的dll文件: 
在SleeveSecurity中设定AES、HmacSHA256解密的秘钥,使用传入的值计算一个长度为256的摘要,再取0-16作为AES的密钥,取16-32作为HmacSHA256的密钥

如果得不到对应的key,就无法对sleeve文件夹中的dll进行解密,连接服务端时候会提示[Sleeve] Bad HMAC的错误提示:

hmac解密部分可参考:Cobaltstrike 4破解之 我自己给我自己颁发license
(https://mp.weixin.qq.com/s/Pneu8R0zoG0ONyFXF9VLpg)
所以完成破解的关键是对应cs版本的key。
NO.3 解密方式
方法一:硬编码key
对AuthCrypto().decrypt进行RSA解密后赋值的参数写死:
 
中间4.0、4.1、4.2的key是没有用到的可以随便写,最后一组写4.3泄露出来的key。
方法二:自己生成证书
生成代码可参考:https://xz.aliyun.com/t/8557# toc-4
- import javax.crypto.BadPaddingException;
- import javax.crypto.Cipher;
- import javax.crypto.IllegalBlockSizeException;
- import javax.crypto.NoSuchPaddingException;
- import java.io.*;
- import java.security.*;
- public class RSAKeyPairGenerator {
- private PrivateKey privateKey;
- private PublicKey publicKey;
- public RSAKeyPairGenerator() throws NoSuchAlgorithmException {
- KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
- keyGen.initialize(2048);
- KeyPair pair = keyGen.generateKeyPair();
- this.privateKey = pair.getPrivate();
- this.publicKey = pair.getPublic();
- }
- // 将byte 写入文件
- public void byte2File(String path, byte[] data) throws IOException {
- File f = new File(path);
- f.getParentFile().mkdirs();
- FileOutputStream fos = new FileOutputStream(f);
- fos.write(data);
- fos.flush();
- fos.close();
- }
- public PrivateKey getPrivateKey() {
- return privateKey;
- }
- public PublicKey getPublicKey() {
- return publicKey;
- }
- // 加密数据
- public byte[] encryptPri(byte[] data, PrivateKey privateKey) throws BadPaddingException, IllegalBlockSizeException, InvalidKeyException, NoSuchPaddingException, NoSuchAlgorithmException {
- Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
- cipher.init(Cipher.ENCRYPT_MODE, this.privateKey);
- return cipher.doFinal(data);
- }
- public static void main(String[] args) throws NoSuchAlgorithmException, IOException, IllegalBlockSizeException, InvalidKeyException, NoSuchPaddingException, BadPaddingException {
- RSAKeyPairGenerator PairGenerator = new RSAKeyPairGenerator();
- //byte[] data = {-54, -2, -64, -45, 0, 77, 1, -55, -61, 127, 0, 0, 0, 1, 43, 16, 58, 68, 37, 73, 15, 56, -102, -18, -61, 18, -67, -41, 88, -83, 43, -103 };
- byte[] data ={-54, -2, -64, -45, 0, 77, 1, -55, -61, 127, 0, 0, 0, 1, 43,
- 16, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
- 16, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
- 16, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
- 16, 58, 68, 37, 73, 15, 56, -102, -18, -61, 18, -67, -41, 88, -83, 43, -103};
- byte[] rsaByte = PairGenerator.encryptPri(data, PairGenerator.getPrivateKey());
- PairGenerator.byte2File("RSA/cobaltstrike.auth", rsaByte);
- PairGenerator.byte2File("RSA/authkey.private", PairGenerator.getPrivateKey().getEncoded());
- PairGenerator.byte2File("RSA/authkey.pub", PairGenerator.getPublicKey().getEncoded());
- }
- }
复制代码
将生成的authkey.pub放入src/resources/,将生成的cobaltstrike.auth放到打包好的jar包同目录,修改common/AuthCrypto.load()函数中md5数为authkey.pub的值: 
Javaagent方式
Javaagent原理:
https://www.cnblogs.com/rickiyang/p/11368932.html
破解工具可参考:
https://github.com/Twi1ight/CSAgent
破解的核心还是需要cs对应版本的key
NO.4 去除暗桩
beacon/BeaconData中将shouldPad方法的值固定为false:
 
|
|