مقدار صفر برای argc در برنامههای لینوکسی. چرا و چگونه؟
همه چیز از بررسی CVE-2021-4034 و کامپایل مجدد PolKit بر روی Ubuntu 22.04 شروع شد! تصمیم داشتم یک نسخهی آسیبپذیر PolKit رو با فعال کردن Debug Symbols کامپایل کرده و مراحل کامل این CVE رو در GDB بررسی کنم. به صورت خلاصه بگم که این آسیبپذیری در باینری pkexec وجود دارد و به کمک آن میتوان LPE انجام داد. یکی از شرایط استفاده از این آسیبپذیری این است که در زمان اجرای pkexec شرط argc==0 برقرار باشد که از طریق آن متغیرهای محلی خوانده شده و بتوان یک library مخرب را بارگذاری نمود.
از آنجایی که pkexec علاوه بر لینوکس بر روی Solaris, BSD هم قابل استفاده است، در مقالهی اصلی این CVE که توسط Qualys Security منتشر شده است متن زیر مشاهده میشود که از الزام argc==0 برای امکانپذیر بودن این LPE خبر میدهد.
OpenBSD is not exploitable, because its kernel refuses to execve() a program if argc is 0
پس فرض من این بود که در نسخههای اخیر لینوکس هم با کامپایل PolKit باید بتوان این آسیبپذیری را تست کرد. این بود که بر روی Ubuntu 22.04 یک نسخهی آسیبپذیر را کامپایل کرده و یک کد ساده به صورت زیر نوشتم که pkexec را اجرا کرده و argc==0 برقرار باشد.
void main() {
char *args[] = { NULL };
char *envs[] = {"SHELL=/bin/bash", 0};
execve("pkexec", args, envs);
}
با اجرای برنامه و زدن strace مشاهده شد که فراخوانی در سطح user طبق انتظار انجام شد.
execve("pkexec", [], 0x7ffe3883b200 /* 1 var */)
ولی دو تا مورد عجیب رخ داد. اول اینکه برنامه در gdb بر خلاف انتظار با argc==1 اجرا شده و argv[0] که اسم برنامه در آن قرار میگیرد و طبق مدل فراخوانی باید NULL میبود برابر “” شده بود. مورد دومی که عجیب بود پیام زیر در dmesg بود.
process 'exploit' launched 'pkexec' with NULL argv: empty string added
با رسیدن به این مرحله به سراغ Ubuntu 20.04 رفتم و همین کد را بر روی آن اجرا کردم که همه چیز طبق انتظار رخ داده و در gdb با رسیدن به main برنامهی pkexec مقدار argc==0 برقرار بوده و امکان تست CVE وجود داشت. اینجا واضح بود که در کرنلهای جدید لینوکس در فراخوانی سیستمی execve تغییراتی اعمال شده است که جلوی اجرای برنامهها با argc==0 گرفته شود. اینجا دیگه لازم بود کد کرنل چک شود!
با رفتن به github و بررسی فایل fs/exec.c کرنل لینوکس مشاهده شد که در تابع اجرای فراخوانی سیستمی execve کد زیر در March 2022 اضافه شده که جلوی اجرای برنامهها با argc==0 را میگیرد.
/*
* When argv is empty, add an empty string ("") as argv[0] to
* ensure confused userspace programs that start processing
* from argv[1] won't end up walking envp. See also
* bprm_stack_limits().
*/
if (bprm->argc == 0) {
retval = copy_string_kernel("", bprm);
if (retval < 0)
goto out_free;
bprm->argc = 1;
}
پس از این به بعد علاوه بر OpenBSD بر روی لینوکس نیز امکان اجرای آسیبپذیریهای این مدلی وجود نخواهد داشت! :-D
پ.ن: در آینده یک ویدئو از شیوهی کامل اجرای این CVE منتشر میکنم.
#linux #kernel #CVE #PolKit #pkexec #execve