CS 本身 修改特征:watermark水印、Stager Url算法(checksum8)、Beacon Config密钥。
0x01 反编译JAR包 1、用IEDA中的 java-decompiler.jar(需要JDK 11+才可运行)来进行反编译CS。 jar包位于:plugins\java-decompiler\lib Windows 下,即:C:\Program Files\JetBrains\IntelliJ IDEA 2020.3.2\plugins\java-decompiler\lib
2、原版CS 4.4 jarhttps://www.ddosi.org/cobaltstrike-4-4-csagent/ https://github.com/trewisscotch/CobaltStr4.4/
目录结构:
1 2 3 4 5 D:\JAVAPROJECT\CS-CMPILATION │ java-decompiler.jar ├─cs_bin # 原cs jar 目录 │ cobaltstrike.jar └─cs_src # 反编译输出目录
运行反编译命令
1 java -cp java-decompiler.jar org.jetbrains.java.decompiler.main.decompiler.ConsoleDecompiler -dsg=true .\cs_bin\cobaltstrike.jar .\cs_src
反编译完成后得到cs的java源码(cs_src\cobaltstrike.jar 是个zip需要解压。)
0x02 环境搭建 主要参考:
https://pingmaoer.github.io/2020/06/24/CobaltStrike二次开发环境准备/
新建 IDEA 项目 新建项目JDK选 1.8 ,选择低版本jdk是为了向上兼容。 项目创建后,在根目录新建俩文件夹:decompiled_src
和 lib
。decompiled_src 放反编译得到的所有源码文件,lib放原版cs jar包。
设置项目结构 IDEA左上角 文件 > 项目结构 > 模块 > 依赖 添加原版 cobaltstrike.jar
打勾启用
设置入口 ,项目结构 > 工件 > JAR > 来自具有依赖项的模块…
Main-Class :aggressor.Aggressor
测试:src下新建软件包:aggressor,然后转到反编译完的aggressor中Aggressor.java,按F5,或者右键 重构 > 复制文件 至 aggressor ,后续修改其他模块代码也是如此操作 。
编辑 Aggressor.java 添加弹窗测试代码如:JOptionPane.showMessageDialog(null,"hello CS!!!");
生成jar包,菜单,构建 > 构建工件,会在 out/artifacts/xx/ 下生成jar包。
添加运行/调试配置 jar路径:上面生成的jar包。虚拟机选项写 [-XX:+AggressiveHeap -XX:+UseParallelGC]
运行,报错了,找不到授权文件…
下载 https://github.com/Twi1ight/CSAgent 解压复制到 out/artifacts/xx/ 下,跟构建出来的jar同级。
编辑 配置选项,追加虚拟机选项:
1 -javaagent:D:\JavaProject\ChangeCS\out\artifacts\ChangeCS_jar\CSAgent.jar=5e98194a01c6b48fa582a6a9fcbb92d6 -Duser.language=en
不出意外运行后就结束退出了。。。
这是因为触发了暗桩 :去除暗桩
0x03 去除暗桩 通过IDEA复制 beacon/BeaconData.java 到项目src下,将shouldPad方法的值改为false
注释 common/Helper.java 31~51行。
common/Starter.java 11~13行。
common/Starter2.java 12~14行。
重新构建,即可正常运行,弹出hello CS,确定后出现CS登录页面。
0x04 去除水印 修改 common/ListenerConfig.java ,注释掉49行,添加 var3.append((char)CommonUtils.rand(255));
0x05 修改stager checksum8 脚本
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 public class EchoSum { 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("x86: " + checksum8("f796xxx.js" )); System.out.println("x64: " + checksum8("dd83xxx.js" )); } }
https://www.tutorialspoint.com/compile_java_online.php
运行得到:1270、1396
修改 cloudstrike/webserver.java 175、179、183行,替换,以及删除183行的 && uri.matches(“/[A-Za-z0-9]{4}”)
添加个判断,防止不规范路径泄露stage以及修复log4j2漏洞:
1 2 3 if (!uri.startsWith("/" )) { return this .processResponse(uri, method, header, param, false , (WebService)null , new Response ("400 Bad Request" , "text/plain" , "" )); }
修改 common/CommonUtils.java
1385行 return var2.toString(); 为 return “f796xxx.js;”, 1400行 return var1; 为 return dd83xxx.js;
重新构建jar包会报错,删除报错变量:int len$;、String[] 、WebService service; 即可。
0x06 修改服务登录接口 修改 teamserver登录接口,防爆破。
在当前项目src新建软件包ssl ,然后 通过IDEA 复制反编译出来的 ssl/SecureServerSocket.java 和 ssl/SecureSocket.java 到当前项目src/ssl下,修改 48879 为其他数字值。
0x07 解密sleeve资源 用jas502大佬的 cs_file_decrypt 解密 sleeve下的dll资源文件。
需要注意的是 CrackSleeve.java 密钥是其他版本CS的,需要修改为 当前版本的。
cobaltstrike4.4破解方式
1 94 , -104 , 25 , 74 , 1 , -58 , -76 , -113 , -91 , -126 , -90 , -87 , -4 , -69 , -110 , -42
完整 CrackSleeve.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 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 [] OriginKey = {94 , -104 , 25 , 74 , 1 , -58 , -76 , -113 , -91 , -126 , -90 , -87 , -4 , -69 , -110 , -42 }; private static byte [] CustomizeKey = null ; private String DecDir = "cs44Resource/Decode/sleeve" ; private String EncDir = "cs44Resource/Encode/sleeve" ; public static String bytesToHex (byte [] data) { char [] hex = new char [32 ]; for (int i = 0 ; i < 16 ; i++) { int di = data[i]; hex[i << 1 ] = Character.forDigit((di >> 4 ) & 15 , 16 ); hex[(i << 1 ) + 1 ] = Character.forDigit(di & 15 , 16 ); } return new String (hex); } public static byte [] hex2bytes(String var0) { int var1 = var0.length(); byte [] var2 = new byte [var1 / 2 ]; for (int var3 = 0 ; var3 < var1; var3 += 2 ) { var2[var3 / 2 ] = (byte ) ((Character.digit(var0.charAt(var3), 16 ) << 4 ) + Character.digit(var0.charAt(var3 + 1 ), 16 )); } return var2; } public static byte [] Generate_Key() { java.security.SecureRandom random = new java .security.SecureRandom(); byte [] PrivateBytes = new byte [16 ]; random.nextBytes(PrivateBytes); String Random_Keys = java.util.Arrays.toString(PrivateBytes); System.out.println("[-] Example: \n[*] Random_Keys= " + Random_Keys); return PrivateBytes; } 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." ); byte [] Customize_key = Generate_Key(); System.out.println("[*] Random_Keys Hash =>> " + bytesToHex(Customize_key)); System.out.printf("[*] $ java -cp cobaltstrike.jar:. CrackSleeve encode %s \n" , bytesToHex(Customize_key) +"\n" ); 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 , 32 )); CustomizeKey = hex2bytes(CustomizeKeyStr.substring(0 , 32 )); } CrackSleeve Cracker = new CrackSleeve (); if (option.equals("decode" )) { CrackSleevedResource.Setup(OriginKey); Cracker.DecodeFile(); } else if (option.equals("encode" )) { CrackSleevedResource.Setup(CustomizeKey); Cracker.EncodeFile(); } } private void DecodeFile () throws IOException { File saveDir = new File (this .DecDir); if (!saveDir.isDirectory()) { saveDir.mkdirs(); } 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); } 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 ; } }
CrackSleeve.java 与原版cs jar放同一目录,执行如下命令:
1 2 javac -encoding UTF-8 -classpath cobaltstrike.jar CrackSleeve.java java -classpath cobaltstrike.jar;./ CrackSleeve decode
0x08 修改默认密钥 BeaconPayload 修改 beacon/BeaconPayload.java 方法 beacon_obfuscate() 中的 46,即 0x2E为其他数字十六进制形式。
修改DLL密钥 需要修改的dll文件:
1 2 3 4 5 6 7 8 beacon.dll beacon.x64.dll dnsb.dll dnsb.x64.dll pivot.dll pivot.x64.dll extc2.dll extc2.x64.dll
IDA分别打开上述文件,搜索0x2e是否为异或行为(xor),出现则Patch,Alt+I 全局搜索0x2e 进行替换为 BeaconPayload.java 中新的值。注意,32位的dll用32位IDA,64反之一样。
然后,工具栏 Edit–Patch Program–Change byte
修改之后 点击 Edit–Patch Program—Apply Patches to input file ,关闭文件,继续修改下一dll文件个。
0x09 去除BeaconEye特征 用32位IDA打开 beacon.dll,点Hex View-1视图, 按G 跳转到地址:1000A0B9 ,F2或者右键Edit…
6A 00
修改为6A 08
,00可修改其他值。
修改之后 点击 Edit–Patch Program–Apply Patches to input file ,关闭文件。
用64位IDA 打开 beacon.x64.dll,按G跳转到地址:000000018001879B
,右键 Text View
然后IDA左上角 Edit----Patch Program—Assemble 修改指令 xor edx, edx 为 mov edx, esi
Edit–Patch Program–Apply Patches to input file
重新加密DLL 修改完dll后重新加密。
1 java -classpath cobaltstrike.jar;./ CrackSleeve encode 5e98194a01c6b48fa582a6a9fcbb92d6
cs44Resource\Encode\sleeve
下 会生成重新加密后的DLL。
重新构建CS CS项目下新建文件夹 resources,并把其设置为资源类型。并且把 cs44Resource\Encode\sleeve 整个文件夹复制进去:
把CS原来 MANIFEST.MF 中的内容全部复制到项目 src\META0INF\MANIFEST.MF,替换。
构建->构建工件->重新构建,生成新cs jar包。
EvilEye、BeaconEye检测结果如下:
未修改前
修改后
辅助 0x01 端口转发 主要还是Nginx/socat 中转,如下图(来自某大佬博客)。详情待实操补充。
0x02 云函数 略
0x03 JA3/S/JARM 找到篇文章:
https://www.bc-security.org/post/ja3-s-signatures-and-how-to-avoid-them/
https://www.vectra.ai/blogpost/c2-evasion-techniques
好似可以通过修改 TLS 版本,能起到一定的"混淆"作用?
参考 java 文档 :https://java.com/en/configure_crypto.html
禁用 TLS 选项:jdk.tls.disabledAlgorithms
编辑文件 conf/security/java.securitylib/security/java.security 添加或者删除某些tl版本
如windows上java 11 C:\Program Files\Java\jdk-11.0.13\conf\security\java.security
可以把SSLv3、TLSv1、1.1删除,或添加 TLS 1.3。又或者用高版本(11、14、16)java运行 cs服务端。
Reference
https://wbglil.gitbook.io/cobalt-strike/ https://lengjibo.github.io/CobaltStrikeCode/ https://bewhale.github.io/posts/50202.html https://www.bilibili.com/read/cv14238932 https://www.anquanke.com/post/id/265090 https://hosch3n.github.io/2020/12/16/检测与隐藏Cobaltstrike服务器/ https://maka8ka.cc/post/cobaltstrike-4-3-破解-修复暗桩/ https://ucasers.cn/对cobaltstrike4.4的简单魔改/