
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 में गए पैच अंतर पर आधारित है।
आधिकारिक एडवाइज़री इस प्रकार है:
"Apache Ranger के संस्करणों <= 2.7.0 में NashornScriptEngineCreator में रिमोट कोड एक्सिक्यूशन भेद्यता की सूचना दी गई है।"
यह गलत है, या कम से कम भ्रामक है। NashornScriptEngineCreator संवेदनशील क्लास नहीं है। यदि कुछ है तो यह कोडबेस में दो Nashorn-संबंधित घटकों में से अधिक सुदृढ़ किया गया है — जिस पर हम आगे बात करेंगे।
Apache Ranger पंक्ति-स्तरीय फ़िल्टर नीतियों का मूल्यांकन करने के लिए JavaScript एक्सप्रेशन का उपयोग करता है। ये ऐसे नियम हैं जो तय करते हैं कि किसी दिए गए उपयोगकर्ता को डेटासेट में किसी विशिष्ट रिकॉर्ड तक पहुंच मिलती है या नहीं। उन एक्सप्रेशन को JVM पर चलाने के लिए, Ranger एक JavaScript इंजन एम्बेड करता है। संस्करणों <= 2.7.0 में वह इंजन Nashorn है — Oracle का बिल्ट-इन JS इंजन जो JDK 8 से 14 के साथ आया था।
एक बात जो एडवाइज़री पूरी तरह से छोड़ देती है — और यह प्रभाव के दायरे के लिए बहुत मायने रखती है — वह यह है कि jdk.nashorn.api.scripting.NashornScriptEngineFactory बिल्कुल भी Ranger के स्रोत कोड का हिस्सा नहीं है। यह एक JDK बिल्ट-इन है। इसे JDK 8 में शामिल किया गया था, JDK 11 में बहिष्कृत (deprecated) किया गया, और JDK 15 में पूरी तरह से हटा दिया गया। इसलिए यह भेद्यता केवल तभी पहुंच योग्य है जब आप JDK 8 से 14 पर Ranger चला रहे हों। JDK 15+ पर कुछ भी प्रभावित नहीं होता क्योंकि वहाँ Nashorn मौजूद ही नहीं है, और ScriptEngineUtil चुपचाप GraalJS या JavaScriptEngineCreator पर पहुँच जाता है।
Ranger में दो क्लासें Nashorn का उपयोग करती हैं। वे इसे बहुत अलग तरह से संभालती हैं।
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 — सभी Java क्लास एक्सेस को ClassFilter स्तर पर ब्लॉक करता है, हर चीज़ के लिए false लौटाता हैक्या यह बुलेटप्रूफ है? नहीं। रिफ्लेक्शन चेन और java.lang.invoke ट्रिक्स के माध्यम से Nashorn ClassFilter बायपास मौजूद हैं। लेकिन यह दूसरी क्लास में जो मिलता है उसकी तुलना में अर्थपूर्ण रूप से अधिक लॉक-डाउन है।
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() से आता है — यह JavaScript एक्सप्रेशन है जो Ranger पंक्ति फ़िल्टर नीति में संग्रहीत होता है। नीति प्रशासक विशेषाधिकार वाला हमलावर उस एक्सप्रेशन को जो चाहे सेट कर सकता है। जब कोई उपयोगकर्ता उस नीति द्वारा शासित किसी संसाधन तक पहुँचता है, तो hasAccessToRecord() सक्रिय होता है, नीति भंडार से filterExpr खींचता है, और filterRow() इसे लगभग बिना किसी सैंडबॉक्सिंग के Nashorn को सौंप देता है।
हमले की श्रृंखला:
नीति प्रशासक पहुँच
|
v
nestedstructure संसाधन पर पंक्ति फ़िल्टर नीति बनाएँ/संशोधित करें
|
v
filterExpr सेट किया गया: java.lang.Runtime.getRuntime().exec("...")
|
v
कोई भी उपयोगकर्ता संसाधन तक पहुँचता है → hasAccessToRecord() ट्रिगर होता है
|
v
filterExpr filterRow() में प्रवाहित होता है → Nashorn मूल्यांकन → RCE
फिक्स को RANGER-4076: Remove Nashorn Script Engine के रूप में ट्रैक किया गया है, जिसे 8 दिसंबर 2025 को Kishor Gollapalliwar द्वारा कमिट किया गया। 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(-)
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;
- }
- }
-}
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;
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<String, Boolean> 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 नहीं है — यह अपना इंजन-स्तरीय हुक खो देता है और केवल स्ट्रिंग जाँच के साथ एक नियमित क्लास बन जाता हैScriptEngineManager से बदल दिए गएnashorn-compat मोड में चलता हैpolyglot.js.allowHostAccess को true पर सेट किया गया है — यह बैकवर्ड संगतता के लिए एक समझौता है, यह ClassFilter के बजाय GraalJS के अपने सैंडबॉक्स पर निर्भर करता है। यदि आप फिक्स की गुणवत्ता की परवाह करते हैं तो इस पर नज़र रखना उचित हैपैच यह स्पष्ट करता है कि RecordFilterJavaScript वास्तविक सुरक्षा लक्ष्य था। NashornScriptEngineCreator को हटाना सफाई थी, फिक्स नहीं।
सच कहूँ तो यह समझना कठिन नहीं है कि यह कैसे हुआ। NashornScriptEngineCreator ठीक agents-common में स्थित है, यह पूरे कोडबेस में सबसे स्पष्ट रूप से Nashorn-नामित क्लास है, और Nashorn के लिए एक बुनियादी grep इसे तुरंत सामने ला देगा। दूसरी ओर RecordFilterJavaScript plugin-nestedstructure में रहता है जो एक अलग सबमॉड्यूल है, और क्लास के नाम में ऐसा कुछ भी नहीं है जो Nashorn की ओर संकेत करे। यदि कोई वास्तव में कोड पथ का अनुसरण किए बिना त्वरित ट्राइएज करता, तो वे संभवतः NashornScriptEngineCreator पर पहुँचकर वहीं रुक जाते।
ऐसा प्रतीत होता है कि यहाँ यही हुआ है। संवेदनशील क्लास और एडवाइज़री में नामित क्लास दो अलग चीज़ें हैं।
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 में अपग्रेड करना सही कदम है। लेकिन एडवाइज़री क्लास को गलत बताती है और गंभीरता स्कोर उस चीज़ को प्रतिबिंबित नहीं करता जो वास्तव में इसका शोषण करने के लिए आवश्यक है।