المشروع
للتشغيل: ./script.sh
المستودع على جيت هاب - https://github.com/LaCinquette/practice-22-23
خطوات التنفيذ
-
للبحث عن ثغرة اخترت مشروع FFmpeg. وجدت فيه commit يلبي المتطلبات، وهي:
- يطابق النوع CWE-476 - إلغاء مرجعية مؤشر فارغ (NULL pointer dereference)
-
أنشأت ملف Dockerfile يتضمن ما يلي:
- اخترت Ubuntu 22.04 كصورة أساسية
- قمت بتعيين
DEBIAN_FRONTEND=nointeractive لاستبعاد أي تفاعل مع سطر الأوامر أثناء تثبيت الحزم وتنفيذ الأوامر الأخرى
- قمت بتثبيت التبعيات اللازمة المذكورة في الويكي الرسمي
- أُجهّز مكان العمل (مجلد workdir)
- أُحمّل وأفك ضغط وأنقل الـ commit الذي يحتوي على الثغرة غير المُصلَحة إلى مكان العمل
- أشغّل بناء البرنامج باستخدام أداة make
- أنشئ السكربت copy_out.sh المسؤول عن البحث عن ملف الكائن (object file) المطلوب الذي يحتوي على الخطأ ونسخه إلى الدليل workspace/out
- أترك تعليمة لتنفيذ السكربت copy_out.sh، والتي يجب أن تُنفَّذ عند تشغيل الحاوية
-
لأتمتة العملية أنشئ السكربت script.sh الذي يبني الصورة ويشغّل الحاوية، ثم يأخذ الملف منها وينقله إلى الدليل الحالي:
- أحذف المجلد الحالي out (إن وُجد)
- أبني الصورة بالاسم ffmpeg_image
- أشغّل حاوية بناءً على الصورة المبنية، مررًا لها المعاملات التالية:
--rm للحذف التلقائي للحاوية عند الانتهاء
-v $PWD/out:/workspace/out لتركيب دليل مؤقت يتم من خلاله نقل ملف الكائن إلى المضيف
- أنسخ الملف المطلوب من المجلد out
- أحذف المجلد out
تحليل الثغرة
المصادر:
- Commit مع إصلاح الخطأ
- صفحة على Github حول الثغرة
- رسالة من الباحث الأمني حول الثغرة
التحليل:
المشكلة:
- باستخدام الأمر
ffmpeg -c:v dnxhd -i poc.mov -y output.ts يتم تمرير ملف .mov مُنشأ خصيصًا إلى البرنامج
- لتحليل (parsing) صيغة DNxHD يتم استدعاء الدالة dnxhd_parse الموجودة في الملف dnxhd_parser
- وهي بدورها تستدعي الدالة dnxhd_find_frame_end (في نفس الملف)، التي لا تجد النهاية في ظروف معينة وتُرجع رقمًا سالبًا ليس رمز خطأ
- يُمرَّر هذا الرقم بعد ذلك مع معاملات أخرى إلى الدالة ff_combine_frame (الموجودة في الملف parser) لدمج الإطارات
- ولأن هذا الرقم ليس رمز خطأ، فإن ff_combine_frame تعتبره إزاحة وتحاول إعادة قراءة هذه البايتات من المخزن المؤقت، وبما أن المخزن المؤقت فارغ في تلك اللحظة، يحدث إلغاء مرجعية مؤشر فارغ، مما يؤدي إلى Segmentation fault
في لغة التجميع (Assembly):
0x08000d4e - بداية المقطع
-
movsxd rcx, dword [rbx + 0xc]
نحصل في السجل rcx على مرجع إلى pc->buffer
-
add rcx, rdi
نضيف next إلى pc->buffer
-
add rcx, qword [rbx]
نضيف pc->last_index إلى next و pc->buffer
-
movzx edi, byte [rcx]
يُحسب البايت عند العنوان rcx، ولكن بما أن pc->buffer فارغ (null) في البداية، يحدث Segmentation fault
الحل:
منع الوصول إلى منطقة ذاكرة غير مخصصة، من خلال المعالجة الصحيحة للحالة الاستثنائية. عند اكتشاف رقم سالب في البايتات المتبقية، تتم متابعة معالجة الملف من خلال تخطي الحلقة.