Share
## https://sploitus.com/exploit?id=32B15F38-C228-5BA9-BC6C-3CC29F08D5A7
# CVE-2025-59059: Misattributed RCE in Apache Ranger  a correction

**CVE:** CVE-2025-59059  
**Affected versions:** Apache Ranger  "Remote Code Execution Vulnerability in NashornScriptEngineCreator is reported in Apache Ranger versions = JVM_MAJOR_CLASS_VERSION_JDK15;
             } else {
                 logWarn = true;
```

---

### File 3: `RecordFilterJavaScript.java` the real fix

this is where the actual security change is. the direct `NashornScriptEngineFactory` call gets replaced with GraalJS, and `SecurityFilter` loses its `ClassFilter` role entirely gets demoted to a plain class with just the string check.

```diff
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");
         }
     }
```

few things worth calling out from this diff:

- `SecurityFilter` is no longer a `ClassFilter` loses its engine-level hook and becomes just a regular class with only the string check left
- Nashorn imports are gone, replaced with `ScriptEngineManager`
- GraalJS runs in `nashorn-compat` mode for backward compat with existing policy expressions
- `polyglot.js.allowHostAccess` is set to `true` this is a tradeoff for backward compat, it relies on GraalJS's own sandbox rather than a ClassFilter. worth keeping an eye on if you care about the fix quality

the patch makes it clear that `RecordFilterJavaScript` was the actual security target. deleting `NashornScriptEngineCreator` was cleanup, not the fix.

---

## Why the CVE named the wrong class

honestly its not that hard to see how this happened. `NashornScriptEngineCreator` is sitting right in `agents-common`, its the most obviously named Nashorn class in the whole codebase, and a basic grep for Nashorn would surface it immediately. `RecordFilterJavaScript` on the other hand lives in `plugin-nestedstructure` which is a separate submodule, and theres nothing in the class name that hints at Nashorn. if someone did a quick triage without actually following the code path they'd probably land on `NashornScriptEngineCreator` and stop there.

thats appears to be what happened here. the vulnerable class and the class that got named in the advisory are two different things.

---

## Why the CVSS 9.8 doesnt hold up

a 9.8 implies network reachable, no auth required, no user interaction needed. the reality is quite different:

| Factor | Reality |
|--------|---------|
| Authentication | requires policy admin privileges in Ranger |
| Plugin requirement | `plugin-nestedstructure` is not enabled by default |
| JDK constraint | only exploitable on JDK 8โ€“14, JDK 15+ is unaffected |
| Network exposure | FOFA fingerprinting returned ~35 publicly exposed Ranger instances |

none of that is reflected in the advisory. to actually exploit this you need policy admin access, which is a significant privilege level inside a Ranger deployment. this isnt unauthenticated RCE. factoring in the auth requirement and the non-default plugin, something in the 6.0โ€“7.0 range would be a more honest score.

---

## Summary

| | Advisory claim | Actual finding |
|---|---|---|
| Vulnerable class | `NashornScriptEngineCreator` | `RecordFilterJavaScript` |
| Attack type | Unauthenticated RCE | Authenticated RCE (policy admin required) |
| Affected JDK | Not specified | JDK 8โ€“14 only |
| Plugin required | Not specified | `plugin-nestedstructure` (non-default) |
| CVSS | 9.8 Critical | ~6.0 Medium (argued) |
| Internet exposure | Not specified | ~35 instances (FOFA) |

the vuln is real and upgrading to 2.8.0 is the right call. but the advisory gets the class wrong and the severity score doesnt reflect whats actually required to exploit this.

---

## References

- Apache Ranger 2.8.0 release: https://ranger.apache.org/download.html
- Patch commit (RANGER-4076): https://gitbox.apache.org/repos/asf/ranger.git
- CVE entry: https://vulners.com/cve/CVE-2025-59059
- Apache mailing list thread: https://lists.apache.org/thread/z47q86rho80390lf2qcmoc2josvs0gtv