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는 JSON:API 및 REST 이미지 필드에 FileIsImage를 추가했습니다. CKEditor5ImageController::getImageUploadValidators는 여전히 FileExtension, FileSizeLimit, FileImageDimensions만 반환합니다. FileImageDimensions는 파일이 유효한 이미지가 아닐 때 위반 없이 반환됩니다. 남은 검사는 파일 이름 확장자뿐입니다.
CKEditor 5 이미지 업로드를 사용하는 텍스트 포맷의 사용자는 x.png라는 이름의 비이미지 바이트를 /ckeditor5/upload-image/{editor}에 POST하여 201과 공개 파일 URL을 받을 수 있습니다.
| ID | 아직 CVE 없음 |
| 분류 | 무제한 업로드 / 스니핑될 경우 저장형 XSS |
| 도달 범위 | 원격 (인증된 포맷 사용자) |
| CWE | CWE-434, CWE-79 |
| CVSS | Medium: 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 이미지 업로드를 사용하는 텍스트 포맷의 인증된 사용자 (Standard에서 basic_html / full_html) |
| 라이선스 | GNU Affero GPL v3.0 |
| 랩 | 127.0.0.1 전용 |
편집기에서 image_upload.status가 true인 포맷을 사용할 수 있는 계정이 필요합니다. Standard에서는 basic_html과 full_html입니다. 계정이 없는 완전한 외부인은 이 라우트에 접근할 수 없습니다 (_entity_access: editor.use 및 CSRF).
| 권한 | 메서드 / 싱크 | 종류 | 랩 입증 또는 추론 |
|---|---|---|---|
sites/default/files/inline-images/ 아래 공개 .png에 비이미지 바이트 쓰기 | POST /ckeditor5/upload-image/{editor} multipart upload | 무결성 | 랩 입증. x.png가 201과 url=/sites/default/files/inline-images/x.png 반환 |
| 공개 파일 URL에서 해당 바이트 다시 읽기 | JSON url에 대한 GET | 정찰 | 랩 입증. 200, 49바이트, 본문에 DRUPAL-CKEDITOR-FILEISIMAGE-WITNESS 포함 |
| 웹 서버나 브라우저가 파일을 HTML/JS로 스니핑할 경우 방문자에 대한 저장형 XSS | 동일한 공개 URL, Content-Type이 이미지가 아님 | 무결성 (방문자 오리진) | 추론. 이 랩은 Apache MIME을 변경하지 않았습니다. Drupal이 바이트를 저장했습니다. |
| 동일한 라우트를 통해 실제 이미지 업로드 | 대조군 control.png 1x1 PNG | 의도된 동작 | 랩 입증. 201 |
이 잔여 항목에서 제외된 것: .php를 통한 PHP/RCE (랩 입증 422 FileExtension); 비인증 업로드; JSON:API/REST ImageItem (해당 경로는 이미 FileIsImage를 추가함); LPE; 기본 이미지 확장자 허용 목록에서의 셸.
2026 SA-CORE 물결 이후 Drupal 11.4.8을 고정했습니다. 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는 파일이 유효한 이미지가 아닐 때 위반 없이 반환됩니다. Hub drupal:11.4는 11.4.9로 떠올랐고, 이 고정은 composer-exact drupal/recommended-project:11.4.8입니다. 11.4.0에서 Standard에서 Article이 제거되었으므로, 이미지는 설치 후 core/recipes/article_content_type을 적용하여 /node/add/article이 CKEditor 5를 로드하도록 합니다.
이미 기록된 잘못된 방향: FileImageDimensions를 MIME 검사로 취급 (이는 유효한 이미지에 대한 크기 제한이며, 그렇지 않으면 무동작); 기본 허용 목록에서 PHP 셸을 주장 (.php는 422); 떠다니는 hub 태그를 사용하여 11.4.9가 나타나는 것을 관찰. 극적 요소: 리버스 셸. 오라클은 가짜 x.png에 대한 201과 동일한 바이트를 x.php로 명명했을 때의 422입니다.
동일 고정의 잔여 항목: oEmbed second-hop SSRF.
python3 drupal-ckeditor-fileisimage-Abraxas-Labs.py
또는
cd lab
./run.sh
오직 http://127.0.0.1:19500만 대상으로 합니다. php:8.4-apache-trixie에서 실행되는 라이브 Drupal 11.4.8 Standard, 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
CKEditor5ImageController::getImageUploadValidators에 'FileIsImage' => []를 추가하세요. .php가 422로 유지되도록 FileExtension을 유지하세요. Media Library의 이미지 미디어 타입은 일반 FileItem 대신 ImageItem을 사용하여 동일한 제약 조건을 상속해야 합니다.
61044508071749629aaf3380aa85971e94df9e84)CKEditor5ImageController.phpGNU Affero GPL v3.0. LICENSE 참조. 루프백 랩 전용. 보증 없음.