
Apache Livy अनधिकृत फ़ाइल एक्सेस भेद्यता के लिए एक POC
केवल शैक्षिक और सुरक्षा अनुसंधान उद्देश्यों के लिए। उन प्रणालियों के विरुद्ध उपयोग न करें जिनके स्वामी आप नहीं हैं या जिनके परीक्षण की स्पष्ट लिखित अनुमति आपके पास नहीं है। → पूर्ण अस्वीकरण
| फ़ील्ड | विवरण |
|---|---|
| CVE ID | CVE-2025-60012 |
| गंभीरता | मध्यम (CVSS 6.3) |
| प्रभावित | Apache Livy 0.7.0-incubating, 0.8.0-incubating — जब Apache Spark 3.1 या उसके बाद से कनेक्टेड हो |
| समाधान संस्करण | Apache Livy 0.9.0-incubating |
| CWE | CWE-20: अनुचित इनपुट सत्यापन |
| खुलासा तिथि | 2026-03-13 |
| रिपोर्टकर्ता | Furue Hideyuki |
Livy के REST या JDBC इंटरफ़ेस तक पहुँच रखने वाला एक प्रमाणित उपयोगकर्ता, गढ़े गए कॉन्फ़िगरेशन मानों के साथ एक Spark सत्र या बैच जॉब सबमिट कर सकता है। दो कमज़ोरियाँ मिलकर हमलावर को उनकी अनुमत पथों के बाहर स्थित लोकल फ़ाइलसिस्टम फ़ाइलों को संदर्भित करने की अनुमति देती हैं:
spark.archives के लिए सत्यापन का अभाव — Spark 3.1 ने spark.archives को सभी क्लस्टर मैनेजरों में
आर्काइव फ़ाइलें वितरित करने के एक एकीकृत तरीके के रूप में पेश किया। Livy 0.8.0 की हार्डकोडेड
सूची, जिसमें पथ-सत्यापित होने वाली कॉन्फ़िगरेशन कुंजियाँ हैं (HARDCODED_SPARK_FILE_LISTS), में
spark.archives शामिल नहीं है। इसलिए इस कुंजी के माध्यम से पास किया गया पथ कभी भी लोकल फ़ाइलसिस्टम
व्हाइटलिस्ट (livy.file.local-dir-whitelist) के विरुद्ध जाँचा नहीं जाता, जिससे हमलावर को
किसी भी लोकल फ़ाइल को संदर्भित करने की अनुमति मिलती है।
व्हाइटलिस्ट जाँच में पाथ ट्रैवर्सल बाईपास — यहाँ तक कि उन कॉन्फ़िगरेशन कुंजियों के लिए भी जिनका सत्यापन
किया जाता है, Livy 0.8.0 में व्हाइटलिस्ट तुलना कच्चे पथ पर एक साधारण Java String startsWith
कॉल का उपयोग करती है। एक हमलावर पाथ ट्रैवर्सल का उपयोग करके इसे बायपास कर सकता है:
/whitelisted/dir/../../etc/passwd स्ट्रिंग जाँच में पास हो जाता है लेकिन अनुमत निर्देशिका के बाहर
रिज़ॉल्व हो जाता है।
LivyConf.scalaभेद्य (v0.8.0): https://github.com/apache/incubator-livy/blob/v0.8.0-incubating/server/src/main/scala/org/apache/livy/LivyConf.scala
समाधान (v0.9.0): https://github.com/apache/incubator-livy/blob/v0.9.0-incubating/server/src/main/scala/org/apache/livy/LivyConf.scala
Session.scalaभेद्य (v0.8.0): https://github.com/apache/incubator-livy/blob/v0.8.0-incubating/server/src/main/scala/org/apache/livy/sessions/Session.scala
समाधान (v0.9.0): https://github.com/apache/incubator-livy/blob/v0.9.0-incubating/server/src/main/scala/org/apache/livy/sessions/Session.scala
दोनों संस्करणों को इस वर्कस्पेस में निम्नलिखित सटीक कमांड्स का उपयोग करके सीधे आधिकारिक Apache Livy GitHub रिपॉजिटरी से क्लोन किया गया:
रिपॉजिटरी: https://github.com/apache/incubator-livy```bash
git clone --depth=1 --branch v0.8.0-incubating
https://github.com/apache/incubator-livy
livy-0.8.0
git clone --depth=1 --branch v0.9.0-incubating
https://github.com/apache/incubator-livy
livy-0.9.0
| संस्करण | टैग | समाधानित कमिट | स्थानीय पथ |
|---------|-----|-----------------|------------|
| 0.8.0-incubating | `v0.8.0-incubating` | `78b512658e4baf1183f2b352203ada1928d8111a` | `./livy-0.8.0/` |
| 0.9.0-incubating | `v0.9.0-incubating` | `7215f209b25b96488189567807eaded00953a492` | `./livy-0.9.0/` |
## सटीक कोड अंतर
अंतर उत्पन्न करने के लिए दोनों टैग को स्थानीय रूप से क्लोन किया गया (ऊपर देखें) और फिर चलाया गया:```bash
diff -u livy-0.8.0/server/src/main/scala/org/apache/livy/LivyConf.scala \
livy-0.9.0/server/src/main/scala/org/apache/livy/LivyConf.scala
diff -u livy-0.8.0/server/src/main/scala/org/apache/livy/sessions/Session.scala \
livy-0.9.0/server/src/main/scala/org/apache/livy/sessions/Session.scala
LivyConf.scala: spark.archives को हार्डकोडेड फ़ाइल सूची में जोड़ा गया```diffprivate val HARDCODED_SPARK_FILE_LISTS = Seq( SPARK_JARS, SPARK_FILES, SPARK_ARCHIVES, SPARK_PY_FILES,
**v0.8.0 में प्रविष्टि के अभाव का प्रभाव:**
जब कोई उपयोगकर्ता `conf: {"spark.archives": "file:///etc/passwd"}` के साथ एक सत्र प्रस्तुत करता है, Livy 0.8.0 उस मान पर `resolveURIs()` कभी नहीं कहता है और इसे `livy.file.local-dir-whitelist` के विरुद्ध कभी जाँचता नहीं है। यह पथ बिना अमान्यित (unvalidated) Spark को आगे भेज दिया जाता है।
---
### फिक्स 2 — `Session.scala`: व्हाइटलिस्ट जाँच से पहले पथ सामान्यीकरण```diff
def resolveURI(uri: URI, livyConf: LivyConf): URI = {
...
if (resolved.getScheme() == "file") {
- require(livyConf.localFsWhitelist.find(resolved.getPath().startsWith).isDefined,
+ require(livyConf.localFsWhitelist.find(
+ Paths.get(resolved.getPath()).normalize.startsWith).isDefined,
s"Local path ${uri.getPath()} cannot be added to user sessions.")
}
}
Impact in v0.8.0:
रॉ स्ट्रिंग startsWith जाँच को पाथ ट्रैवर्सल पेलोड से बायपास किया जा सकता है।
उदाहरण: यदि `livy.file.local-dir-whitelist = /opt/safe-data```` /opt/safe-data/../../../etc/passwd
- v0.8.0: `"/opt/safe-data/../../../etc/passwd".startsWith("/opt/safe-data")` → **true** (बायपास हो गया)
- v0.9.0: `Paths.get("/opt/safe-data/../../../etc/passwd").normalize` → `/etc/passwd`
`/etc/passwd`.startsWith(`/opt/safe-data`) → **false** (अवरुद्ध)
## हमला वेक्टर सारांश```
Attacker (authenticated REST/JDBC user)
│
▼
POST /sessions (or /batches)
{
"conf": {
"spark.archives": "file:///etc/shadow" ← Attack 1: unvalidated Spark 3.1 key
"spark.jars": "file:///safe/../etc/shadow" ← Attack 2: path traversal bypass
}
}
│
▼
Livy 0.8.0 — validation skipped / bypassed
│
▼
Spark reads the file and distributes it to executors
│
▼
Attacker retrieves file contents via job output / logs
इस PoC के सभी चरण निम्नलिखित सिस्टम पर निष्पादित और सत्यापित किए गए:
| घटक | विवरण |
|---|---|
| होस्ट OS | Ubuntu 24.04.4 LTS (Noble Numbat) |
| कर्नेल | 6.17.0-14-generic x86_64 |
| आर्किटेक्चर | x86_64 |
| कुल मेमोरी | 15.49 GiB |
| डॉकर इंजन | 28.2.2 |
| होस्ट JDK | OpenJDK 17.0.18 (केवल होस्ट द्वारा उपयोग — कंटेनर eclipse-temurin:11-jdk-focal का उपयोग करते हैं) |
| कंटेनर आधार छवि | eclipse-temurin:11-jdk-focal (JDK 11, Ubuntu Focal) |
| स्पार्क संस्करण (दोनों छवियाँ) | 3.1.3 with Hadoop 3.2 |
| लिवी संस्करण — असुरक्षित छवि | 0.8.0-incubating (Scala 2.12 build) |
| लिवी संस्करण — पैच की गई छवि | 0.9.0-incubating (Scala 2.12 build) |
. ├── LICENSE ├── README.md ├── docker/ │ ├── fixed/ │ │ ├── Dockerfile │ │ └── livy.conf │ └── vulnerable/ │ ├── Dockerfile │ └── livy.conf ├── livy-0.8.0/ ← Apache Livy 0.8.0-incubating source ├── livy-0.9.0/ ← Apache Livy 0.9.0-incubating source └── test/ └── validate.sh
---
## अवधारणा का प्रमाण