
يوثّق CVE-2026-65320، وهو اجتياز مسار tar-slip في untar_dir() الخاص بـ fastcore، مع إثبات مفهوم غير ضار يوضح الكتابة التعسفية للملفات وتنفيذ التعليمات البرمجية.
untar_dir()الخطورة: حرجة، CVSS 3.1 9.8
المتجه (v3.1): CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H
المتأثر: fastcore <= 2.2.30
تم الإصلاح في: لم يتم الإصلاح وقت كتابة هذا التقرير
CWE: CWE-22 (التقييد غير الصحيح لاسم المسار إلى دليل محظور، 'اجتياز المسار')
المكوّن: fastcore.xtras.untar_dir() → fastcore.xtras._unpack() → shutil.unpack_archive()
بيئة التشغيل عالية الخطورة: Python < 3.14 (يجعل Python 3.14 مرشّح tar الآمن data هو الافتراضي)
أبلغ عنه: Rahul Karne
CNA: VulnCheck
fastcore.xtras.untar_dir(fname, dest) هي دالة مساعدة موثّقة تقوم بفك ضغط أرشيف tar "إلى dest".
داخليًا، تقوم بتمرير الأرشيف إلى shutil.unpack_archive() من المكتبة القياسية دون مرشّح استخراج، ودون التحقق من مسارات أعضاء الأرشيف، ودون أي فحص احتواء بعد الاستخراج.
ونتيجة لذلك، يمكن لأرشيف TAR يحتوي أعضاؤه على تسلسلات اجتياز ../ أو مسارات مطلقة أن يكتب ملفات خارج الدليل الذي طلب المتصل من fastcore الاستخراج إليه.
المتصل الذي يتوقع بشكل معقول أن untar_dir(fname, dest) ستحصر كل شيء داخل dest يحصل بدلاً من ذلك على كتابة ملفات عشوائية في أي مكان يمكن للعملية الكتابة فيه.
كتابة ملفات عشوائية تحت صلاحية العملية التي تستدعي untar_dir().
اعتمادًا على ما يتم استبداله، يمكن أن يؤدي ذلك إلى:
يوضّح إثبات المفهوم سلسلة الكتابة → الاستيراد → تنفيذ التعليمات البرمجية كاملة من البداية إلى النهاية، بالكامل داخل دليل تجريبي غير ضار.
fastcore هي تبعية أساسية في منظومة fast.ai بأكملها، بما في ذلك:
fastainbdevghapiوهي تتلقى عشرات الملايين من التنزيلات شهريًا.
untar_dir() هي البدائية الأساسية للاستخراج وراء سير العمل الشائع "تنزيل أرشيف، ثم فك ضغطه"، والذي يتبع نفس النمط العام لـ untar_data في fastai.
أي مسار برمجي يغذّي untar_dir() بأرشيف لا تكون محتوياته موثوقة بالكامل يكون معرّضًا للخطر.
توجد التعليمات البرمجية المعرّضة للخطر في fastcore/xtras.py:
def _unpack(fname, out):
import shutil
shutil.unpack_archive(str(fname), str(out)) # <-- no filter, no validation
ls = out.ls()
return ls[0] if len(ls) == 1 else out
def untar_dir(fname, dest, rename=False, overwrite=False, uid=-1, gid=-1):
"untar `file` into `dest` ..."
import tempfile, shutil
dest = Path(dest)
with tempfile.TemporaryDirectory() as d:
out = Path(d) / remove_suffix(Path(fname).stem, '.tar')
out.mkdir()
...
src = _unpack(fname, out) # extraction escapes `out` here
...
shutil.move(str(src), dest)
...
return dest
تنشئ untar_dir() دليل استخراج مؤقت داخلي:
<tempdir>/<random>/<archive-stem>/
ثم تستدعي _unpack()، التي تمرّر الأرشيف مباشرة إلى:
shutil.unpack_archive()
بالنسبة لأرشيفات TAR، يستدعي هذا في النهاية tarfile.extractall().
على Python < 3.14، يكون سلوك الاستخراج الافتراضي هو fully_trusted، مما يعني أنه يمكن احترام الأعضاء المتجاوزة.
على سبيل المثال، عضو أرشيف مثل:
../../../some/other/dir/file
يُكتب نسبةً إلى دليل الاستخراج وبالتالي يمكنه الهروب منه.
لا شيء في fastcore:
filter="data" حيث يكون مدعومًا....تتعامل untar_dir() مع shutil.unpack_archive() كما لو كانت مستخرج أرشيف آمنًا ومعزولاً.
لكنها ليست كذلك.
تحذّر وثائق مكتبة Python القياسية من أن استخراج الأرشيفات غير الموثوقة قد ينشئ ملفات خارج الوجهة المطلوبة، وتوصي بترشيح الاستخراج الآمن لأرشيفات TAR.
لا تمرّر fastcore مرشّح استخراج مناسبًا ولا تنفّذ حارس احتواء مكافئًا لإصدارات Python التي تدعمها.
كما أنها لا تحذّر المتصلين بـ untar_dir() من هذا السلوك الحساس أمنيًا.
يمكن أن تحتوي أعضاء TAR على سلاسل مسارات عشوائية.
على إصدارات Python المتأثرة، لا يقوم tarfile.extractall() بتحييد تسلسلات اجتياز .. تلقائيًا بشكل افتراضي.
لذلك، فإن كتابة عضو متجاوز نسبةً إلى دليل الاستخراج المؤقت في fastcore كافية للهروب من ذلك الدليل والكتابة في أي مكان تملك العملية صلاحية الكتابة فيه.
البدائية الموثوقة هي الاجتياز النسبي مثل:
../../../target/file
وهذه هي التقنية المستخدمة في إثبات المفهوم.
المدخل الخطير هو محتويات الأرشيف، وليس مسار الوجهة الذي اختاره المطوّر.
غالبًا ما يتم الحصول على الأرشيفات من مواقع خارجية أو شبه موثوقة، بما في ذلك:
لذلك، أي خدمة أو أداة تنزّل أرشيفًا من مصدر غير موثوق أو شبه موثوق ثم تعالجه باستخدام untar_dir() قد تكون قابلة للوصول.
يتضمن إثبات المفهوم أيضًا نسخة تقدّم الأرشيف المُصمَّم من نقطة نهاية HTTP يتحكم بها المهاجم لتوضيح مسار التسليم هذا.
يتطلب الاستغلال الناجح:
أن يستدعي التطبيق fastcore.xtras.untar_dir() على أرشيف تكون محتوياته متأثرة بالمهاجم، مثل أرشيف مرفوع أو منزّل أو غير موثوق بأي شكل آخر.
أن تستخدم بيئة التشغيل Python < 3.14، أو أي إعداد آخر لا يُفرض فيه مرشّح استخراج TAR آمن.
للتصعيد من كتابة ملفات عشوائية إلى تنفيذ التعليمات البرمجية، يجب أن يستورد أو يحمّل التطبيق لاحقًا ملفًا من موقع يمكن الوصول إليه عبر الاجتياز، مثل:
تتطلب كتابة الملفات العشوائية وتعديل الإعدادات/البيانات الشرطين 1 و2 فقط.
يستدعي poc_fastcore_cna_demo.py واجهة fastcore.xtras.untar_dir() الحقيقية.
العرض غير ضار عن قصد.
كل ملف ينشئه إثبات المفهوم يبقى داخل دليل تجريبي واحد يُحدَّد باستخدام:
--demo-root
لا يعدّل إثبات المفهوم ملفات نظام حقيقية ولا يصل إلى خدمات سحابية أو بيانات اعتماد.
يطبع تفاصيل البيئة والتعليمات البرمجية المعرّضة للخطر الخاصة بـ _unpack() و untar_dir().
ينشئ تخطيطًا تجريبيًا يحتوي على:
service_app/plugins/
ينشئ:
attacker_supplied_upload.tar
يحتوي على أعضاء أرشيف مصمّمة للإشارة خارج جذر الاستخراج المقصود، بما في ذلك أعضاء الاجتياز و:
service_app/plugins/startup_hook.py
def auto_extract_uploaded_archive(received_archive):
return untar_dir(
received_archive,
intended_root,
overwrite=True
)
يتحقق من أن الملفات كُتبت خارج دليل الاستخراج المقصود.
يحاكي إعادة تحميل الخدمة باستيراد الملف المكتوب من الأرشيف:
startup_hook.py
ينشئ خطاف بدء التشغيل غير الضار:
SERVICE_RELOAD_MARKER
يوضّح هذا السلسلة الكاملة:
Attacker-controlled archive
↓
Path traversal during extraction
↓
Arbitrary file write
↓
Write into importable / auto-loaded location
↓
Application loads attacker-controlled file
↓
Code execution
ثبّت الحزمة المتأثرة:
pip install fastcore
تم تأكيد المشكلة عبر:
fastcore 2.2.30
يمكن تشغيل العرض مقابل أي من:
fastcore المثبّتة عبر pip--sourceتبقى جميع كتابات إثبات المفهوم داخل الدليل المحدد عبر --demo-root.
نظّف أي مخرجات سابقة لإثبات المفهوم:
python poc_fastcore_cna_demo.py --demo-root ./poc_fastcore --clean
شغّل العرض: