白帽子
STATEMENT
声明
由于传播、利用此文所提供的信息而造成的任何直接或者间接的后果及损失,均由使用者本人负责,雷神众测及文章作者不为此承担任何责任。
雷神众测拥有对此文章的修改和解释权。如欲转载或传播此文章,必须保证此文章的完整性,包括版权声明等全部内容。未经雷神众测允许,不得任意修改或者增减此文章内容,不得以任何方式将其用于商业目的。
基础环境搭建
用到的cobaltstrike原版本是4.4,可以去找下原包,4.4的sha256为:
7af9c759ac78da920395debb443b9007fdf51fa66a48f0fbdaafb30b00a8a858
https://verify.cobaltstrike.com/
为什么是4.4而不是4.5或者更新的版本?
更新的可能有些暗桩并没有发现,4.5前一段还有一个暗桩被公开。当然如果4.7流出来的话,可以优先二开4.7,因为原生支持socks5代理。
java环境,有jdk就行,个人推荐还是oracle jdk8
oracle官方下载地址:https://www.oracle.com/java/technologies/downloads/# java8
反编译与调试编译
反编译
公开的源码包:https://www.ddosi.org/cs-4-4/
用之前最后看一下,当然也可以自己反编译
反编译的话推荐用Luyten:https://github.com/deathmarine/Luyten
结合已经公开的源码包看
调试编译
模块,将原版jar包导入作为依赖
工件,可以理解成编译输出jar包
Client端
-XX:+AggressiveHeap -XX:+UseParallelGC
TeamServer端
-XX:ParallelGCThreads=4 -server -XX:+AggressiveHeap -XX:+UseParallelGC -Dfile.encoding=UTF-8
先弹个helloword编译个试试,界面在aggressor/Aggressor.java,导入类即可之后构建工件。
JOptionPane.showMessageDialog(null, "Hello world");
成功弹出"Hello world",之后就可以删掉这个弹窗的代码,开始二开CS之旅了。
整个授权
直接添加硬编码密钥的方法,修改common/Authorization.java里面的Authorization函数,把没有的直接注释掉,当然,删了也可以。
public Authorization() {
this.watermark = 0;
this.validto = "";
this.error = null;
this.valid = false;
/*
String s = CommonUtils.canonicalize("cobaltstrike.auth");
if (!new File(s).exists()) {
try {
File parentFile = new File(this.getClass().getProtectionDomain().getCodeSource().getLocation().toURI());
if (parentFile.getName().toLowerCase().endsWith(".jar")) {
parentFile = parentFile.getParentFile();
}
s = new File(parentFile, "cobaltstrike.auth").getAbsolutePath();
}
catch (Exception ex) {
MudgeSanity.logException("trouble locating auth file", ex, false);
}
}
final byte[] file = CommonUtils.readFile(s);
if (file.length == 0) {
this.error = "Could not read " + s;
return;
}
final AuthCrypto authCrypto = new AuthCrypto();
final byte[] decrypt = authCrypto.decrypt(file);
*/
final AuthCrypto authCrypto = new AuthCrypto();
final byte[] decrypt = {1, -55, -61, 127, //证书时间限制29999999(永久)
0, 0, 0, 1, //watermark(水印)
44, //版本
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,
16, 94, -104, 25, 74, 1, -58, -76, -113, -91, -126, -90, -87, -4, -69, -110, -42};
if (decrypt.length == 0) {
this.error = authCrypto.error();
return;
}
暗桩1
common/Stater.java的initializeStarter函数,直接注释掉,结束下一个。
暗桩2
common/Stater2.java的initializeStarter函数,直接注释掉,结束下一个
暗桩3
common/Helper.java的startHelper函数,将中间大量计算的注释掉,返回值为真即可
暗桩4
beacon/BeaconData.java里的shouldPad函数,this.shouldPad值改成false就ok
暗桩5
beacon/CommandBuilder.java,红线之下,全部删掉,这个暗桩属实是有点坑了(4个小时左右会再校验,校验不通过,功能失效)
修改默认生成配置文件名字
aggressor/Prefs.java里面设置了客户端保存在用户目录下的配置文件名称:.aggressor.prop,修改即可(防止踩蜜罐后导致服务端链接信息暴露)
修改checksum8算法
直接删掉checksum8算法计算,固定请求的url路径,common/CommonUtils.java请求的url
public static String MSFURI(int var0) {
return "weui.min.js";
}
public static String MSFURI_X64() {
return "weui.js";
}
cloudstrike/WebServer.java,函数对应MSFURI、MSFURI_X64
public static long checksum8(String text) {
if (text.length() < 4) {
return 0L;
} else {
text = text.replace("/", "");
long sum = 0L;
for(int x = 0; x < text.length(); ++x) {
sum += (long)text.charAt(x);
}
//return sum % 256L;
return sum;
}
}
public static boolean isStager(String uri) {
return checksum8(uri) == 1079L; //weui.min.js的长度
}
public static boolean isStagerX64(String uri) {
return checksum8(uri) == 709L;//weui.js的长度
}
checksum8辅助算法计算脚本,可以在https://c.runoob.com/compile/10/ ,直接运行计算
public class EchoTest {
public static long checksum8(String text) {
if (text.length() < 4) {
return 0L;
}
text = text.replace("/", "");
long sum = 0L;
for (int x = 0; x < text.length(); x++) {
sum += text.charAt(x);
}
return sum;
}
public static void main(String[] args) throws Exception {
System.out.println(checksum8("ds6f565sdf44s65d4f21dsf1.jpg"));
}
}
修复CVE-2022-23317漏洞
cloudstrike/WebServer.java的_serve函数添加上个url的判断
if (!uri.startsWith("/")) {
return this.processResponse(uri, method, header, param, false, null, new Response("400 Bad Request", "text/plain", ""));
}
修改xor密钥
beacon/BeaconPayload.java的beacon_obfuscate函数,将46修改成想要的数字,现在的46对应的是2e,比如66对应42进制,可以使用转换进制https://tool.oschina.net/hexconvert/
对应的需要修改dll里的xor密钥,先解密->修改->加密->替换。
需要修改的dll文件有
解密可以使用这个项目https://github.com/ca3tie1/CrackSleeve ,下面贴一下适用于4.4的解密代码
import common.*;
import dns.SleeveSecurity;
import java.io.*;
import java.util.Enumeration;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
public class CrackSleeve {
private static byte[] OriginKey40 = {27, -27, -66, 82, -58, 37, 92, 51, 85, -114, -118, 28, -74, 103, -53, 6 };
private static byte[] OriginKey41 = {-128, -29, 42, 116, 32, 96, -72, -124, 65, -101, -96, -63, 113, -55, -86, 118 };
private static byte[] OriginKey42 = {-78, 13, 72, 122, -35, -44, 113, 52, 24, -14, -43, -93, -82, 2, -89, -96};
private static byte[] OriginKey43 = {58, 68, 37, 73, 15, 56, -102, -18, -61, 18, -67, -41, 88, -83, 43, -103};
private static byte[] OriginKey44 = {94, -104, 25, 74, 1, -58, -76, -113, -91, -126, -90, -87, -4, -69, -110, -42};
private static byte[] CustomizeKey = null;
private String DecDir = "sleeve";
private String EncDir = "Encode/sleeve";
public static void main(String[] args) throws IOException {
if (args.length == 0 || args[0].equals("-h") || args[0].equals("--help")) {
System.out.println("UseAge: CrackSleeve OPTION [key]");
System.out.println("Options:");
System.out.println("\tdecode\t\tDecode sleeve files");
System.out.println("\tencode\t\tEncode sleeve files");
System.out.println("\tkey\t\tCustomize key string for encode sleeve files");
System.exit(0);
}
String option = args[0];
// if (option.toLowerCase().equals("encode"))
// {
// if (args.length <= 1){
// System.out.println("[-] Please enter key.");
// System.exit(0);
// }
// String CustomizeKeyStr = args[1];
// if (CustomizeKeyStr.length() < 16)
// {
// System.out.println("[-] key length must be 16.");
// System.exit(0);
// }
// System.out.println("Init Key: "+CustomizeKeyStr.substring(0,16));
// CustomizeKey = CustomizeKeyStr.substring(0,16).getBytes();
// }
CrackSleeve Cracker = new CrackSleeve();
// 使用正版key初始化SleeveSecurity中的key
if (option.equals("decode")){
CrackSleevedResource.Setup(OriginKey44);
Cracker.DecodeFile();
}else if (option.equals("encode")){
//CrackSleevedResource.Setup(CustomizeKey);
CrackSleevedResource.Setup(OriginKey44);
Cracker.EncodeFile();
}
}
private void DecodeFile() throws IOException {
// 文件保存目录
File saveDir = new File(this.DecDir);
if (!saveDir.isDirectory())
saveDir.mkdirs();
// 获取jar文件中sleeve文件夹下的文件列表
try {
String path = this.getClass().getClassLoader().getResource("sleeve").getPath();
String jarPath = path.substring(5,path.indexOf("!/"));
Enumeration<JarEntry> jarEnum = new JarFile(new File(jarPath)).entries();
while (jarEnum.hasMoreElements())
{
JarEntry Element = jarEnum.nextElement();
String FileName = Element.getName();
if (FileName.indexOf("sleeve")>=0 && !FileName.equals("sleeve/")) {
System.out.print("[+] Decoding "+FileName+"......");
byte[] decBytes = CrackSleevedResource.DecodeResource(FileName);
if (decBytes.length > 0) {
System.out.println("Done.");
CommonUtils.writeToFile(new File(saveDir,"../"+FileName),decBytes);
}
else
System.out.println("Fail.");
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
private void EncodeFile(){
// 文件保存目录
File saveDir = new File(this.EncDir);
if (!saveDir.isDirectory())
saveDir.mkdirs();
// 获取解密文件列表
File decDir = new File(this.DecDir);
File[] decFiles = decDir.listFiles();
if (decFiles.length == 0) {
System.out.println("[-] There's no file to encode, please decode first.");
System.exit(0);
}
for (File file : decFiles){
String filename = decDir.getPath()+"/"+file.getName();
System.out.print("[+] Encoding " + file.getName() + "......");
byte[] encBytes = CrackSleevedResource.EncodeResource(filename);
if (encBytes.length > 0) {
System.out.println("Done.");
CommonUtils.writeToFile(new File(saveDir,file.getName()),encBytes);
}
else
System.out.println("Fail.");
}
}
}
class CrackSleevedResource{
private static CrackSleevedResource singleton;
private SleeveSecurity data = new SleeveSecurity();
public static void Setup(byte[] paramArrayOfbyte) {
singleton = new CrackSleevedResource(paramArrayOfbyte);
//singleton = new CrackSleevedResource(CommonUtils.readResource("resources/cobaltstrike.auth"));
}
public static byte[] DecodeResource(String paramString) {
return singleton._DecodeResource(paramString);
}
public static byte[] EncodeResource(String paramString) {
return singleton._EncodeResource(paramString);
}
private CrackSleevedResource(byte[] paramArrayOfbyte) {
this.data.registerKey(paramArrayOfbyte);
}
private byte[] _DecodeResource(String paramString) {
byte[] arrayOfByte1 = CommonUtils.readResource(paramString);
if (arrayOfByte1.length > 0) {
long l = System.currentTimeMillis();
return this.data.decrypt(arrayOfByte1);
}
byte[] arrayOfByte2 = CommonUtils.readResource(paramString);
if (arrayOfByte2.length == 0) {
CommonUtils.print_error("Could not find sleeved resource: " + paramString + " [ERROR]");
} else {
CommonUtils.print_stat("Used internal resource: " + paramString);
}
return arrayOfByte2;
}
private byte[] _EncodeResource(String paramString){
try {
File fileResource = new File(paramString);
InputStream fileStream = new FileInputStream(fileResource);
if (fileStream != null)
{
byte[] fileBytes = CommonUtils.readAll(fileStream);
if (fileBytes.length > 0)
{
byte[] fileEncBytes = this.data.encrypt(fileBytes);
return fileEncBytes;
}
}
} catch (FileNotFoundException e) {
e.printStackTrace();
}
return null;
}
}
把原本的jar包中的sleeve解压出来,准备解密
javac -encoding UTF-8 -classpath cobaltstrike.jar CrackSleeve.java
java -classpath cobaltstrike.jar:. CrackSleeve decode
把解密出来的dll拉到ida里面,定位0x2e的位置,修改即可
修改完成之后,再保存输出即可,最好创建一个备份,然后ok。其他的dll和这个修改的方式一样。
未完待续...
参考
https://github.com/emo-cat/ysoserial_rs
https://mp.weixin.qq.com/s/do88_4Td1CSeKLmFqhGCuQ
安恒信息
✦
杭州亚运会网络安全服务官方合作伙伴
成都大运会网络信息安全类官方赞助商
武汉军运会、北京一带一路峰会
青岛上合峰会、上海进博会
厦门金砖峰会、G20杭州峰会
支撑单位北京奥运会等近百场国家级
重大活动网络安保支撑单位
END
长按识别二维码关注我们