
Apache Spark 4.2.0 - عالي - عن بُعد - بقايا صلاحيات - خريطة مصدر بيانات Python على مستوى العملية
abraxaslabs.tech · github.com/abraxas · @abraxas_null · [email protected] · spark-python-ds-static
التحليل المكتوب: https://abraxaslabs.tech/research/spark-python-ds-static
الفئة: صلاحية متبقية الوصول: عن بُعد
Apache Spark 4.2.0 (32f72996) - Apache Software Foundation
DataSourceManager.dataSourceBuilders هي خريطة على مستوى الـ JVM تعمل بمبدأ "الأول يفوز". يقوم SPARK-45917 بالتسجيل التلقائي لمصادر بيانات Python من نوع pyspark_* من sys.path الخاص بعامل البحث. تُبقي عزل جلسة Connect الملفات تحت SparkFiles/<sessionUUID>/، ويبقى registerPython الخاص بكل جلسة على runtimeDataSourceBuilders. ومع ذلك، فإن أول عملية بحث تضع دليل SparkFiles الخاص بالجلسة المُطلِقة على sys.path، وتُسلسل (pickle) كل DefaultSource، وتُخزّن تلك الفئات مؤقتًا في الكائن المرافق. بعد ذلك، تُشغّل استعلامات Connect SQL اللاحقة (أو Hive Thrift SQL) read.format(name) / USING name تلك الفئة بصلاحية Spark UID دون إرسال Python proto.
الزارع هو مستأجر Connect بلغة Python مسموح له بالفعل باستخدام Python. الضحية هو مستأجر SQL فقط على نفس مشغّل Connect. تبقى الأسماء المستعارة في Java (parquet، csv، json، orc، jdbc) على مسار ServiceLoader الخاص بـ Java. عمليات الزرع اللاحقة بعد امتلاء الخريطة لا تُحدّثها. AC:H هو أول عملية بحث بالإضافة إلى اسم ليس اسمًا مستعارًا في Java.
| المعرّف | لا يوجد CVE بعد |
| الفئة | صلاحية متبقية (مصدر بيانات Python عبر الجلسات بصلاحية Spark UID؛ وليس إرسال مهام دون مصادقة) |
| الوصول | عن بُعد (مشغّل Spark Connect مشترك) |
| CWE | CWE-269، CWE-502 |
| CVSS | عالٍ: 8.8 CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:C/C:H/I:H/A:H |
| المنتج | Apache Spark |
| المتأثر | 4.2.0 (32f7299601108917fb01920a54e084595b7b3bf8) السجل الثابت لمصادر بيانات Python |
| المصادقة | مستأجر Connect بلغة Python (الزارع) بالإضافة إلى مستخدم SQL فقط لاحقًا عبر Connect / Hive Thrift SQL (الضحية) |
| الترخيص | GNU Affero GPL v3.0 |
| المختبر | 127.0.0.1:18970 فقط. ملف الشاهد. أداة الخريطة المرافقة. |
الاحتفاظ بجلسة Spark Connect بلغة Python على مشغّل مشترك. زرع pyfiles/pyspark_<name>.py الذي يكون فيه DefaultSource.name() اسمًا قصيرًا لموصّل غير موجود (delta، iceberg، hudi، ...). إطلاق أول عملية بحث عن مصدر بيانات Python على تلك الجلسة. كل جلسة لاحقة في نفس الـ JVM تُنشئ فيها read.format(name) أو SQL USING name الفئة المُسلسلة بصلاحية Spark UID على عامل مخطط المشغّل وعلى المنفّذين.
هذه هي مجموعة الصلاحيات الكاملة. الزارع يشغّل Python بالفعل. ما تبقّى هو أن مستأجر SQL فقط، الذي لم يُرسل قط REGISTER_DATA_SOURCE / Python UDF proto، لا يزال يُشغّل تلك الفئة.
| الطريقة | المصب | الصلاحية |
|---|---|---|
الزارع: AddArtifacts باسم pyfiles/pyspark_<name>.py ثم أول format(name) / بحث | الخريطة المرافقة DataSourceManager.dataSourceBuilders (كائن DataSourceManager.scala) بالإضافة إلى iter_modules في lookup_data_sources.py لـ pyspark_* | تخزين DefaultSource المُسلسل الخاص بالجلسة المُطلِقة مؤقتًا على مستوى الـ JVM. المختبر: layer='static' trigger='attacker' first-wins=yes. |
الضحية: SQL USING <name> / spark.read.format(name) على جلسة لم تزرع شيئًا | PythonDataSourceV2 createDataSourceInPython | تشغيل تلك الفئة بصلاحية Spark UID. المختبر: planted_by='attacker' witness_written=True static-cross-session=yes. يحتوي ملف الأوراكل على SPARK-PYTHON-DS-STATIC-WITNESS. |
| الطريقة | المصبّ | النتيجة |
|---|---|---|
| زرع لاحق لاسم ثانٍ بعد امتلاء الخريطة | نفس الخريطة الساكنة | مغلق. الأول يفوز. المختبر: later-lookup found=False، والرفيق ما يزال delta فقط. |
registerPython("alpha") لكل جلسة | runtimeDataSourceBuilders | معزول. النسخة تستنسخ وقت التشغيل فقط. المختبر: runtime-isolated=yes. قراءة الضحية لـ alpha هي found=False. |
الاسم المستعار Java parquet | Java DataSourceRegister ServiceLoader | مغلق. المختبر: java-alias-closed=yes layer='java'. الأمر نفسه لـ csv/json/orc/jdbc. |
| الطريقة | المصبّ | الصلاحية |
|---|---|---|
USING من الضحية بعد الزرع | عاملو Python في driver + executor | تنفيذ كود على مستوى العنقود بهوية Spark UID. يكتب المختبر ملف شاهد بدلاً من حمولة. |
| المسار نفسه | انهيار العامل / الدوران | إتاحة بهوية Spark UID. ليس الثغرة العالية. العالية هي تنفيذ الكود. |
Hive Thrift SQL USING على SparkSession مشتركة | نفس الخريطة الساكنة | نفس البقايا إذا تقاسم Thrift وConnect الـ driver JVM. لم يُختبر. |
parquet / csv / json / orc / jdbc. ServiceLoader يفوز.concatenatePaths يرفض .. والأسماء المطلقة).pythonExec المقدَّم من العميل. يستخدم مخطط Connect خادم PYSPARK_PYTHON / python3.registerPython عبر الجلسات. خريطة وقت التشغيل لكل نسخة DataSourceManager.pyspark محلي أحادي المستخدم بجلسة واحدة. لا يوجد مستأجر آخر.ثبّتُّ apache/spark v4.2.0 (32f7299601108917fb01920a54e084595b7b3bf8). ثمانية وعشرون بحثًا في المصدر. إرسال المهام الذي ينفّذ كود المستخدم بحكم التصميم. History Jackson، وUI shell، وانتحال الهوية المحلي في PySpark، ومسار استقبال RPC المستقل مغلقة في هذه المراجعة. تعزل Connect الـ artifacts ملفات pyfiles تحت UUID جلسة. registerPython في وقت التشغيل لكل جلسة. أما الكائن الرفيق فليس كذلك.
يسجّل SPARK-45917 حزم pyspark_* تلقائيًا من PYTHONPATH. هذا مقصود للموصّلات التي يثبّتها المشغّل. المتبقي هو أن عامل البحث أيضًا يقوم بـ sys.path.insert لمجلد SparkFiles الخاص بـالجلسة المُطلِقة (worker_util.setup_spark_files)، ثم ي pickle أي DefaultSource من نوع pyspark_* يجده في خريطة عامة على مستوى العملية لا تُحدَّث أبدًا.
منعطف خاطئ: اعتبار عزل registerPython هو الحد الكامل بين المستأجرين. وقت التشغيل معزول. أما الساكن فليس كذلك.
cd lab
./run.sh
Loopback 127.0.0.1:18970. مشروع Compose spark-python-ds-static. الشاهد SPARK-PYTHON-DS-STATIC-WITNESS. أداة Python من object DataSourceManager بالإضافة إلى خرائط وقت التشغيل لكل جلسة. زرع pyspark_delta.py فقط في SparkFiles الخاص بالمهاجم. بحث المهاجم يملأ الرفيق. الزرع اللاحق لـ iceberg لا يُحدّث. read الخاص بالضحية لـ delta يكتب الـ oracle. registerPython("alpha") لا يسرّب. parquet الخاص بـ Java يبقى Java.
السطر الأخير:
SUCCESS SPARK-PYTHON-DS-STATIC static-cross-session=yes runtime-isolated=yes first-wins=yes java-alias-closed=yes SPARK-PYTHON-DS-STATIC-WITNESS
لا تضع ملفات SparkFiles الخاصة بالجلسة على sys.path لعامل البحث عند ملء الخريطة الثابتة. حمّل حزم PYTHONPATH الخاصة بالمشغّل فقط (SPARK-45917 كما هو مصمم). أو اجعل الخريطة الثابتة مفهرسة حسب الجلسة / UUID الخاص بـ JobArtifactSet، بالطريقة التي تُفهرس بها القطع الأثرية بالفعل. لا ينبغي أن يؤدي الملء الفارغ بأسبقية الأول إلى تثبيت فئات مستأجر آخر طوال عمر برنامج التشغيل.