
Apache Spark 4.2.0 - عالي - عن بُعد - قراءة ملف - RPC StreamRequest File join بدون مصادقة
abraxaslabs.tech · github.com/abraxas · @abraxas_null · [email protected] · spark-rpc-stream-unauth
الفئة: صلاحية متبقية / قراءة ملفات الوصول: عن بُعد
Apache Spark 4.2.0 (32f72996) - Apache Software Foundation
CVE-2020-9480 أغلق RequestSubmitDriver / RegisterApplication غير المُصادَق عليهما على مستوى لوحة تحكم RPC. AbstractAuthRpcHandler يحمي receive() و receiveStream(). لكنه لا يحمي getStreamManager(). لا يزال TransportRequestHandler يوجّه StreamRequest إلى ذلك المدير قبل SASL. يقوم NettyStreamManager.openStream بتنفيذ new File(dir, fname) دون أي تحقق من البقاء داخل الجذر.
StreamRequest غير المُصادَق عليه مع classes/../../path يقرأ ملفات Spark-UID. الأبناء المطلقون على Unix يبقون تحت الدليل المسجَّل (المختبر 404). files/ هي عملية get على خريطة (404). receive() / receiveStream() تبقى 401. هذا ليس RCE الخاص بـ 9480.
| المعرّف | لا يوجد CVE بعد |
| الفئة | صلاحية متبقية / قراءة ملفات (متبقٍ غير محمي في مستوى بيانات RPC من CVE-2020-9480) |
| الوصول | عن بُعد (TCP غير مُصادَق عليه إلى RpcEnv الخاص بالسائق / ESS / BlockManager مع spark.authenticate=true) |
| CWE | CWE-862، CWE-22 |
| CVSS | مرتفع: 7.5 CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N |
| المنتج | Apache Spark |
| المتأثر | 4.2.0 (32f7299601108917fb01920a54e084595b7b3bf8). spark.authenticate=true مُفعَّل من قِبل المشغّل. قراءة ملفات المضيف تتطلب دليلاً مسجَّلاً (spark.repl.class.outputDir /classes و/أو artifacts الخاص بـ Connect). |
| المصادقة | لا شيء على StreamRequest / getStreamManager() |
| الترخيص | GNU Affero GPL v3.0 |
| المختبر | 127.0.0.1 فقط |
TCP إلى منفذ Spark RPC الذي لديه spark.authenticate=true ودليل مسجَّل. لا سر مشترك. لا spark.network.crypto.enabled. أرسل StreamRequest على مستوى النقل (نوع الرسالة 6) مع streamId بقيمة classes/../../<path>. لا يستدعي TransportRequestHandler.processStreamRequest أبداً RpcHandler.receive. بل يستدعي getStreamManager().openStream. غلاف 9480 يعيد المدير الحقيقي. new File(dir, "../../oracle/secret") يُحوَّل قانونياً خارج جذر REPL/artifact ويعود كـ FileSegmentManagedBuffer.
هذا قراءة لملف على المضيف بصلاحيات Spark UID. ليس إرسال مهمة. ليس RequestSubmitDriver. ليس shell.
| الطريقة | المصب | الصلاحية |
|---|---|---|
StreamRequest غير مُصادَق عليه مع classes/../../<path> | NettyStreamManager.openStream ينفّذ new File(dir, fname) ثم FileSegmentManagedBuffer | قراءة ملفات Spark-UID التي يستطيع السائق فتحها. مُثبَت مختبرياً: opened /opt/repl-classes/../../oracle/secret، القانوني /oracle/secret، المحتوى SPARK-RPC-STREAM-UNAUTH-WITNESS، x-auth-gated=false، x-authenticated=false. |
StreamRequest غير مُصادَق عليه مع classes/hello.txt (ضابط داخل الدليل) | نفس الدمج، يبقى تحت الدليل المسجَّل | قراءة ملف موجود فعلاً تحت /classes. مُثبَت مختبرياً 200 IN-DIR-OK، بلا شاهد. يؤكد أن خادم الملفات يعمل دون SASL حتى لاسم محتوى. |
| الطريقة | المصب | الصلاحية |
|---|---|---|
ChunkFetchRequest / StreamRequest غير مُصادَق عليهما لـ streamId حيّ في ESS | OneForOneStreamManager.getChunk / openStream. checkAuthorization يُتخطّى عندما client.getClientId() == null | قراءة بايتات shuffle / disk-RDD قيد التنفيذ لتطبيق آخر. التوافر: الاستنزاف يقدّم curChunk. يتطلب streamId حياً (بداية عشوائية، ثم +1). الأداة لم تخاطب ESS حياً. |
MergedBlockMetaRequest غير مُصادَق عليه | AbstractAuthRpcHandler.getMergedBlockMetaReqHandler() أيضاً غير محمي | قراءة خرائط بتات chunks الخاصة بـ merged-shuffle لـ appId مختار عندما يكون ESS لديه merged shuffle. نفس تخطّي clientId الفارغ. لم يُختبر. |
| الطريقة | المصب | يبقى مغلقاً |
|---|---|---|
StreamRequest غير مُصادَق عليه مع classes//oracle/secret (ابن مطلق على Unix) | new File(dir, "/oracle/secret") | 404. المسار المفتوح هو /opt/repl-classes/oracle/secret. File على Unix يدمج؛ لا يتجاهل الأب. ادعاء Hunt بأن ابناً مطلقاً يُسقط الجذر خاطئ على Unix. |
StreamRequest غير مُصادَق عليه مع files//oracle/secret | get على خريطة files | 404. files/ و jars/ عمليات بحث بالاسم الأساسي، وليست دمجاً. |
receive() / OneWayMessage غير مُصادَق عليهما | AbstractAuthRpcHandler.receive | 401 Unauthenticated call to receive(). x-auth-gated=true. |
receiveStream() / UploadStream غير مُصادَق عليهما | AbstractAuthRpcHandler.receiveStream | 401 Unauthenticated call to receiveStream(). |
receiveStream() مُصادَق عليه بالسر المختبري | نفس البوابة بعد doAuthChallenge | 200 receiveStream-ok. ضابط أن السر لا يزال يفتح مسار 9480. |
مُثبَت مختبرياً مقابل مُستنتَج: قراءة ملفات .. غير المُصادَق عليها، 404 للمطلق على Unix، 404 لـ files/، receive / receiveStream المحميان كلها مخبرية. استنزاف ESS shuffle مُستنتَج.
مغلق مجاور (لا تبالغ):
RequestSubmitDriver / RegisterApplication غير المُصادَق عليهما عبر OneWayMessage / UploadStream / RpcRequest. لا يزال يتطلب السر المشترك.spark.authenticate=false (SECURITY.md FAQ، الإعداد الافتراضي الموثَّق للشبكة الموثوقة). ليس هذا المتبقي.AppIsolationSuite / SPARK-57889 مع clientId غير فارغ)./classes أو artifacts مسجَّل، لذا هذا التجوّل في ملفات المضيف يخص السائق / REPL / Connect، وليس master:7077.spark.authenticate.secret.ثبّتُ apache/spark v4.2.0 (32f7299601108917fb01920a54e084595b7b3bf8)، ليس master وليس 4.3 RC. ثمانية وعشرون بحثاً في المصدر. إرسال المهام على عنقود مفتوح مقصود بالتصميم. تحميل نموذج ML كود قابل للتنفيذ. RCE العام لـ Jackson History، وshell id -Gn في الواجهة، وانتحال PySpark المحلي، ومسار receive في 9480 كلها مغلقة في هذا التقشير.
كان بحث مصادقة RPC يُفترض أن يكون فحص إعادة إيداع لـ 9480. لا يزال AuthServerBootstrap يغلّف المعالج. لا يزال receive غير المُصادَق عليه يرمي استثناءً. ثم قرأت getStreamManager(). ثلاثة أسطر. يعيد المفوَّض. يوجّه TransportRequestHandler.handle رسالة StreamRequest حول receive. الإعداد الافتراضي StreamManager.checkAuthorization عملية لا تفعل شيئاً. أضاف SPARK-57889 فحص معرّف نصي على StreamRequest وأبقى على تخطّي clientId الفارغ الذي يستخدمه ESS لـ "المصادقة معطّلة"، وهو أيضاً "هذا الاتصال لم يُصادَق عليه قط".
يسجّل SparkContext الدليل /classes عندما يكون spark.repl.class.outputDir مضبوطاً. يسجّل ArtifactManager الخاص بـ Connect الدليل artifacts. يقسم NettyStreamManager.openStream ftype/fname. الأنواع غير files/jars تنفّذ new File(dir, fname) دون فحص البادئة القانونية.