Drupal 11.4.8 — высокий уровень — удалённо — загрузка — загрузка изображений в CKEditor 5 по-прежнему пропускает FileIsImage
abraxaslabs.tech · github.com/abraxas · @abraxas_null · [email protected] · drupal-ckeditor-fileisimage
Класс: Неограниченная загрузка файлов / хранимая XSS Достижимость: Удалённая
Drupal core 11.4.8 (61044508) - Drupal
SA-CORE-2026-009 добавил FileIsImage для полей изображений в JSON:API и REST. CKEditor5ImageController::getImageUploadValidators по-прежнему возвращает только FileExtension, FileSizeLimit и FileImageDimensions. FileImageDimensions возвращает результат без нарушения, когда файл не является допустимым изображением. Расширение имени файла остаётся единственной проверкой.
Пользователь текстового формата с загрузкой изображений CKEditor 5 может отправить POST с не-изображением под именем x.png на /ckeditor5/upload-image/{editor} и получить 201 плюс публичный URL файла.
| ID | CVE пока нет |
| Класс | Неограниченная загрузка / хранимая XSS при сниффинге |
| Достижимость | Удалённая (аутентифицированный пользователь формата) |
| CWE | CWE-434, CWE-79 |
| CVSS | Средний: 5.4 CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:C/C:L/I:L/A:N |
| Продукт | Drupal core |
| Затронуто | 11.4.8 (61044508071749629aaf3380aa85971e94df9e84) |
| Аутентификация | аутентифицированный пользователь текстового формата с загрузкой изображений CKEditor 5 (basic_html / full_html в Standard) |
| Лицензия | GNU Affero GPL v3.0 |
| Лаборатория | только 127.0.0.1 |
Требуется учётная запись, которая может использовать формат, у которого у редактора image_upload.status имеет значение true. В Standard это basic_html и full_html. Полностью посторонний без учётной записи не может обратиться к этому маршруту (_entity_access: editor.use плюс CSRF).
| Привилегия | Метод / точка срабатывания | Тип | Доказано в лаборатории или выведено |
|---|---|---|---|
Запись не-изображения в публичный .png в sites/default/files/inline-images/ | POST /ckeditor5/upload-image/{editor} multipart upload | целостность | Доказано в лаборатории. x.png вернул 201 и url=/sites/default/files/inline-images/x.png |
| Чтение этих байтов обратно по публичному URL файла | GET по JSON url | разведка | Доказано в лаборатории. 200, 49 байт, тело содержало DRUPAL-CKEDITOR-FILEISIMAGE-WITNESS |
| Хранимая XSS у посетителя, если веб-сервер или браузер сниффит файл как HTML/JS | Тот же публичный URL, Content-Type не image | целостность (origin посетителя) | Выведено. В этой лаборатории Apache MIME не менялся. Drupal сохранил байты. |
| Загрузка настоящего изображения через тот же маршрут | Контрольный control.png 1x1 PNG | предполагаемое поведение | Доказано в лаборатории. 201 |
Закрыто в этом остатке: PHP/RCE через .php (доказано в лаборатории 422 FileExtension); неаутентифицированная загрузка; JSON:API/REST ImageItem (эти пути уже добавляют FileIsImage); LPE; шелл по стандартному списку разрешённых расширений изображений.
Я зафиксировал Drupal 11.4.8 после волны SA-CORE 2026. JSON:API уже добавляет недостающее ограничение:
if (\is_a($field_definition->getItemDefinition()->getClass(), ImageItem::class, TRUE) && !array_key_exists('FileIsImage', $validators)) {
$validators['FileIsImage'] = [];
}
REST FileUploadResource.php делает то же самое. CKEditor 5 остался на имени файла + размере + размерах:
return [
'FileExtension' => [
'extensions' => implode(' ', $allowed_extensions),
],
'FileSizeLimit' => [
'fileLimit' => $max_filesize,
],
'FileImageDimensions' => [
'maxDimensions' => $max_dimensions,
],
];
FileImageDimensionsConstraintValidator возвращает результат без нарушения, когда файл не является допустимым изображением. Тег хаба drupal:11.4 перешёл на 11.4.9; эта фиксация — composer-точная drupal/recommended-project:11.4.8. В 11.4.0 Article был удалён из Standard, поэтому образ применяет core/recipes/article_content_type после установки, чтобы /node/add/article загружал CKEditor 5.
Уже зафиксированные ошибочные пути: трактовка FileImageDimensions как проверки MIME (это размеры для допустимого изображения и no-op в противном случае); утверждение о PHP-шелле по стандартному списку разрешённых расширений (.php даёт 422); использование плавающего тега хаба и наблюдение появления 11.4.9. Театр: обратный шелл. Оракул — 201 на поддельном x.png плюс 422 на тех же байтах под именем x.php.
Остатки той же фиксации: oEmbed second-hop SSRF.
python3 drupal-ckeditor-fileisimage-Abraxas-Labs.py
или
cd lab
./run.sh
Цель только http://127.0.0.1:19500. Живой Drupal 11.4.8 Standard на php:8.4-apache-trixie, SQLite.
SUCCESS drupal-ckeditor-fileisimage png-http=201 php-http=422 control-png-http=201 editor=basic_html stored-http=200 stored-has-witness=yes url=/sites/default/files/inline-images/x.png DRUPAL-CKEDITOR-FILEISIMAGE-WITNESS
Добавьте 'FileIsImage' => [] в CKEditor5ImageController::getImageUploadValidators. Оставьте FileExtension, чтобы .php оставался 422. Медиа-типы изображений в Media Library должны использовать ImageItem, а не общий FileItem, чтобы они наследовали то же ограничение.
61044508071749629aaf3380aa85971e94df9e84)CKEditor5ImageController.phpGNU Affero GPL v3.0. См. LICENSE. Только loopback-лаборатория. Без гарантий.