pl4tyz/CVE-2025-59059-Misattributed-RCE-in-Apache-Ranger-Static-Analysis-Correction
GitHub: pl4tyz/CVE-2025-59059-Misattributed-RCE-in-Apache-Ranger-Static-Analysis-Correction
针对 Apache Ranger CVE-2025-59059 的校正分析,揭示官方公告归因错误的类名和被高估的 CVSS 评分,帮助安全从业者准确理解漏洞实际影响范围。
Stars: 0 | Forks: 0
# CVE-2025-59059:Apache Ranger 中被错误归因的 RCE —— 修正说明
**CVE:** CVE-2025-59059
**受影响版本:** Apache Ranger <= 2.7.0
**修复版本:** Apache Ranger 2.8.0
**CVSS(官方):** 9.8 严重
**实际严重程度(论证):** ~6.0 中等 —— 见下文分析
## 前言
本文不涉及新的漏洞利用或可用的 PoC。目的是纠正关于 CVE-2025-59059 的公开披露中的错误,特别是两点:错误的类被指名为易受攻击组件,以及 CVSS 评分没有反映实际的利用限制。这里的所有分析都基于对 Apache Ranger 2.7.0 源代码的静态分析以及 2.8.0 版本的补丁差异。
## 官方公告的内容
官方公告写道:
这是错误的,或者至少具有误导性。`NashornScriptEngineCreator` 并不是易受攻击的类。如果非要说的话,它是代码库中两个与 Nashorn 相关的组件中被加固得更好的那个 —— 我们稍后会详细讨论。
## 背景:Apache Ranger 中的 Nashorn
Apache Ranger 使用 JavaScript 表达式来评估行级过滤策略。这些是决定特定用户是否有权访问数据集中特定记录的规则。为了在 JVM 上实际运行这些表达式,Ranger 嵌入了一个 JavaScript 引擎。在 <= 2.7.0 的版本中,该引擎是 Nashorn,即 Oracle 在 JDK 8 到 14 中内置的 JS 引擎。
公告完全遗漏了一件事 —— 这对确定影响范围非常重要 —— 即 `jdk.nashorn.api.scripting.NashornScriptEngineFactory` 根本不是 Ranger 源代码的一部分。它是 JDK 内置的。它包含在 JDK 8 中,在 JDK 11 中被弃用,并在 JDK 15 中被完全移除。因此,只有在 JDK 8 到 14 上运行 Ranger 时,此漏洞才可被触及。任何在 JDK 15+ 上的版本都不受影响,因为 Nashorn 在那里根本不存在,而且 `ScriptEngineUtil` 会静默回退到 GraalJS 或 `JavaScriptEngineCreator`。
Ranger 中有两个类使用 Nashorn。它们对它的处理方式截然不同。
## 两个与 Nashorn 相关的类
### 1. `NashornScriptEngineCreator` —— 被点名但更安全的那个
位于:
```
agents-common/src/main/java/org/apache/ranger/plugin/util/NashornScriptEngineCreator.java
```
```
public class NashornScriptEngineCreator implements ScriptEngineCreator {
private static final String[] SCRIPT_ENGINE_ARGS = new String[] {
"--no-java", "--no-syntax-extensions"
};
@Override
public ScriptEngine getScriptEngine(ClassLoader clsLoader) {
NashornScriptEngineFactory factory = new NashornScriptEngineFactory();
ret = factory.getScriptEngine(SCRIPT_ENGINE_ARGS, clsLoader, RangerClassFilter.INSTANCE);
...
}
private static class RangerClassFilter implements ClassFilter {
@Override
public boolean exposeToScripts(String className) {
LOG.warn("script blocked: attempt to use Java class {}", className);
return false;
}
}
}
```
这个类实际上做了三件事来加固引擎:
- `--no-java` —— 切断脚本内部对 `java.*` 命名空间的直接访问
- `--no-syntax-extensions` —— 禁用非标准的 Nashorn 语法
- `RangerClassFilter` —— 在 ClassFilter 级别阻止所有 Java 类访问,对所有内容返回 false
它绝对安全吗?不。通过反射链和 `java.lang.invoke` 技巧可以绕过 Nashorn ClassFilter。但它比我们在另一个类中发现的要明显更受限制。
### 2. `RecordFilterJavaScript` 实际易受攻击的类
位于:
```
plugin-nestedstructure/src/main/java/org/apache/ranger/authorization/nestedstructure/authorizer/RecordFilterJavaScript.java
```
```
public class RecordFilterJavaScript {
static class SecurityFilter implements ClassFilter {
@Override
public boolean exposeToScripts(String s) {
return false;
}
boolean containsMalware(String filterExpr) {
// only checks for this one specific string
return filterExpr.contains("this.engine");
}
}
public static boolean filterRow(String user, String filterExpr, String jsonString) {
SecurityFilter securityFilter = new SecurityFilter();
if (securityFilter.containsMalware(filterExpr)) {
throw new MaskingException("cannot process filter expression due to security concern...");
}
// instantiates Nashorn directly — no --no-java, no --no-syntax-extensions
NashornScriptEngineFactory factory = new NashornScriptEngineFactory();
ScriptEngine engine = factory.getScriptEngine(securityFilter);
String script = " jsonAttr = JSON.parse(jsonString); " + filterExpr;
Bindings bindings = engine.createBindings();
bindings.put("jsonString", jsonString);
bindings.put("user", user);
boolean hasAccess = (boolean) engine.eval(script, bindings);
...
}
}
```
将其与 `NashornScriptEngineCreator` 进行比较,区别显而易见:
- 没有 `--no-java` 标志,`java.*` 命名空间对脚本完全开放
- 没有 `--no-syntax-extensions`
- 唯一的“安全措施”是一个字符串检查:`filterExpr.contains("this.engine")`
- `filterExpr` 被直接拼接到 Nashorn 评估的脚本中
因为没有传递 `--no-java`,`java.*` 命名空间是完全可访问的。你根本不需要任何绕过技术:
```
var runtime = java.lang.Runtime.getRuntime();
runtime.exec("id");
```
那里根本没有 `this.engine`。黑名单与此攻击路径完全无关。
有趣的是,开发人员显然知道至少一种绕过模式。测试文件 `TestRecordFilterJavaScript.java` 中有这样的内容:
```
RecordFilterJavaScript.filterRow("user",
"this.engine.factory.scriptEngine.eval('java.lang.Runtime.getRuntime().exec(\"/Applications/Spotify.app/Contents/MacOS/Spotify\")')",
...);
```
该测试的存在是为了确认 `this.engine` 被阻止。未被阻止的是直接的 `java.*` 路径,这根本不需要 `this.engine`。
## 入口点
`RecordFilterJavaScript.filterRow()` 仅从一个地方被调用:
```
plugin-nestedstructure/src/main/java/org/apache/ranger/authorization/nestedstructure/authorizer/NestedStructureAuthorizer.java
```
```
private boolean hasAccessToRecord(String schema, String user, ..., String jsonString, ...) {
RangerAccessResult result = plugin.evalRowFilterPolicies(request, null);
if (result.isRowFilterEnabled()) {
String filterExpr = result.getFilterExpr();
ret = RecordFilterJavaScript.filterRow(user, filterExpr, jsonString);
}
return ret;
}
```
`filterExpr` 来自 `result.getFilterExpr()` —— 即存储在 Ranger 行过滤策略中的 JavaScript 表达式。拥有策略管理员权限的攻击者可以将该表达式设置为他们想要的任何内容。当任何用户访问受该策略管理的资源时,`hasAccessToRecord()` 会触发,从策略存储中提取 `filterExpr`,然后 `filterRow()` 将其交给 Nashorn,且基本没有沙箱保护。
**攻击链:**
```
Policy admin access
|
v
Create/modify a row filter policy on a nestedstructure resource
|
v
filterExpr set to: java.lang.Runtime.getRuntime().exec("...")
|
v
Any user accesses the resource → hasAccessToRecord() triggered
|
v
filterExpr flows into filterRow() → Nashorn evaluates it → RCE
```
## 补丁差异
该修复被追踪为 `RANGER-4076: Remove Nashorn Script Engine`,由 Kishor Gollapalliwar 于 2025 年 12 月 8 日提交。取自 Apache 邮件列表提交存档。三个文件被更改,27 行插入,81 行删除。
```
commit 923a8473de2985cd389d45062cd4717d5ca13235
Author: Kishor Gollapalliwar
AuthorDate: Mon Dec 8 14:55:56 2025 +0530
RANGER-4076: Remove Nashorn Script Engine
.../plugin/util/NashornScriptEngineCreator.java | 67 ----------------------
.../ranger/plugin/util/ScriptEngineUtil.java | 7 +--
.../authorizer/RecordFilterJavaScript.java | 34 ++++++++---
3 files changed, 27 insertions(+), 81 deletions(-)
```
### 文件 1:`NashornScriptEngineCreator.java` 被完全删除
```
diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/util/NashornScriptEngineCreator.java
deleted file mode 100644
index b890fe85d..000000000
--- a/agents-common/src/main/java/org/apache/ranger/plugin/util/NashornScriptEngineCreator.java
+++ /dev/null
@@ -1,67 +0,0 @@
-package org.apache.ranger.plugin.util;
-
-import jdk.nashorn.api.scripting.ClassFilter;
-import jdk.nashorn.api.scripting.NashornScriptEngineFactory;
-
-public class NashornScriptEngineCreator implements ScriptEngineCreator {
-
- private static final String[] SCRIPT_ENGINE_ARGS = new String[] {
- "--no-java", "--no-syntax-extensions"
- };
- private static final String ENGINE_NAME = "NashornScriptEngine";
-
- @Override
- public ScriptEngine getScriptEngine(ClassLoader clsLoader) {
- ScriptEngine ret = null;
- if (clsLoader == null) {
- clsLoader = getDefaultClassLoader();
- }
- try {
- NashornScriptEngineFactory factory = new NashornScriptEngineFactory();
- ret = factory.getScriptEngine(SCRIPT_ENGINE_ARGS, clsLoader, RangerClassFilter.INSTANCE);
- } catch (Throwable t) {
- LOG.debug("NashornScriptEngineCreator.getScriptEngine(): failed to create engine type {}", ENGINE_NAME, t);
- }
- return ret;
- }
-
- private static class RangerClassFilter implements ClassFilter {
- static final RangerClassFilter INSTANCE = new RangerClassFilter();
-
- @Override
- public boolean exposeToScripts(String className) {
- LOG.warn("script blocked: attempt to use Java class {}", className);
- return false;
- }
- }
-}
```
### 文件 2:`ScriptEngineUtil.java` Nashorn 从引擎创建器链中被移除
```
diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/util/ScriptEngineUtil.java
--- a/agents-common/src/main/java/org/apache/ranger/plugin/util/ScriptEngineUtil.java
+++ b/agents-common/src/main/java/org/apache/ranger/plugin/util/ScriptEngineUtil.java
@@ -28,10 +28,9 @@
public class ScriptEngineUtil {
- private static final String SCRIPT_ENGINE_CREATOR_NASHHORN =
- "org.apache.ranger.plugin.util.NashornScriptEngineCreator";
private static final String SCRIPT_ENGINE_CREATOR_GRAAL =
"org.apache.ranger.plugin.util.GraalScriptEngineCreator";
private static final String SCRIPT_ENGINE_CREATOR_JS =
"org.apache.ranger.plugin.util.JavaScriptEngineCreator";
- private static final String[] SCRIPT_ENGINE_CREATORS = new String[] {
- SCRIPT_ENGINE_CREATOR_NASHHORN,
- SCRIPT_ENGINE_CREATOR_GRAAL,
- SCRIPT_ENGINE_CREATOR_JS
- };
+ private static final String[] SCRIPT_ENGINE_CREATORS = new String[] {
+ SCRIPT_ENGINE_CREATOR_GRAAL,
+ SCRIPT_ENGINE_CREATOR_JS
+ };
@@ -108,9 +107,7 @@ private static void initScriptEngineCreator(String serviceType) {
} catch (Throwable t) {
boolean logWarn;
- if (creatorClsName.equals(SCRIPT_ENGINE_CREATOR_NASHHORN)) {
- logWarn = JVM_MAJOR_CLASS_VERSION < JVM_MAJOR_CLASS_VERSION_JDK15;
- } else if (creatorClsName.equals(SCRIPT_ENGINE_CREATOR_GRAAL)) {
+ if (creatorClsName.equals(SCRIPT_ENGINE_CREATOR_GRAAL)) {
logWarn = JVM_MAJOR_CLASS_VERSION >= JVM_MAJOR_CLASS_VERSION_JDK15;
} else {
logWarn = true;
```
### 文件 3:`RecordFilterJavaScript.java` 真正的修复
这是实际进行安全更改的地方。直接的 `NashornScriptEngineFactory` 调用被替换为 GraalJS,并且 `SecurityFilter` 完全失去了其 `ClassFilter` 角色 —— 降级为一个仅进行字符串检查的普通类。
```
diff --git a/plugin-nestedstructure/src/main/java/org/apache/ranger/authorization/nestedstructure/authorizer/RecordFilterJavaScript.java
--- a/plugin-nestedstructure/.../RecordFilterJavaScript.java
+++ b/plugin-nestedstructure/.../RecordFilterJavaScript.java
@@ -18,13 +18,16 @@
-import jdk.nashorn.api.scripting.ClassFilter;
-import jdk.nashorn.api.scripting.NashornScriptEngineFactory;
+import javax.script.ScriptContext;
+import javax.script.ScriptEngineManager;
+import java.util.HashMap;
+import java.util.Map;
@@ -54,8 +57,25 @@
if (securityFilter.containsMalware(filterExpr)) {
throw new MaskingException("cannot process filter expression...");
}
- NashornScriptEngineFactory factory = new NashornScriptEngineFactory();
- ScriptEngine engine = factory.getScriptEngine(securityFilter);
+ ClassLoader clsLoader = Thread.currentThread().getContextClassLoader();
+ ScriptEngineManager mgr = new ScriptEngineManager(clsLoader);
+ ScriptEngine engine = mgr.getEngineByName("graal.js");
+
+ if (engine != null) {
+ try {
+ Map graalVmConfigs = new HashMap<>();
+ graalVmConfigs.put("polyglot.js.allowHostAccess", Boolean.TRUE);
+ graalVmConfigs.put("polyglot.js.nashorn-compat", Boolean.TRUE);
+
+ Bindings bindings = engine.getBindings(ScriptContext.ENGINE_SCOPE);
+ bindings.putAll(graalVmConfigs);
+ engine.setBindings(bindings, ScriptContext.ENGINE_SCOPE);
+ } catch (Throwable t) {
+ logger.debug("RecordFilterJavaScript.filterRow(): failed to create engine type {}", "graal.js", t);
+ }
+ }
@@ -83,12 +103,8 @@
- static class SecurityFilter implements ClassFilter {
- @Override
- public boolean exposeToScripts(String s) {
- return false;
- }
-
+ static class SecurityFilter {
boolean containsMalware(String filterExpr) {
return filterExpr.contains("this.engine");
}
}
```
此差异中有几点值得指出:
- `SecurityFilter` 不再是 `ClassFilter` —— 失去了引擎级别的钩子,变成了仅剩字符串检查的普通类
- Nashorn 导入已消失,被替换为 `ScriptEngineManager`
- GraalJS 在 `nashorn-compat` 模式下运行,以向后兼容现有的策略表达式
- `polyglot.js.allowHostAccess` 被设置为 `true` —— 这是为了向后兼容而做的权衡,它依赖于 GraalJS 自己的沙箱而不是 ClassFilter。如果你关心修复质量,这一点值得关注
补丁清楚地表明 `RecordFilterJavaScript` 才是实际的安全目标。删除 `NashornScriptEngineCreator` 是清理工作,而不是修复本身。
## 为什么 CVE 点名了错误的类
老实说,不难看出这是怎么发生的。`NashornScriptEngineCreator` 就在 `agents-common` 中,它是整个代码库中名称最明显的 Nashorn 类,基本的 grep 搜索 Nashorn 就会立即找到它。另一方面,`RecordFilterJavaScript` 位于 `plugin-nestedstructure` 中,这是一个独立的子模块,而且类名中没有任何暗示 Nashorn 的内容。如果有人在没有实际跟踪代码路径的情况下进行了快速分类,他们很可能会停在 `NashornScriptEngineCreator` 上。
这里似乎就发生了这种情况。易受攻击的类和公告中点名的类是两码事。
## 为什么 CVSS 9.8 站不住脚
9.8 意味着网络可达、无需认证、无需用户交互。实际情况截然不同:
| 因素 | 现实情况 |
|--------|---------|
| 认证 | 需要 Ranger 中的策略管理员权限 |
| 插件要求 | `plugin-nestedstructure` 默认未启用 |
| JDK 限制 | 仅可在 JDK 8–14 上利用,JDK 15+ 不受影响 |
| 网络暴露 | FOFA 指纹识别返回了约 35 个公开暴露的 Ranger 实例 |
公告中没有反映任何这一点。要实际利用此漏洞,你需要策略管理员访问权限,这是 Ranger 部署中重要的权限级别。这不是未经认证的 RCE。考虑到认证要求和非默认插件,6.0–7.0 范围内的分数会是更诚实的评分。
## 总结
| | 公告声明 | 实际发现 |
|---|---|---|
| 易受攻击的类 | `NashornScriptEngineCreator` | `RecordFilterJavaScript` |
| 攻击类型 | 未经认证的 RCE | 经认证的 RCE(需要策略管理员权限) |
| 受影响的 JDK | 未指定 | 仅限 JDK 8–14 |
| 需要的插件 | 未指定 | `plugin-nestedstructure`(非默认) |
| CVSS | 9.8 严重 | ~6.0 中等(论证) |
| 互联网暴露 | 未指定 | ~35 个实例(FOFA) |
漏洞是真实的,升级到 2.8.0 是正确的做法。但公告弄错了类名,而且严重程度评分没有反映利用此漏洞的实际要求。
## 参考资料
- Apache Ranger 2.8.0 发布:https://ranger.apache.org/download.html
- 补丁提交(RANGER-4076):https://gitbox.apache.org/repos/asf/ranger.git
- CVE 条目:https://www.cve.org/CVERecord?id=CVE-2025-59059
- Apache 邮件列表主题:https://lists.apache.org/thread/z47q86rho80390lf2qcmoc2josvs0gtv
标签:Apache Ranger, Apache漏洞, CVE-2025-59059, CVSS评分争议, GHAS, Java安全, JDK兼容性, JS文件枚举, Nashorn, RCE, 云安全监控, 企业安全, 协议分析, 安全专业人员, 安全研究员, 数据治理, 权限提升, 漏洞分析, 编程工具, 网络资产管理, 行级过滤器, 补丁比对, 路径探测, 远程代码执行, 静态分析