
CVE-2026-13233 (Drupal OpenAI Provider, SA-CONTRIB-2026-053): SSRF через URL ответа / чтение локальных файлов. Недоверенный источник, не промпт. Безопасный воспроизводитель + обнаружения. Исправлено в версиях 1.1.1/1.2.2.
Ненадёжный upstream: провайдер, который загрузил URL, указанный в его собственном ответе.
Drupal OpenAI Provider (ai_provider_openai) · SSRF (CWE-918) · CVE-2026-13233 · SA-CONTRIB-2026-053
Статус: Скоординированное раскрытие завершено (2026-07-10). Сообщено конфиденциально Команде безопасности Drupal, исправлено и опубликовано как CVE-2026-13233 / уведомление SA-CONTRIB-2026-053 (оценка Умеренно критично). Автор указан как обнаружитель и разработчик исправления.
Исследование классического дефекта авторизации/проверки входных данных в «точке подключения» ИИ: провайдер
генерации изображений ИИ загружал URL, содержащийся в ответе вышестоящего API, используя
функцию общего назначения без разрешённого списка схем. В развёртываниях, где настроенный upstream
не является доверенным по умолчанию (собственная конечная точка / прокси / самостоятельный шлюз / скомпрометированное
реле), ответ мог указывать серверу на file:// или внутренний адрес.
Недоверенный ввод, достигающий стока, — это URL ответа upstream, а не запрос пользователя.
file:// чтение локальных файлов; внутренний анализ автора
также отнёс это к CWE-73 / CWE-441.3.1 / условно 5.3
(Средний); предыдущие 6.5 / 7.4 отозваны, и средняя оценка это подтвердила.ai_provider_openai) < 1.1.1 и 1.2.0–1.2.1 (проверено 1.2.1) на Drupal core 11.2Вся проверка проводилась в собственной, сетевой изолированной песочнице против имитации upstream. Реальный API OpenAI никогда не вызывался. Оракулы утечки были только синтетические.
Подтверждено (динамически продемонстрировано):
settings.php подтверждено sha256 побайтовым совпадением (35,182 Б) — содержимое никогда не выводилось; учётные данные БД
поэтому находятся в области действия.Не проверено (явно не заявлено):
Не затронуто: по умолчанию api.openai.com через TLS. Настройка host контролируется администратором / config-sync / развёртыванием
— атакующий без прав администратора во время выполнения не может её изменить.
URL ответа передавался функции общего назначения, которая учитывает все зарегистрированные обёртки потоков PHP
(file, php, http, data, phar, …). response_format по умолчанию равен url, поэтому уязвимая
ветвь — это путь по умолчанию. file:// является обёрткой файловой системы по умолчанию в PHP и не защищена
allow_url_fopen (ранее заявление «требует allow_url_fopen» было неверным и отозвано).
Исправляйте примитив, а не только место вызова:
http/https перед запросом; отключите перенаправления (30x может перенаправить на file://).file://…, http://169.254.169.254/… и перенаправление 302→file://
должны быть отклонены.Полное направление: docs/fix-direction.md.
.
├── README.md # this file (English, canonical)
├── README_ja.md # Japanese mirror
├── SECURITY.md # reporting policy + responsible-disclosure disclaimer
├── .gitignore # blocks secrets / primary evidence from being committed
├── docs/
│ └── fix-direction.md # root-cause fix + variant hunt + minimal-diff sketch (EN)
├── reproducer/ # SAFE reproducer only (sandbox, mock upstream, benign oracle)
│ ├── README.md # how to stand up the network-isolated sandbox
│ ├── verdict.md # what "success" means: raw-byte match + 200-vs-403 + exit codes
│ ├── mock-openai-server.py # benign mock; default happy-path PNG, ssrf_demo → file:///etc/hostname only
│ └── docker-compose.yml # TEMPLATE: isolated Drupal sandbox + mock (you add the vulnerable target)
├── detections/
│ ├── README.md
│ ├── sigma/web-egress-to-internal-after-imagegen.yml
│ ├── sigma/php-sensitive-file-open.yml
│ ├── sentinel/imagegen-egress-correlation.kql
│ └── splunk/imagegen-content-type-mismatch.spl
└── timeline.md # disclosure timeline (single source of truth)
Документация сейчас на японском (
.ja.md); английские канонические переводы — в планах. Из этого репозитория изъяты: уязвимые нагрузки и любое готовое PoC чтения файлов. Публичныйreproducer/демонстрирует механизм дефекта на имитации с использованием единственного безвредного оракула (/etc/hostname) — без чтения произвольного пути, без захватаsettings.php, без IMDS. Это не оружие против живых сайтов. Обновитесь до 1.1.1 / 1.2.2.
Цель: позволить исследователю подтвердить класс дефекта без предоставления эксплойта для живой цели или раскрытия секретов. Всё работает офлайн.
docker compose с приложением и имитацией upstream в изолированной сети (--network none
для проверок языковых примитивов). Никакие учётные данные не подключаются. Имитация никогда не общается с реальным API.host, указывающий на недоверенный прокси/самостоятельный
шлюз». Она настраивается внеполосно (не через запрос) для возврата синтетического целевого URL — это кодирует
исправленную модель атакующего (upstream выбирает URL, пользователь только запускает).file:///etc/hostname и уникальный маркер для каждого запуска, записанный только внутри контейнера приложения → доказывает,
что приложение прочитало выбранный upstream путь (маркер отсутствует на имитации).settings.php проверено с помощью sha256 побайтового совпадения, содержимое никогда не выводилось → доказывает, что «учётные данные БД находятся в зоне ответственности»
без их раскрытия.0 эксплуатируемо / 1 предусловие /
2 не воспроизведено / ошибка очистки / ошибка). См. .См. reproducer/README.md.
См. timeline.md. Кратко:
Практические детекции находятся в detections/; обоснование в
docs/detection-engineering.md. Кратко:
169.254.169.254 / loopback / RFC1918 в узком временном окне после вызова API. Не срабатывайте только на
исходящем трафике.file:// никогда не попадают в сеть — обнаруживайте открытие чувствительных файлов веб-процессами через аудит хоста
(auditd/EDR), с базовой линией, чтобы срабатывали только нестандартные чтения в рамках запроса генерации изображения.image/*, чьи магические байты не являются изображением.{request_id, actor, target_url, scheme, resolved_ip, content_type}. Только стандартные логи этого не поймают.file:// — обёртка файловой системы по умолчанию; не защищена allow_url_fopen)reproducer/ только для песочницы и использует синтетические оракулы. Это не эксплойт для живой цели и
не содержит секретов. Не запускайте его против систем, которыми вы не владеете и не имеете разрешения тестировать.Все исследования проводились автором в собственной, сетевой изолированной песочнице, против имитации upstream —
реальный API поставщика никогда не использовался, и никакая сторонняя система никогда не тестировалась. Находка была сообщена
конфиденциально сначала Команде безопасности Drupal и опубликована только в синхронизации с исправлением и уведомлением
(SA-CONTRIB-2026-053, CVE-2026-13233); оценка поставщика — Умеренно критично. Показатели CVSS автора
являются предварительной самооценкой, а не официальным баллом, и были намеренно консервативными (без
CVSS Critical/High) — в соответствии со средней оценкой поставщика. Этот материал предназначен для защитных и
образовательных целей. См. SECURITY.md.
399| Дата | Событие |
|---|
| 2026-06-10 | Статический анализ обнаружил сток; динамически подтверждено чтение file:// по фиксированному пути |
| 2026-06-11 | Подтверждено чтение произвольного пути + sha256 совпадение settings.php + внутренний HTTP SSRF |
| 2026-06-11 | Достижимость для производственного потребителя (внутренний путь автомата; FWA легитимный HTTP-exfil) |
| 2026-06-11 | Независимая рецензия сверстников исправила завышенные заявления (модель атакующего, CVSS, allow_url_fopen); отправка поставлена на HOLD |
| 2026-06/07 | Элементы HOLD устранены; сообщено конфиденциально Команде безопасности Drupal; двойник базового класса отмечен для drupal/ai |
| 2026-07-10 | Выпущены исправления 1.1.1 / 1.2.2; опубликованы уведомление SA-CONTRIB-2026-053 и CVE-2026-13233; автор указан как обнаружитель и разработчик исправления |