Следующая новость
Предыдущая новость

Как подчинить конфиг. Учимся эксплуатировать новую уязвимость в PHP-FPM и Nginx

05.12.2019 22:13
Как подчинить конфиг. Учимся эксплуатировать новую уязвимость в PHP-FPM и Nginx

Содержание статьи

  • Стенд
  • Детали уязвимости
  • Заключение

Недавно мои коллеги обнаружили опасную уязвимость в связке из Nginx и PHP-FPM, которая нередко встречается на веб-серверах. Созданный ими эксплоит позволяет изменить настройки PHP путем внедрения переменных окружения, а цепочка таких настроек приведет к удаленному выполнению кода. Давай разбираться, что же там накосячили разработчики PHP.

Впервые аномальное поведение было обнаружено Андреем @d90pwn Данау во время квалификации Real World CTF 2019. Сервер странно реагировал на отправленный в URL символ перевода строки (%0a). Этой идеей заинтересовались Омар @beched Ганиев и Эмиль @neex Лернер. Эмиль разобрался, почему так происходит, нашел способ эксплуатации и написал рабочий эксплоит, а Омар довел этот баг до получения RCE.

Суть проблемы сводится к тому, что в некоторых конфигурациях FPM злоумышленник может выполнить атаку типа buffer underflow и осуществить запись в адресное пространство, зарезервированное для данных протокола FastCGI. Это позволит выполнять произвольные команды на целевой системе.

Уязвимость получила идентификатор CVE-2019-11043 и провокационное название PHuiP-FPizdaM. Для эксплуатации атакующему не нужно никаких прав, поэтому баг имеет критический статус. Проблема присутствует в обеих ветках PHP — 5 и 7, однако ввиду особенностей оптимизации эксплуатация возможна только в PHP седьмой версии.

INFO

Полный список уязвимых версий:

  • PHP ветки 7.1.x — все версии ниже 7.1.33
  • PHP ветки 7.2.x — все версии ниже 7.2.24
  • PHP ветки 7.3.x — все версии ниже 7.3.11

Стенд

Для начала нам нужен стенд. Я буду использовать уже полюбившиеся контейнеры Docker. Если не хочешь возиться с отладкой, то можно просто запустить готовое окружение с vulhub.

docker-compose.yml
version: '2' services:   nginx:     image: nginx:1     volumes:       - ./www:/usr/share/nginx/html       - ./default.conf:/etc/nginx/conf.d/default.conf     depends_on:       - php     ports:       - "8080:80"   php:     image: php:7.2.10-fpm     volumes:       - ./www:/var/www/html 

Запускается простой командой: docker-compose up -d.

Я собираюсь посмотреть на уязвимость поближе, поэтому будем собирать PHP из исходников. Для начала стартуем Debian.

$ docker run --rm -ti --cap-add=SYS_PTRACE --security-opt seccomp=unconfined --name=phprce --hostname=phprce -p80:80 debian /bin/bash $ apt update 

Теперь позаботимся об установке нужных пакетов.

$ apt install -y build-essential git autoconf automake libtool re2c bison libxml2-dev libgd-dev curl gdb libssl-dev nginx vim nano 

Возьмем последнюю уязвимую версию php — 7.3.10.

$ cd ~ $ git clone --depth=1 --branch PHP-7.3.10 https://github.com/php/php-src.git $ cd php-src 

Сконфигурируем PHP с поддержкой php-fpm.

$ ./buildconf --force $ ./configure --enable-debug --enable-fpm --with-openssl --with-fpm-user="www-data" --with-fpm-group="www-data" 

Теперь дело за компиляцией и установкой. Здесь все стандартно.

$ make $ make install 

Меняем имена стандартных конфигурационных файлов.

$ mv /usr/local/etc/php-fpm.conf.default /usr/local/etc/php-fpm.conf $ mv /usr/local/etc/php-fpm.d/www.conf.default /usr/local/etc/php-fpm.d/www.conf 

Далее изменяем путь до папки, где находятся конфиги.

$ sed -s -i 's/=NONE/=/usr/local/' /usr/local/etc/php-fpm.conf 

В конфиге php-fpm (/usr/local/etc/php-fpm.d/www.conf) настраиваем количество дочерних процессов. Чтобы было проще отлаживать, рекомендую поставить 1.

pm = static pm.max_children = 1 

Теперь дело за файлами конфигурации для Nginx.

/etc/nginx/sites-enabled/default
server {     listen 80 default_server;     listen [::]:80 default_server;      root /var/www/html;      index index.html index.php;      server_name _;      location ~ [^/].php(/|$) {         fastcgi_split_path_info ^(.+?.php)(/.*)$;          include fastcgi_params;         fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;         fastcgi_param PATH_INFO       $fastcgi_path_info;         fastcgi_param PATH_TRANSLATED $document_root$fastcgi_path_info;          fastcgi_pass   127.0.0.1:9000;         fastcgi_index  index.php;     } } 

В fastcgi_pass указан адрес нашего PHP-FPM, по умолчанию он висит на 9000 порту. Некоторые части конфига я поясню в процессе разбора уязвимости.

Затем нужно создать файл PHP в корне веб-сервера (/var/www/html/). Тут подойдет даже пустой скрипт, главное, чтобы он имел расширение .php, и Nginx отправлял его к PHP. Я создал index.php, который выводит приветствие.

/var/www/html/index.php
<?php echo 'Hi there!'; 

Теперь все готово, можно запускать Nginx.

$ service nginx start 

А затем и PHP-FPM через отладчик.

$ gdb --args php-fpm --nodaemonize 

Для GDB включаем возможность отлаживать дочерние процессы и стартуем сервис.

set follow-fork-mode child r 
Как подчинить конфиг. Учимся эксплуатировать новую уязвимость в PHP-FPM и Nginx
Готовый к работе стенд с PHP-FPM и Nginx

Детали уязвимости

Первым делом заглянем в коммит, который патчит уязвимость.

Как подчинить конфиг. Учимся эксплуатировать новую уязвимость в PHP-FPM и Nginx
Коммит, который патчит уязвимость CVE-2019-11043 в PHP 7.3.10
/php-src-php-7.3.10/sapi/fpm/fpm/fpm_main.c
1151: path_info = env_path_info ? env_path_info + pilen - slen : NULL; 1152: tflag = (orig_path_info != path_info); 
/php-src-php-7.3.11/sapi/fpm/fpm/fpm_main.c
1151: path_info = (env_path_info && pilen > slen) ? env_path_info + pilen - slen : NULL; 1152: tflag = path_info && (orig_path_info != path_info); 

Как видишь, добавлены дополнительные проверки для переменных path_info и tflag. В этой части кода происходит обработка путей вида /info.php/test.

Поставим брекпоинт чуть выше запатченных строк, на строке 1143, и попробуем отправить GET-запрос с байтом переноса строки (%0a).

Продолжение доступно только участникам

Материалы из последних выпусков становятся доступны по отдельности только через два месяца после публикации. Чтобы продолжить чтение, необходимо стать участником сообщества «Xakep.ru».

Присоединяйся к сообществу «Xakep.ru»!

Членство в сообществе в течение указанного срока откроет тебе доступ ко ВСЕМ материалам «Хакера», увеличит личную накопительную скидку и позволит накапливать профессиональный рейтинг Xakep Score! Подробнее

1 год

7690 р.

1 месяц

720 р.

Я уже участник «Xakep.ru»

Источник

Последние новости