Skip to content
KitploitKITPLOIT
ToolsExploitsBlog
Log in
Submit
ToolsExploitsBlog
Submit

Hacking, PenTest, and Cybersecurity Tools for Your Security Arsenal!

Kitploit is a directory of hacking, cybersecurity, and pentesting tools. Discover the latest project updates to find vulnerabilities, analyze systems, automate testing, and strengthen your security.

FeedsContactPrivacy© 2026 Kitploit

Tool Directory

Categories

View all categories
Loading categories
drupal-ckeditor-fileisimage — Drupal 11.4.8 - High - Remote - Upload - CKEditor 5 image upload still skips FileIsImage | Kitploit
Tools/GitHubGitHub/abraxas/drupal-ckeditor-fileisimage
Vulnerability AnalysisExploitationWeb Application ExploitationSecurity VirtualizationWeb SecurityPenetration TestingLabs & Practice
GitHub
abraxas/drupal-ckeditor-fileisimage

drupal-ckeditor-fileisimage

Drupal 11.4.8 - High - Remote - Upload - CKEditor 5 image upload still skips FileIsImage

View Repository
33 days agoNot yet reviewed

Most Popular

View all →

Discover the most used tools by our community.

Explore all tools

Browse our collection of tools

View all tools →
Share

Abraxas Labs - drupal-ckeditor-fileisimage

abraxaslabs.tech  ·  github.com/abraxas  ·  @abraxas_null  ·  [email protected]  ·  drupal-ckeditor-fileisimage

drupal-ckeditor-fileisimage

Class: Unrestricted upload / stored XSS Reach: Remote

Drupal core 11.4.8 (61044508) - Drupal

SA-CORE-2026-009 added FileIsImage on JSON:API and REST image fields. CKEditor5ImageController::getImageUploadValidators still returns FileExtension, FileSizeLimit, and FileImageDimensions only. FileImageDimensions returns without a violation when the file is not a valid image. Filename extension is the remaining check.

A user of a text format with CKEditor 5 image upload can POST non-image bytes named x.png to /ckeditor5/upload-image/{editor} and get 201 plus a public files URL.

IDno CVE yet
ClassUnrestricted upload / stored XSS if sniffed
ReachRemote (authenticated format user)
CWECWE-434, CWE-79
CVSSMedium: 5.4 CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:C/C:L/I:L/A:N
ProductDrupal core
Affected11.4.8 (61044508071749629aaf3380aa85971e94df9e84)
Authauthenticated user of a text format with CKEditor 5 image upload (basic_html / full_html on Standard)
LicenseGNU Affero GPL v3.0
Lab127.0.0.1 only

What an attacker can do

Needs an account that can use a format whose editor has image_upload.status true. On Standard that is basic_html and full_html. A complete stranger with no account cannot hit this route (_entity_access: editor.use plus CSRF).

PrivilegeMethod / sinkKindLab-proven or inferred
Write non-image bytes to a public .png under sites/default/files/inline-images/POST /ckeditor5/upload-image/{editor} multipart uploadintegrityLab-proven. x.png returned 201 and url=/sites/default/files/inline-images/x.png
Read those bytes back at the public files URLGET of the JSON urlreconLab-proven. 200, 49 bytes, body contained DRUPAL-CKEDITOR-FILEISIMAGE-WITNESS
Stored XSS in a visitor if the web server or browser sniffs the file as HTML/JSSame public URL, Content-Type not imageintegrity (visitor origin)Inferred. This lab did not change Apache MIME. Drupal stored the bytes.
Upload a real image through the same routeControl control.png 1x1 PNGintendedLab-proven. 201

Closed on this leftover: PHP/RCE via .php (lab-proven 422 FileExtension); unauthenticated upload; JSON:API/REST ImageItem (those paths already add FileIsImage); LPE; a shell on the default image-extension allowlist.

How I found it

I pinned Drupal 11.4.8 after the 2026 SA-CORE wave. JSON:API already adds the missing constraint:

if (\is_a($field_definition->getItemDefinition()->getClass(), ImageItem::class, TRUE) && !array_key_exists('FileIsImage', $validators)) {
  $validators['FileIsImage'] = [];
}

REST FileUploadResource.php does the same. CKEditor 5 was left on filename + size + dimensions:

return [
  'FileExtension' => [
    'extensions' => implode(' ', $allowed_extensions),
  ],
  'FileSizeLimit' => [
    'fileLimit' => $max_filesize,
  ],
  'FileImageDimensions' => [
    'maxDimensions' => $max_dimensions,
  ],
];

FileImageDimensionsConstraintValidator returns without a violation when the file is not a valid image. Hub drupal:11.4 floated to 11.4.9; this pin is composer-exact drupal/recommended-project:11.4.8. 11.4.0 dropped Article from Standard, so the image applies core/recipes/article_content_type after install so /node/add/article loads CKEditor 5.

Wrong turns already recorded: treating FileImageDimensions as a MIME check (it is dimensions on a valid image, and a no-op otherwise); claiming a PHP shell on the default allowlist (.php is 422); using the floating hub tag and watching 11.4.9 come up. Theatre: a reverse shell. The oracle is 201 on fake x.png plus 422 on the same bytes named x.php.

Same pin leftovers: oEmbed second-hop SSRF.

Lab

python3 drupal-ckeditor-fileisimage-Abraxas-Labs.py

or

cd lab
./run.sh

Target only http://127.0.0.1:19500. Live Drupal 11.4.8 Standard on 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

The fix

Add 'FileIsImage' => [] to CKEditor5ImageController::getImageUploadValidators. Keep FileExtension so .php stays 422. Image media types in Media Library should use ImageItem rather than a generic FileItem so they inherit the same constraint.

References

  • github.com/drupal/drupal tag 11.4.8 (61044508071749629aaf3380aa85971e94df9e84)
  • CKEditor5ImageController.php
  • Nearby patched (not this upload): SA-CORE-2026-009
  • Same tag: drupal-oembed-ssrf
  • CWE-434 · CWE-79

License

GNU Affero GPL v3.0. See LICENSE. Loopback lab only. No warranty.

Download Tool