Drupal 11.4.8 – Hoch – Remote – Upload – CKEditor 5 Bild-Upload umgeht weiterhin FileIsImage
abraxaslabs.tech · github.com/abraxas · @abraxas_null · [email protected] · drupal-ckeditor-fileisimage
Write-up: https://abraxaslabs.tech/research/drupal-ckeditor-fileisimage
Klasse: Unbeschränkter Upload / gespeichertes XSS Reichweite: Remote
Drupal core 11.4.8 (61044508) - Drupal
SA-CORE-2026-009 fügte FileIsImage zu JSON:API- und REST-Bildfeldern hinzu. CKEditor5ImageController::getImageUploadValidators gibt weiterhin nur FileExtension, FileSizeLimit und FileImageDimensions zurück. FileImageDimensions kehrt ohne Verstoß zurück, wenn die Datei kein gültiges Bild ist. Die Dateinamenerweiterung ist die verbleibende Prüfung.
Ein Benutzer eines Textformats mit CKEditor 5-Bildupload kann Nicht-Bild-Bytes namens x.png an /ckeditor5/upload-image/{editor} POSTen und erhält 201 sowie eine öffentliche Datei-URL.
| ID | noch keine CVE |
| Klasse | Unbeschränkter Upload / gespeichertes XSS bei Sniffing |
| Reichweite | Remote (authentifizierter Formatbenutzer) |
| CWE | CWE-434, CWE-79 |
| CVSS | Mittel: 5.4 CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:C/C:L/I:L/A:N |
| Produkt | Drupal core |
| Betroffen | 11.4.8 (61044508071749629aaf3380aa85971e94df9e84) |
| Auth | authentifizierter Benutzer eines Textformats mit CKEditor 5-Bildupload (basic_html / full_html bei Standard) |
| Lizenz | GNU Affero GPL v3.0 |
| Lab | nur 127.0.0.1 |
Erfordert ein Konto, das ein Format verwenden kann, dessen Editor image_upload.status auf true hat. Bei Standard sind das basic_html und full_html. Ein völlig Fremder ohne Konto kann diese Route nicht erreichen (_entity_access: editor.use plus CSRF).
| Berechtigung | Methode / Sink | Art | Im Lab nachgewiesen oder abgeleitet |
|---|---|---|---|
Nicht-Bild-Bytes in eine öffentliche .png unter sites/default/files/inline-images/ schreiben | POST /ckeditor5/upload-image/{editor} multipart upload | Integrität | Im Lab nachgewiesen. x.png lieferte 201 und url=/sites/default/files/inline-images/x.png |
| Diese Bytes über die öffentliche Datei-URL zurücklesen | GET der JSON-url | Recon | Im Lab nachgewiesen. 200, 49 Bytes, Body enthielt DRUPAL-CKEDITOR-FILEISIMAGE-WITNESS |
| Gespeichertes XSS bei einem Besucher, wenn der Webserver oder Browser die Datei als HTML/JS sniffed | Dieselbe öffentliche URL, Content-Type nicht image | Integrität (Besucher-Origin) | Abgeleitet. Dieses Lab änderte Apache MIME nicht. Drupal speicherte die Bytes. |
| Ein echtes Bild über dieselbe Route hochladen | Kontrolle control.png 1x1 PNG | beabsichtigt | Im Lab nachgewiesen. 201 |
Ausgeschlossen bei diesem Rest: PHP/RCE über .php (im Lab nachgewiesen 422 FileExtension); unauthentifizierter Upload; JSON:API/REST ImageItem (diese Pfade fügen bereits FileIsImage hinzu); LPE; eine Shell auf der Standard-Allowlist für Bilddateiendungen.
Ich pinnte Drupal 11.4.8 nach der SA-CORE-Welle 2026. JSON:API fügt die fehlende Constraint bereits hinzu:
if (\is_a($field_definition->getItemDefinition()->getClass(), ImageItem::class, TRUE) && !array_key_exists('FileIsImage', $validators)) {
$validators['FileIsImage'] = [];
}
REST FileUploadResource.php macht dasselbe. CKEditor 5 wurde auf Dateiname + Größe + Abmessungen belassen:
return [
'FileExtension' => [
'extensions' => implode(' ', $allowed_extensions),
],
'FileSizeLimit' => [
'fileLimit' => $max_filesize,
],
'FileImageDimensions' => [
'maxDimensions' => $max_dimensions,
],
];
FileImageDimensionsConstraintValidator gibt ohne Verstoß zurück, wenn die Datei kein gültiges Bild ist. Der Hub drupal:11.4 wurde auf 11.4.9 hochgezogen; dieser Pin ist composer-exakt drupal/recommended-project:11.4.8. 11.4.0 hat Article aus Standard entfernt, daher wendet das Image nach der Installation core/recipes/article_content_type an, damit /node/add/article CKEditor 5 lädt.
Bereits dokumentierte Irrwege: FileImageDimensions als MIME-Prüfung zu behandeln (es prüft Dimensionen auf einem gültigen Bild und ist ansonsten ein No-op); eine PHP-Shell auf der Standard-Allowlist zu behaupten (.php ist 422); das floatende Hub-Tag zu verwenden und zu erleben, wie 11.4.9 auftaucht. Theater: eine Reverse Shell. Das Orakel ist 201 bei einer gefälschten x.png plus 422 bei denselben Bytes unter dem Namen x.php.
Überbleibsel desselben Pins: oEmbed Second-Hop-SSRF.
python3 drupal-ckeditor-fileisimage-Abraxas-Labs.py
oder
cd lab
./run.sh
Ziel nur http://127.0.0.1:19500. Live Drupal 11.4.8 Standard auf 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
Fügen Sie 'FileIsImage' => [] zu CKEditor5ImageController::getImageUploadValidators hinzu. Behalten Sie FileExtension bei, damit .php weiterhin 422 bleibt. Bild-Medientypen in der Medienbibliothek sollten ImageItem anstelle eines generischen FileItem verwenden, damit sie dieselbe Einschränkung erben.
61044508071749629aaf3380aa85971e94df9e84)CKEditor5ImageController.phpGNU Affero GPL v3.0. Siehe LICENSE. Nur Loopback-Lab. Keine Gewährleistung.