
استغلال تصعيد امتيازات محلية لـ CVE-2023-0386 يستهدف overlayfs في نواة لينكس. يتضمن تحليلًا تفصيليًا للثغرة، وكود إثبات مفهوم، ودليل استغلال خطوة بخطوة باستخدام FUSE ومساحات أسماء المستخدمين.
gcc -Wall exp.c `pkg-config fuse --cflags --libs` -o exp
./exp /tmp

المعرفة النظرية في هذه المقالة (مساحات الأسماء، نظام الملفات overlay، نظام الملفات fuse، إلخ) مأخوذة من chatGPT.
رقم الثغرة: CVE-2023-0386
المنتج المتأثر: نواة لينكس - نظام الملفات overlay
النطاق المتأثر: 5.11 ~ 5.19
شروط الاستغلال: يمكن تنفيذ unshare أو إنشاء نظام ملفات overlay
تأثير الاستغلال: رفع الصلاحيات محليًا
تجميع النواة بنفسك:
جهز إصدارًا ضمن نطاق الثغرة، خارج الإصدار 5.15 (يبدو أن 5.15 به مشكلة)، وقم بتمكين نظامي الملفات overlay و fuse:
CONFIG_SLUB_DEBUGOVERLAY_FS
CONFIG_FUSE_FS
تم اختبار Ubuntu 21.10 بإصدار النواة 5.13.0-16-generic ونجح:

قبل تحليل الثغرة، دع chatGPT يتقمص دور خبير نواة لينكس:
(سؤال chatGPT: الآن ستلعب دور خبير نواة لينكس لمساعدتي في بعض الأسئلة)
المعلومات العامة عن الثغرة قليلة، والأكثر مباشرة هو معلومات التصحيح، رابط التصحيح أدناه:
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=4f11ada10d0a

يمكن رؤية أنه تم إضافة تحقق في دالة ovl_copy_up_one، دعنا نسأل chatGPT أولاً عن وظيفة هذه الدالة:

إذن هذه الدالة تحدث أثناء نسخ الملفات من الطبقة السفلية إلى الطبقة العلوية في نظام الملفات overlay. ثم ننظر إلى التحقق الجديد في التصحيح في سياق الكود:
static int ovl_copy_up_one(struct dentry *parent, struct dentry *dentry,
int flags)
{
int err;
DEFINE_DELAYED_CALL(done);
struct path parentpath;
struct ovl_copy_up_ctx ctx = {
.parent = parent,
.dentry = dentry,
.workdir = ovl_workdir(dentry),
};
if (WARN_ON(!ctx.workdir))
return -EROFS;
ovl_path_lower(dentry, &ctx.lowerpath);
err = vfs_getattr(&ctx.lowerpath, &ctx.stat,//[1] الحصول على stat لنظام الملفات السفلي
STATX_BASIC_STATS, AT_STATX_SYNC_AS_STAT);
if (err)
return err;
//[2] التحقق الجديد في التصحيح من وجود تعيين لـ uid و gid في مساحة الاسم الحالية
if (!kuid_has_mapping(current_user_ns(), ctx.stat.uid) ||
!kgid_has_mapping(current_user_ns(), ctx.stat.gid))
return -EOVERFLOW;
[1] أولاً، يتم الحصول على خصائص الملف الهدف في نظام الملفات السفلي عبر دالة vfs_getattr. هذه الدالة تحصل على بنية struct stat للملف عن طريق تمرير struct path الخاص به.
[1.1] ctx.lowerpath هو مسار ملف في نظام الملفات السفلي ضمن overlay، وسيتم شرح نظام الملفات overlay لاحقًا.
[1.2] بنية struct stat تخزن بيانات وصفية للملف، بما في ذلك المالك والمجموعة. ويتم التحقق من معلومات المالك التي تم الحصول عليها في التحقق الجديد أدناه.
[2] ثم يتم استدعاء دالة kuid_has_mapping للتحقق من معلومات المالك والمجموعة التي تم الحصول عليها أعلاه. تتحقق مما إذا كان مالك الملف الهدف ومجموعته لهما تعيين في مساحة اسم المستخدم الحالية.
[2.1] تستقبل دالة kuid_has_mapping معلمتين: بنية struct user_namespace (مساحة اسم المستخدم) وبنية struct kuid (معرف المستخدم في النواة). تتحقق الدالة مما إذا كانت معلومات المستخدم المعطاة لها تعيين في مساحة اسم المستخدم المعطاة. سيتم شرح تعيين المستخدمين في مساحات الأسماء بالتفصيل لاحقًا.
لذا نعلم الآن أنه عند تنفيذ عملية الدالة المحتوية على الثغرة (ovl_copy_up_one)، إذا لم يكن للمالك أو المجموعة للملف الهدف السفلي تعيين في مساحة الاسم الحالية، فسوف تفشل العملية.
إذن مبدأ التصحيح واضح، ولكن مع ذلك، نحتاج إلى حل الأسئلة التالية لإكمال تكرار الثغرة:
ovl_copy_up_one، أي نسخ الملفات من الطبقة السفلية إلى العلوية في overlay؟lowerpath الذي يتم التحقق من تعيين مالكه في سلسلة المنطق أعلاه؟قبل حل هذين السؤالين، نحتاج إلى فهم بعض المعرفة الأساسية:
(سؤال chatGPT: من فضلك اشرح مساحات الأسماء في نواة لينكس)
في لينكس، مساحات الأسماء (namespaces) هي ميزة في النواة تستخدم لعزل الموارد. من خلال مساحات الأسماء، يمكن لمجموعة من العمليات أن تبدو وكأنها تعمل في بيئة نظام مستقلة، مما يحسن الأمان وإمكانية الإدارة. تلعب مساحات الأسماء دورًا رئيسيًا في تقنيات الحاويات (مثل Docker)، حيث تسمح للحاويات بالعمل في بيئات معزولة دون التأثير على الحاويات الأخرى أو النظام المضيف.
تدعم نواة لينكس 7 أنواع من مساحات الأسماء (mount, pid, net, ipc, user, time, cgroup)، كل منها يعزل فئة محددة من موارد النظام. يتم إنشاء وتعديل وإدارة مساحات الأسماء من خلال سلسلة من استدعاءات النظام (مثل clone و unshare و setns). تستخدم بيئات تشغيل الحاويات (مثل Docker) وأدوات المحاكاة الافتراضية الأخرى ميزات مساحات الأسماء لتوفير بيئات تشغيل مستقلة ومعزولة للحاويات.
من بينها، دالة التحقق الجديدة في التصحيح kuid_has_mapping تتعلق بـ مساحة اسم المستخدم (user namespace) من بين مساحات الأسماء السبعة.
(سؤال chatGPT: من فضلك اشرح مساحة اسم المستخدم)
تستخدم مساحة اسم المستخدم (User Namespace) لعزل معرفات المستخدمين (UID) ومعرفات المجموعات (GID). من خلال مساحة اسم المستخدم، يمكن استخدام مجموعات مستقلة من معرفات المستخدمين والمجموعات في مساحات أسماء مختلفة. هذا يعني أن المستخدمين والمجموعات في مساحة اسم مستخدم قد يكون لديهم معرفات أو صلاحيات مختلفة في مساحة اسم أخرى. يمكن لمساحة اسم المستخدم تحسين أمان النظام وإمكانية الإدارة، خاصة في بيئات الحاويات.
الميزة الرئيسية لمساحة اسم المستخدم هي تعيين المعرفات (ID mapping): تسمح مساحة اسم المستخدم بتعيين UID و GID من مساحة اسم إلى أخرى. هذا يعني أنه في مساحات أسماء مستخدم مختلفة، قد يمثل نفس UID و GID مستخدمين ومجموعات مختلفة. على سبيل المثال، المستخدم root (UID 0) في حاوية قد يتم تعيينه إلى مستخدم غير مميز في النظام المضيف.
نحتاج فقط إلى تذكر النقاط التالية:
على سبيل المثال، أقوم بإنشاء مساحة اسم مستخدم جديدة باستخدام المستخدم breeze، ثم في تلك المساحة أنظر إلى ملف مملوك من قبل root، فيظهر أن المالك هو nobody:

هذا لأنه في مساحة الاسم الجديدة، root هو المستخدم breeze الذي أنشأ المساحة، ولم يتم تعيين root من مساحة الاسم الأولية يدويًا إلى المساحة الجديدة، لذلك يتم التعرف عليه كـ nobody.
إذن هنا نعرف أهمية هذا التصحيح: بالنسبة للملف الهدف في نظام الملفات السفلي من overlay، يجب أن يكون للمالك (المجموعة) الخاص به تعيين في مساحة الاسم الحالية حتى تستمر عملية النسخ، وإلا يتم إرجاع خطأ. أي أن الحالة التي يتم فيها التعرف على المالك على أنه nobody ستؤدي إلى فشل النسخ.
(سؤال chatGPT: من فضلك اشرح نظام الملفات overlay في لينكس)
نظام الملفات Overlay (المعروف أيضًا باسم OverlayFS) هو نظام ملفات افتراضي في نواة لينكس. يسمح بدمج اثنين أو أكثر من هياكل الدلائل الموجودة (تسمى الطبقات "السفلية" و"العلوية") في عرض موحد. يكون نظام الملفات Overlay مفيدًا جدًا عند الحاجة إلى إجراء عمليات كتابة على أنظمة ملفات للقراءة فقط (مثل الصور)، حيث يمكنه توجيه عمليات الكتابة إلى طبقة قابلة للكتابة متراكبة. تستخدم هذه الطريقة على نطاق واسع في تقنيات الحاويات (مثل Docker) لأنها توفر حلاً خفيف الوزن وعالي الأداء لمحاكاة أنظمة الملفات.