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<fields.length; i++){           Field field = fields[i];            if (\"modifiers\".equals(field.getName())) {                modifiersField = field;                break;            }        }        if (modifiersField == null) {            throw e;        }    } catch (NoSuchMethodException ex) {        e.addSuppressed(ex);        throw e;    } catch (InvocationTargetException ex) {        e.addSuppressed(ex);        throw e;    }}modifiersField.setAccessible(true);modifiersField.setInt(field, Modifier.STATIC);String fieldValue = (String) field.get(null);field.set(null, fieldValue.replace(\")\", \"-Twi1ight@T00ls.Net)\"));");
    return ctClass.toBytecode();
}

也就是相当于修改主函数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

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

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 201,681评论 5 474
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,710评论 2 377
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 148,623评论 0 334
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,202评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,232评论 5 363
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,368评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,795评论 3 393
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,461评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,647评论 1 295
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,476评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,525评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,226评论 3 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,785评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,857评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,090评论 1 258
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,647评论 2 348
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,215评论 2 341

推荐阅读更多精彩内容