
Apache Livy Path Traversal Whitelist Bypass Vulnerability के लिए एक POC
केवल शैक्षिक और सुरक्षा अनुसंधान उद्देश्यों के लिए। उन सिस्टमों के विरुद्ध उपयोग न करें जिनके आप स्वामी नहीं हैं या जिनके परीक्षण के लिए आपके पास स्पष्ट लिखित अनुमति नहीं है। → पूर्ण अस्वीकरण
| क्षेत्र | विवरण |
|---|---|
| CVE ID | CVE-2025-66249 |
| गंभीरता | महत्वपूर्ण (CVSS N/A — NVD मूल्यांकन लंबित, 2026-03-15 तक) |
| प्रभावित | Apache Livy 0.3.0-incubating से 0.8.0-incubating तक — केवल जब livy.file.local-dir-whitelist को डिफ़ॉल्ट से भिन्न मान पर सेट किया गया हो |
| समाधान | Apache Livy 0.9.0-incubating में |
| CWE | CWE-22: प्रतिबंधित निर्देशिका में पथनाम की अनुचित सीमा ('पथ ट्रैवर्सल') |
| प्रकटीकरण | 2026-03-12 (OSS-Sec) / 2026-03-13 (NVD) |
| रिपोर्टर | हिरोकी एगावा (खोजकर्ता) |
एक प्रमाणित उपयोगकर्ता जिसके पास Livy के REST या JDBC इंटरफ़ेस तक पहुँच है, वह एक स्पार्क सत्र या बैच कार्य को तैयार फ़ाइल-पथ कॉन्फ़िगरेशन मान के साथ प्रस्तुत कर सकता है जो अनुमत निर्देशिका व्हाइटलिस्ट से बच जाता है।
मूल कारण — व्हाइटलिस्ट जाँच में पथ ट्रैवर्सल बाईपास (Session.scala)
जब livy.file.local-dir-whitelist कॉन्फ़िगर किया जाता है, तो Livy 0.8.0 सबमिट किए गए पथों को Java के String.startsWith() को कच्चे, सामान्यीकृत नहीं किए गए पथ पर कॉल करके मान्य करता है। इस जाँच को ../ ट्रैवर्सल अनुक्रमों का उपयोग करके बायपास किया जा सकता है:
/opt/safe-data/../sensitive/secret.txt
कच्चा स्ट्रिंग /opt/safe-data से शुरू होता है, इसलिए जाँच पास हो जाती है — लेकिन पथ हल होकर /opt/sensitive/secret.txt हो जाता है, जो पूरी तरह से व्हाइटलिस्टेड निर्देशिका के बाहर है।
ट्रिगर शर्त: इस भेद्यता का शोषण केवल तभी किया जा सकता है जब livy.file.local-dir-whitelist को डिफ़ॉल्ट (खाली) से भिन्न मान पर सेट किया गया हो। यदि व्हाइटलिस्ट खाली है (डिफ़ॉल्ट), तो पथ मान्यता पूरी तरह से छोड़ दी जाती है और समस्या प्रभावित नहीं होती।
प्रभाव: एक हमलावर जो Livy REST API के माध्यम से एक सत्र सबमिट करता है, वह Livy सर्वर होस्ट पर मनमानी स्थानीय फ़ाइलों को संदर्भित कर सकता है। एक साझा विश्लेषण क्लस्टर में इसका अर्थ है क्रेडेंशियल्स, कुंजियाँ, कॉन्फ़िगरेशन फ़ाइलें, या Livy प्रक्रिया उपयोगकर्ता द्वारा पढ़ी जा सकने वाली कोई भी डेटा का संभावित एक्सपोज़र।
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
# असुरक्षित संस्करण — ./livy-0.8.0/ में क्लोन किया गया
git clone --depth=1 --branch v0.8.0-incubating \
https://github.com/apache/incubator-livy \
livy-0.8.0
# समाधान संस्करण — ./livy-0.9.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/ |
Session.scala: व्हाइटलिस्ट जाँच से पहले Paths.get().normalize() import java.io.InputStream
import java.net.{URI, URISyntaxException}
+import java.nio.file.Paths
import java.security.PrivilegedExceptionAction
+import java.util.concurrent.{Executors, LinkedBlockingQueue, ThreadFactory, ThreadPoolExecutor, TimeUnit}
import java.util.UUID
...
if (resolved.getScheme() == "file") {
// Make sure the location is whitelisted before allowing local files to be added.
- 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.")
}
v0.8.0 में प्रभाव:
कच्चे स्ट्रिंग startsWith जाँच को पथ ट्रैवर्सल पेलोड से बायपास किया जा सकता है।
उदाहरण: यदि livy.file.local-dir-whitelist = /opt/safe-data
/opt/safe-data/../sensitive/secret.txt
"/opt/safe-data/../sensitive/secret.txt".startsWith("/opt/safe-data") → सत्य (बायपास)Paths.get("/opt/safe-data/../sensitive/secret.txt").normalize → /opt/sensitive/secret.txt
/opt/sensitive/secret.txt.startsWith(/opt/safe-data) → असत्य (अवरुद्ध)दोनों टैग को स्थानीय रूप से क्लोन करके (ऊपर देखें) और चलाकर अंतर उत्पन्न किए गए:
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
हमलावर (प्रमाणित REST/JDBC उपयोगकर्ता)
│
▼
POST /sessions
{
"conf": {
"spark.jars": "file:///opt/safe-data/../sensitive/secret.txt"
← पथ व्हाइटलिस्टेड उपसर्ग से शुरू होता है — String.startsWith() पास हो जाता है
← लेकिन ../ ट्रैवर्सल के माध्यम से निर्देशिका के बाहर हल होता है
}
}
│
▼
Livy 0.8.0 — व्हाइटलिस्ट जाँच बायपास (कच्चा startsWith, कोई सामान्यीकरण नहीं)
│
▼
स्पार्क फ़ाइल को पढ़ता है और निष्पादकों को वितरित करता है
│
▼
हमलावर कार्य आउटपुट / लॉग के माध्यम से फ़ाइल सामग्री प्राप्त करता है
इस PoC के सभी चरण निम्नलिखित सिस्टम पर निष्पादित और मान्य किए गए:
| घटक | विवरण |
|---|---|
| होस्ट OS | Ubuntu 24.04.4 LTS (Noble Numbat) |
| कर्नेल | 6.17.0-14-generic x86_64 |
| आर्किटेक्चर | x86_64 |
| कुल मेमोरी | 15 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 Hadoop 3.2 के साथ |
| Livy संस्करण — असुरक्षित इमेज | 0.8.0-incubating |
| Livy संस्करण — समाधान इमेज | 0.9.0-incubating |
CVE-2025-66249-POC/
├── docker/
│ ├── fixed/
│ │ ├── Dockerfile
│ │ ├── livy.conf
│ │ └── start.sh
│ └── vulnerable/
│ ├── Dockerfile
│ ├── livy.conf
│ └── start.sh
├── test/
│ └── validate.sh
├── .gitignore
├── LICENSE
└── README.md
docker/vulnerable/ → इमेज: cve-2025-66249-vulnerable (Livy 0.8.0 + Spark 3.1.3)
docker/fixed/ → इमेज: cve-2025-66249-fixed (Livy 0.9.0 + Spark 3.1.3)
test/validate.sh → एकल स्क्रिप्ट, दोनों वातावरणों के विरुद्ध अपरिवर्तित चलाएँ
पूर्ण अंत-से-अंत अनुक्रम — क्रम में चरण 1 से 4 का पालन करें: