CS stage隐匿与汉化

写在前面

继我们前面铺垫的章节,我们继续改造我们的Cobaltstrike,本章我们对Beacon Stage进行特征掩藏,以更好的保护我们的C2服务器。同时目前已有成熟的汉化方案开源,我们纳入改造以适应我们的项目。

http://cn-sec.com/archives/300922.html Cobaltstrike4.x基础特征修改之从端口到checksum8

Beacon Stage:

Beacon Staging Server 就是分阶段模式中,提供shellcode等功能扩展存储的Stage服务器。
Beacon Staging Server的作用是为了防止Payload过大或者适应不同的攻击场景,可以分阶段进行payload投递。
首先通过投递一个被称为stager的小巧的payload,然后去Beacon staging server下载体积较大更复杂的stage,并且访问stage的URL通过checksum8进行校验。

由Windows Execute 模块生成的就是Stager。
stager上线逻辑:——>运行stager——>自动生成并访问符合checksum8校验的URI进行远程下载stage——>上线

stage隐匿的常规思路:

1.修改checkSum8算法,使得按照默认的checkSum8 URI无法下载stage文件。
2.修改内部beacon的解密密钥,使得NSE解析脚本无法解析下载到的stage的信息。
3.从防火墙上限制访问beacon的端口。
4.每次手动kill掉site管理中的stage和stage64

我们这里选择针对dll和源码的xorkey修改作为方案。

XOR KEY修改

现成脚本地址:https://github.com/ca3tie1/CrackSleeve/blob/master/CrackSleeve.java
脚本原理参考:https://mp.weixin.qq.com/s/Pneu8R0zoG0ONyFXF9VLpg

private static byte[] OriginKey = {58, 68, 37, 73, 15, 56, -102, -18, -61, 18, -67, -41, 88, -83, 43, -103};

带参运行:

或者构建运行:java -classpath MyCS.jar;./ mytools.CrackSleeve decode,即可以获得加密后的dll文件
关于可能遇到的报错问题,这里抄下师傅们的作业,如下是4.0和4.1版本的代码片段,来自零队,所以师傅对脚本源码进行了修改:

在4.3版本中的片段如下:

# common.SleevedResource line9
public static void Setup(byte[] var0) {
   singleton = new SleevedResource(var0);
}
# common.Authorization line65
SleevedResource.Setup(var15);

跟踪发现是从auth文件中读key所得,所以脚本维持原样即可。顺便吐槽下CS4.3为什么所有的变量名几乎都是var*,var1-var18应有尽有,故意而为之以降低代码可读性达到代码保护的效果???

先说下异或问题,已知异或key 3.x为0x69,4.x为0x2e

4.3中这里写的是十进制46 对应二进制即 0x2e,我们这里进行修改,十进制100对应十六进制64:

# beacon.BeaconPayload line3
public static byte[] beacon_obfuscate(byte[] var0) {
   byte[] var1 = new byte[var0.length];

   for(int var2 = 0; var2 < var0.length; ++var2) {
//         var1[var2] = (byte)(var0[var2] ^ 46);
      var1[var2] = (byte)(var0[var2] ^ 100);
   }

   return var1;
}

dll解密完成后,进入修改环节,在ida中打开,进行二进制检索,Search▶Sequence ofBytes或者alt+B,找到xor:

修改其中的hex

然后应用即可:

其它dll修改类似

beacon.dll
beacon.x64.dll
dnsb.dll
dnsb.x64.dll
pivot.dll
pivot.x64.dll
extc2.dll
extc2.x64.dll

师傅们的文章里都提到了上述dll,为防止4.3版本又在这里留了新的暗桩,这里用笨办法对下属非功能dll挨个审查了一般,未发现新的dll使用该xor,所以继续抄之前版本的作业即可,机制无改变,排雷结束。

这里略改了下上面提到的脚本的加密策略,不在输入新的key,使用原key:

        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 = OriginKey;
        }

直接带参运行即可对修改后的dll进行加密

将加密后的sleeve直接放到根目录即可,进行重构的时候会覆盖原项目,再说一句,记得点构建工件前点重建项目!!!
改动前:

改动后:

在教程最后,破解相关代码全部未删除一并封装到了最终的cobaltstrike.jar中,方便各位小伙伴学习

当然也是可以单独调用的:

java -classpath cobaltstrike.jar;./ mytools.CrackSleeve          //dll加解密工具,小改动encode,封装官方key不再传参
java -classpath cobaltstrike.jar;./ mytools.GetMd5               //内置公钥哈希校验
java -classpath cobaltstrike.jar;./ mytools.RSAKeyPairGenerator  //auth生成脚本,4.3官方key已封装

checkSum8算法修改

修改checkSum8算法,使得按照默认的checkSum8 URI无法下载stage文件。

checkSum8算法的修改方式与其优缺点:
1.修改checkSum8的92L与93L为非默认的值,从而加大连接获取难度。
缺点:最多进过256次破解,也能够导致stage被下载。
2.通过修改sum值算法,固定下载URI,这样只有指定的uri可以获取到下载连接
缺点:更换URI需要重新编译计算sum值。

Beacon Stager listener 去特征https://mp.weixin.qq.com/s/HibtLfikI_0ezcLVCRxqaA
关于CobaltStrike的Stager被扫问题https://mp.weixin.qq.com/s/0MPM3bysJJYr5jbRnES_Vg

其中修改默认值92L、93L更为简单

# cloudstrike.WebServer line166
public static boolean isStager(String uri) {
   return checksum8(uri) == 92L;
}

public static boolean isStagerX64(String uri) {
   return checksum8(uri) == 93L && uri.matches("/[A-Za-z0-9]{4}");
}

但优缺点大家仁者见仁智者见智,目前的团队内部版本暂未采用此方案。

汉化

https://github.com/Twi1ight/CSAgent

这个已经有师傅给出4.x通用的成熟方案了,借助java的预加载进行非侵入式篡改。

但Releases中加入了作者的个人banner的license修改,导致和我们的服务端不能适配,不过师傅贴心的提供了源码,且源码中并未有banner添加,所以在这里自己编译顺便进行小小的修改。
主要改动涉及以下,首先我们前面已经提到了,暗桩和认证部分我们已经自己处理好了,所以这些通通可以注释掉。

if (className == null) {
    return classfileBuffer;
    // } else if (className.equals("beacon/BeaconData")) {
    //     // 暗桩修复,修改zip包后,30分钟所有命令都会变成exit,非侵入式修改下其实不需要
    //     CtClass cls = classPool.makeClass(new ByteArrayInputStream(classfileBuffer));
    //     CtMethod mtd = cls.getDeclaredMethod("shouldPad");
    //     mtd.setBody("{$0.shouldPad = false;}");
    //     return cls.toBytecode();
} else if (className.equals("common/Authorization")) {
    // 设置破解key
    CtClass cls = classPool.makeClass(new ByteArrayInputStream(classfileBuffer));
    String func = "public static byte[] hex2bytes(String s) {" +
            "   int len = s.length();" +
            "   byte[] data = new byte[len / 2];" +
            "   for (int i = 0; i < len; i += 2) {" +
            "       data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4) + Character.digit(s.charAt(i+1), 16));" +
            "   }" +
            "   return data;" +
            "}";
    CtMethod hex2bytes = CtNewMethod.make(func, cls);
    cls.addMethod(hex2bytes);

    CtConstructor mtd = cls.getDeclaredConstructor(new CtClass[]{});
    mtd.setBody("{$0.watermark = 1234567890;" +
            "$0.validto = \"forever\";" +
            "$0.valid = true;" +
            "common.MudgeSanity.systemDetail(\"valid to\", \"perpetual\");" +
            "common.MudgeSanity.systemDetail(\"id\", String.valueOf($0.watermark));" +
            "common.SleevedResource.Setup(hex2bytes(\"" + hexkey + "\"));" +
            "}");
    return cls.toBytecode();
}

然后是对common.MudgeSanity的修改,主要是增加了systeminfo中的Loader内容

 else if (className.equals("common/MudgeSanity")) {
    CtClass cls = classPool.makeClass(new ByteArrayInputStream(classfileBuffer));
    CtMethod mtd = cls.getDeclaredMethod("systemInformation");
    mtd.instrument(
            new ExprEditor() {
                public void edit(MethodCall m)
                        throws CannotCompileException {
                    if (m.getClassName().equals("java.lang.StringBuffer")
                            && m.getMethodName().equals("append")) {
                        m.replace("{" +
                                "if ($1.startsWith(\"Version:\")) {" +
                                "   $1 += \"Loader: https://github.com/Twi1ight/CSAgent\\n\";" +
                                "}" +
                                "$_ = $proceed($$);" +
                                "}");
                    }
                }
            });
    return cls.toBytecode();

即这里,原版和csagent对比:

不过这里不影响license比对,可以不改动,但是如果用的Releases中打好的包,是无法登录c2的,因为那个修改是改的aggressor.Aggressor

if (className.equals("aggressor/Aggressor") || className.equals("server/TeamServer") || className.equals("aggressor/headless/Start")) {
    ctClass = this.classPool.makeClass(new ByteArrayInputStream(classfileBuffer));
    ctMethod = ctClass.getDeclaredMethod("main");
    this.classPool.importPackage("java.lang.reflect");
    ctMethod.insertBefore("Field field = aggressor.Aggressor.class.getDeclaredField(\"VERSION\");field.setAccessible(true);Field modifiersField = null;try {    modifiersField = Field.class.getDeclaredField(\"modifiers\");} catch (NoSuchFieldException e) {    try {        Method getDeclaredFields0 = Class.class.getDeclaredMethod(\"getDeclaredFields0\", new Class[]{boolean.class});        boolean accessibleBeforeSet = getDeclaredFields0.isAccessible();        getDeclaredFields0.setAccessible(true);        Field[] fields = (Field[]) getDeclaredFields0.invoke(Field.class, new Object[]{Boolean.FALSE});        getDeclaredFields0.setAccessible(accessibleBeforeSet);        for (int i=0; i

也就是相当于修改主函数aggressor/Aggressor中的VERSION 字段:

public class Aggressor {
   public static final String VERSION = "4.3 (20210317) " + (License.isTrial() ? "Trial" : "Licensed to WingsSec");
   public static final String VERSION_SHORT = "4.3";
   public static MultiFrame frame = null;
   ... ...
}

另外大家在使用csagent的时候还需要传参,这个参数传递如下:

最终出现在我们上面提到的破解部分,所以这块也是不需要的:

else if (className.equals("common/Authorization")) {
    // 设置破解key
    ... ...
    mtd.setBody("{$0.watermark = 1234567890;" +
            "$0.validto = \"forever\";" +
            "$0.valid = true;" +
            "common.MudgeSanity.systemDetail(\"valid to\", \"perpetual\");" +
            "common.MudgeSanity.systemDetail(\"id\", String.valueOf($0.watermark));" +
            "common.SleevedResource.Setup(hex2bytes(\"" + hexkey + "\"));" +
            "}");
    return cls.toBytecode();
}

最终修改如下:

public class PreMain {
    public static void premain(String agentArgs, Instrumentation inst) {
//        if (agentArgs == null) {
//            System.out.println("[CSAgent] Agent options not found!");
//            return;
//        }
        inst.addTransformer(new CobaltStrikeTransformer(), true);
    }

    static class CobaltStrikeTransformer implements ClassFileTransformer {
        private final ClassPool classPool = ClassPool.getDefault();
//        private final String hexkey;
        private final Boolean needTranslation;

        public CobaltStrikeTransformer() {
//            this.hexkey = args;
            this.needTranslation = Files.exists(Paths.get("resources/translation.txt"));
        }
        ... ...
}

总的来说,移除破解功能,保留汉化功能,这样一个好处就是可适用于任意破解版本的汉化。

那么最终客户端的使用为:

java -XX:ParallelGCThreads=4 -XX:+AggressiveHeap -XX:+UseParallelGC -javaagent:CSAgent.jar -jar cobaltstrike.jar

服务端正常使用即可,不需要额外修改,最终效果:

你可能感兴趣的:(CS stage隐匿与汉化)