
Эксплойт для получения root-прав через CVE-2021-4034 (PwnKit), использующий запись за пределами границ в pkexec для повышения привилегий до root в Linux-системах.
Root-эксплойт для уязвимости 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;
+ /* Disable remote file access from GIO. */
+ setenv ("GIO_USE_VFS", "local", 1);
+
/* check for correct invocation */
if (geteuid () != 0)
{
Версии до этого коммита эксплуатируются без необходимости задавать
переменную GIO_USE_VFS. Версии после — не эксплуатируются, если эта переменная не задана.
Назначение коммита на самом деле — ложный след. Дело не в том, что означает переменная, а в том, как её наличие влияет на окружение программы. За истиной нужно обратиться к libc.
Окружение процесса в libc представлено массивом char *,
на который указывает эта глобальная переменная:
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;
/* We allocated this space; we can extend it. */
new_environ = (char **) realloc (last_environ,
(size + 2) * sizeof (char *));
// ... пропущено
last_environ = __environ = new_environ;
}
__add_to_environ() вызывается как setenv(3), так и putenv(3) для выполнения
того, что они обещают — установки переменной окружения. Если переменная окружения в вопросе не определена, environ приходится перераспределять, чтобы вместить
новую запись (указатель на новую пару ключ=значение). Если она
определена, размер массива environ не изменился, и поэтому нет
причины для перераспределения. Для краткости я опустил эту часть кода — призываю вас изучить её самостоятельно.
Теперь вернёмся к эксплойту. Если вы дошли до этого места, вы, вероятно,
уже знаете методологию этого эксплойта (если нет, пожалуйста, ознакомьтесь с
оригинальным отчётом).
Мы пытаемся протащить переменную окружения, передавая пустые аргументы программы
(argv) в pkexec. Когда argc действительно пуст (даже без имени программы),
переменные окружения, которые находятся рядом, сталкиваются с аргументами.
Мы злоупотребляем этим поведением, чтобы заставить pkexec записать канонический путь
целевого исполняемого файла в окружение. Однако прежде чем мы доберёмся до этой части
кода, происходит вот что:
setenv ("GIO_USE_VFS", "local", 1);
Если эта переменная отсутствует в окружении, environ будет
перераспределён и, таким образом, никогда не столкнётся с argv. В результате
запись за пределами границ не будет влиять на окружение программы, из-за чего
эксплойт завершится неудачей.