
CVE-2020-25578 و CVE-2020-25579: بعض ثغرات تسريب المعلومات في FreeBSD التي وجدتها في عام 2020.
ufs_create ولم أجد أي ثغراتstruct dirent المخصصة على المكدسmsdosfs_readdir غير مكتمل. قاموا بتصحيح مثيل واحد من الثغرة، لكن ليس الثاني.mqueuefs و autofs و smbfs و tmpfs التي تسمح لي بتسريب مؤشر كامل بحجم 8 بايت. كتبت دليلاً على المفهوم لتأكيد ذلك.كما ذكر أعلاه، الثغرة الأصلية التي وجدتها كانت في msdosfs_readdir أثناء تحليلي للتصحيح الخاص بالالتزام المرتبط أعلاه.
التدفق الأساسي لاستدعاء readdir في FreeBSD هو كما يلي:
#include <dirent.h>
int main(void) {
struct dirent *dp;
DIR *dirp;
dirp = opendir("./somedir");
dp = readdir(dirp);
}
اعتمادًا على نظام الملفات الذي يوجد فيه somedir، قد يتم استدعاء أي من وظائف *_readdir العديدة في نواة FreeBSD.
التصحيح أعلاه يضيف دالة تسمى dirent_terminate والتي يُقصد استدعاؤها قبل إرجاع كائن struct dirent إلى مساحة المستخدم (غالبًا باستخدام دالة uiomove). ستقوم هذه الدالة بإفراغ بايتات الحشو بالإضافة إلى أي بايتات متبقية في حقل d_name من الهيكل. تعريف struct dirent موجود هنا.
بالنظر إلى التصحيح، في السطر 1562، يمكنك رؤية dirent_terminate تُستدعى مع المتغير dirbuf كوسيطة. بعد ذلك، يتم استدعاء uiomove لنسخ محتويات dirbuf إلى مساحة المستخدم. ومع ذلك، لاحظ أن هذه السطور من الكود تقع ضمن كتلة جملة if هذه. التعليق أعلاه هذه الجملة يوضح أن هذا الفرع يُتخذ فقط إذا تم استدعاء readdir على جذر نظام ملفات MSDOS، لذلك يمكننا ببساطة تجاوز جملة if هذه عن طريق استدعاء readdir في أي دليل فرعي بعد جذر نظام الملفات.
في الأسفل، نرى استدعاء آخر لـ uiomove في السطر 1691. ومع ذلك، عند قراءة الكود بعناية، سترى أن dirent_terminate لم تُستدعَ في هذه الحالة، مما يعني أن بايتات الحشو ستبقى غير مهيأة. لسوء الحظ، تم إفراغ حقل d_name في بداية هذه الدالة (هنا)، لذا لا يمكننا الحصول على تسريب أكبر.
أولاً، لم يكن لدي قرص USB، لذلك كان علي إيجاد طريقة لتركيب نظام ملفات MSDOS. ما يلي يعمل:
$ dd if=/dev/zero of=test.img bs=512 count=256000
$ sudo mdconfig -a -t vnode -f test.img
$ sudo newfs_msdos -s 131072000 /dev/md1 # mdconfig أعاد md1
$ mkdir ./temp
$ sudo mount -t msdosfs /dev/md1 ./temp
$ mkdir ./temp/test_dir
يمكن العثور على دليل المفهوم في original_poc.c. ما عليك سوى ترجمته باستخدام clang وتشغيله من نفس الدليل الذي توجد به الأوامر أعلاه، وسترى البايتات المسربة مطبوعة.
بدأت في البحث عن متغيرات لهذه الثغرة. أعتقد أنني قمت بالبحث عن uiomove\(&.*, والذي أعاد حوالي 15-20 نتيجة، وراجعتها جميعًا يدويًا. لسوء الحظ، لا توجد أي من المتغيرات بشكل افتراضي في FreeBSD (يجب تمكين أنظمة الملفات يدويًا / ترجمتها في النواة). الوظائف التي تحتوي على المتغيرات هي كما يلي:
mqfs_readdirtmpfs_dir_getdotdenttmpfs_dir_getdotdotdentsmbfs_readvdirautofs_readdir_oneالثغرة هي نفسها تمامًا في كل هذه الوظائف، لذا سأغطي mqfs_readdir فقط.
struct dirent entry على المكدسdirent_terminate لإفراغ بايتات الحشو + حقول d_name من الهيكلvfs_read_dirent. هذه الدالة ستستدعي uiomove لنسخ الهيكل إلى مساحة المستخدميبدو كل شيء جيدًا حتى الآن، أليس كذلك؟ ليس بالضرورة. علينا التأكد من تهيئة جميع حقول الهيكل. إذا نظرت عن كثب إلى الكود، سترى أن حقل d_off يبقى غير مهيأ. نوع هذا الحقل هو off_t، وهو في الأساس int64_t. عندما يتم نسخ الهيكل إلى مساحة المستخدم، نحصل على بيانات غير مهيأة في هذا الحقل.
سيعمل هذا الدليل نفسه لجميع المتغيرات، ما عليك سوى تشغيله على نظام ملفات مختلف. بالنسبة لـ mqueuefs، افعل ما يلي (يلزم تمكين / ترجمة mqueuefs في النواة أولاً):
$ mkdir ./temp
$ sudo mount -t mqueuefs null ./temp
يمكن العثور على دليل المفهوم نفسه في variants_poc.c. ما عليك سوى ترجمته باستخدام clang وتشغيله من نفس الدليل الذي توجد به الأوامر أعلاه. سترى مؤشرات النواة مطبوعة (من المفترض مؤشر مكدس واحد ومؤشر قسم كود / كومة واحد، لم أتحقق من ذلك).