Drupal 11.4.8 - Alta - Remoto - Upload - O upload de imagem do CKEditor 5 ainda ignora FileIsImage
abraxaslabs.tech · github.com/abraxas · @abraxas_null · [email protected] · drupal-ckeditor-fileisimage
Classe: Upload irrestrito / XSS armazenado Alcance: Remoto
Drupal core 11.4.8 (61044508) - Drupal
SA-CORE-2026-009 adicionou FileIsImage em campos de imagem do JSON:API e REST. CKEditor5ImageController::getImageUploadValidators ainda retorna apenas FileExtension, FileSizeLimit e FileImageDimensions. FileImageDimensions retorna sem violação quando o arquivo não é uma imagem válida. A extensão do nome do arquivo é a verificação restante.
Um usuário de um formato de texto com upload de imagem do CKEditor 5 pode enviar bytes que não são imagem com o nome x.png para /ckeditor5/upload-image/{editor} e obter 201 mais uma URL pública de arquivos.
| ID | ainda sem CVE |
| Classe | Upload irrestrito / XSS armazenado se detectado |
| Alcance | Remoto (usuário autenticado do formato) |
| CWE | CWE-434, CWE-79 |
| CVSS | Médio: 5.4 CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:C/C:L/I:L/A:N |
| Produto | Drupal core |
| Afetado | 11.4.8 (61044508071749629aaf3380aa85971e94df9e84) |
| Autenticação | usuário autenticado de um formato de texto com upload de imagem do CKEditor 5 (basic_html / full_html no Standard) |
| Licença | GNU Affero GPL v3.0 |
| Lab | apenas 127.0.0.1 |
Precisa de uma conta que possa usar um formato cujo editor tenha image_upload.status verdadeiro. No Standard, isso é basic_html e full_html. Um completo desconhecido sem conta não consegue acessar essa rota (_entity_access: editor.use mais CSRF).
| Privilégio | Método / sink | Tipo | Comprovado em lab ou inferido |
|---|---|---|---|
Escrever bytes que não são imagem em um .png público sob sites/default/files/inline-images/ | POST /ckeditor5/upload-image/{editor} multipart upload | integridade | Comprovado em lab. x.png retornou 201 e url=/sites/default/files/inline-images/x.png |
| Ler esses bytes de volta na URL pública de arquivos | GET da url do JSON | recon | Comprovado em lab. 200, 49 bytes, corpo continha DRUPAL-CKEDITOR-FILEISIMAGE-WITNESS |
| XSS armazenado em um visitante se o servidor web ou navegador detectar o arquivo como HTML/JS | Mesma URL pública, Content-Type não é imagem | integridade (origem do visitante) | Inferido. Este lab não alterou o MIME do Apache. O Drupal armazenou os bytes. |
| Enviar uma imagem real pela mesma rota | Controle control.png 1x1 PNG | pretendido | Comprovado em lab. 201 |
Fechado neste resquício: PHP/RCE via .php (comprovado em lab 422 FileExtension); upload não autenticado; JSON:API/REST ImageItem (esses caminhos já adicionam FileIsImage); LPE; um shell na lista de permissões padrão de extensões de imagem.
Fixe a versão do Drupal 11.4.8 após a onda de SA-CORE de 2026. O JSON:API já adiciona a restrição ausente:
if (\is_a($field_definition->getItemDefinition()->getClass(), ImageItem::class, TRUE) && !array_key_exists('FileIsImage', $validators)) {
$validators['FileIsImage'] = [];
}
O REST FileUploadResource.php faz o mesmo. O CKEditor 5 ficou com nome de arquivo + tamanho + dimensões:
return [
'FileExtension' => [
'extensions' => implode(' ', $allowed_extensions),
],
'FileSizeLimit' => [
'fileLimit' => $max_filesize,
],
'FileImageDimensions' => [
'maxDimensions' => $max_dimensions,
],
];
FileImageDimensionsConstraintValidator retorna sem violação quando o arquivo não é uma imagem válida. O hub drupal:11.4 flutuou para 11.4.9; este pin é exato no composer drupal/recommended-project:11.4.8. O 11.4.0 removeu o Article do Standard, então a imagem aplica core/recipes/article_content_type após a instalação para que /node/add/article carregue o CKEditor 5.
Caminhos errados já registrados: tratar FileImageDimensions como uma verificação de MIME (é dimensões em uma imagem válida, e um no-op caso contrário); afirmar um shell PHP na lista de permissões padrão (.php é 422); usar a tag flutuante do hub e ver o 11.4.9 aparecer. Teatro: um reverse shell. O oráculo é 201 em x.png falso mais 422 nos mesmos bytes nomeados x.php.
Resquícios do mesmo pin: SSRF de segundo salto do oEmbed.
python3 drupal-ckeditor-fileisimage-Abraxas-Labs.py
ou
cd lab
./run.sh
Alvo apenas http://127.0.0.1:19500. Drupal 11.4.8 Standard ao vivo em 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
Adicione 'FileIsImage' => [] a CKEditor5ImageController::getImageUploadValidators. Mantenha FileExtension para que .php continue 422. Tipos de mídia de imagem na Media Library devem usar ImageItem em vez de um FileItem genérico para que herdem a mesma restrição.
61044508071749629aaf3380aa85971e94df9e84)CKEditor5ImageController.phpGNU Affero GPL v3.0. Veja LICENSE. Apenas lab em loopback. Sem garantia.