CVE-2024-32871: Allocation of Resources Without Limits or Throttling (CWE-770)
value: Pimcore is an Open Source Data & Experience Management Platform. The Pimcore thumbnail generation can be used to flood the server with large files. By changing the file extension or scaling factor of the requested thumbnail, attackers can create files that are much larger in file size than the original. This vulnerability is fixed in 11.2.4.
CWE description: The product allocates a reusable resource or group of resources on behalf of an actor without imposing any restrictions on the size or number of resources that can be allocated, in violation of the intended security policy for that actor.
CVSS v3 base score: 7.5
Published: 2024-06-04T15:15Z
Repository: https://github.com/pimcore/pimcore
Language: PHP
Files changed: models/Asset/Document/ImageThumbnail.php, models/Asset/Image/Thumbnail.php, models/Asset/Thumbnail/ImageThumbnailTrait.php, models/Asset/Video/ImageThumbnail.php
Commit date: 2024-05-13 15:13:10 +0200
Commit message: Fix thumbnail generation if asset or config is null

Diff: diff --git a/models/Asset/Document/ImageThumbnail.php b/models/Asset/Document/ImageThumbnail.php
index 0c9dc267f1d7..f62a11936a83 100644
--- a/models/Asset/Document/ImageThumbnail.php
+++ b/models/Asset/Document/ImageThumbnail.php
@@ -74,18 +74,19 @@ final class ImageThumbnail implements ImageThumbnailInterface
     /**
      * @throws ThumbnailFormatNotSupportedException
      */
     public function generate(bool $deferredAllowed = true): void
     {
-        if (!$this->checkAllowedFormats($this->config->getFormat(), $this->asset)) {
-            throw new ThumbnailFormatNotSupportedException();
-        }
-
         $deferred = $deferredAllowed && $this->deferred;
         $generated = false;
 
         if ($this->asset && empty($this->pathReference)) {
+
+            if (!$this->checkAllowedFormats($this->config->getFormat(), $this->asset)) {
+                throw new ThumbnailFormatNotSupportedException();
+            }
+
             $config = $this->getConfig();
             $cacheFileStream = null;
             $config->setFilenameSuffix('page-' . $this->page);
 
             try {
diff --git a/models/Asset/Image/Thumbnail.php b/models/Asset/Image/Thumbnail.php
index 50610402adbd..996ac6aac80a 100644
--- a/models/Asset/Image/Thumbnail.php
+++ b/models/Asset/Image/Thumbnail.php
@@ -110,17 +110,11 @@ final class Thumbnail implements ThumbnailInterface
      *
      * @internal
      */
     public function generate(bool $deferredAllowed = true): void
     {
-        if (!$this->checkAllowedFormats($this->config->getFormat(), $this->asset)) {
-            throw new ThumbnailFormatNotSupportedException();
-        }
-
-        if (!$this->checkMaxScalingFactor($this->config->getHighResolution())) {
-            throw new ThumbnailMaxScalingFactorException();
-        }
+        $this->validate();
 
         $deferred = false;
         $generated = false;
 
         if ($this->asset && empty($this->pathReference)) {
@@ -458,6 +452,24 @@ final class Thumbnail implements ThumbnailInterface
             }
         }
 
         return implode(', ', $srcSetValues);
     }
+
+    /**
+     * @throws ThumbnailFormatNotSupportedException
+     * @throws ThumbnailMaxScalingFactorException
+     */
+    private function validate(): void
+    {
+        if(!$this->asset || !$this->config) {
+            return;
+        }
+        if (!$this->checkAllowedFormats($this->config->getFormat(), $this->asset)) {
+            throw new ThumbnailFormatNotSupportedException();
+        }
+
+        if (!$this->checkMaxScalingFactor($this->config->getHighResolution())) {
+            throw new ThumbnailMaxScalingFactorException();
+        }
+    }
 }
diff --git a/models/Asset/Thumbnail/ImageThumbnailTrait.php b/models/Asset/Thumbnail/ImageThumbnailTrait.php
index 2adb8941a7d8..ed11fa500f9c 100644
--- a/models/Asset/Thumbnail/ImageThumbnailTrait.php
+++ b/models/Asset/Thumbnail/ImageThumbnailTrait.php
@@ -432,11 +432,11 @@ trait ImageThumbnailTrait
 
     private function checkAllowedFormats(string $format, ?Asset $asset = null): bool
     {
         $format = strtolower($format);
         if($asset) {
-            $original = pathinfo($asset->getRealFullPath(), PATHINFO_EXTENSION);
+            $original = strtolower(pathinfo($asset->getRealFullPath(), PATHINFO_EXTENSION));
             if ($format === $original || $format === 'source') {
                 return true;
             }
         }
 
diff --git a/models/Asset/Video/ImageThumbnail.php b/models/Asset/Video/ImageThumbnail.php
index 1ccce4708bc0..c44b507501df 100644
--- a/models/Asset/Video/ImageThumbnail.php
+++ b/models/Asset/Video/ImageThumbnail.php
@@ -75,24 +75,25 @@ final class ImageThumbnail implements ImageThumbnailInterface
 
         return $path;
     }
 
     /**
-     * @throws Exception
+     * @throws Exception|\League\Flysystem\FilesystemException|ThumbnailFormatNotSupportedException
      *
      * @internal
      */
     public function generate(bool $deferredAllowed = true): void
     {
-        if (!$this->checkAllowedFormats($this->config->getFormat(), $this->asset)) {
-            throw new ThumbnailFormatNotSupportedException();
-        }
-
         $deferred = $deferredAllowed && $this->deferred;
         $generated = false;
 
         if ($this->asset && empty($this->pathReference)) {
+
+            if (!$this->checkAllowedFormats($this->config->getFormat(), $this->asset)) {
+                throw new ThumbnailFormatNotSupportedException();
+            }
+
             $cs = $this->asset->getCustomSetting('image_thumbnail_time');
             $im = $this->asset->getCustomSetting('image_thumbnail_asset');
 
             if ($im || $this->imageAsset) {
                 if ($this->imageAsset) {

Vulnerable code:         if (!$this->checkAllowedFormats($this->config->getFormat(), $this->asset)) {
            throw new ThumbnailFormatNotSupportedException();
        }

        if (!$this->checkAllowedFormats($this->config->getFormat(), $this->asset)) {
            throw new ThumbnailFormatNotSupportedException();
        }

        if (!$this->checkMaxScalingFactor($this->config->getHighResolution())) {
            throw new ThumbnailMaxScalingFactorException();
        }
            $original = pathinfo($asset->getRealFullPath(), PATHINFO_EXTENSION);
     * @throws Exception
        if (!$this->checkAllowedFormats($this->config->getFormat(), $this->asset)) {
            throw new ThumbnailFormatNotSupportedException();
        }

Fixed code: 
            if (!$this->checkAllowedFormats($this->config->getFormat(), $this->asset)) {
                throw new ThumbnailFormatNotSupportedException();
            }

        $this->validate();

    /**
     * @throws ThumbnailFormatNotSupportedException
     * @throws ThumbnailMaxScalingFactorException
     */
    private function validate(): void
    {
        if(!$this->asset || !$this->config) {
            return;
        }
        if (!$this->checkAllowedFormats($this->config->getFormat(), $this->asset)) {
            throw new ThumbnailFormatNotSupportedException();
        }

        if (!$this->checkMaxScalingFactor($this->config->getHighResolution())) {
            throw new ThumbnailMaxScalingFactorException();
        }
    }
            $original = strtolower(pathinfo($asset->getRealFullPath(), PATHINFO_EXTENSION));
     * @throws Exception|\League\Flysystem\FilesystemException|ThumbnailFormatNotSupportedException

            if (!$this->checkAllowedFormats($this->config->getFormat(), $this->asset)) {
                throw new ThumbnailFormatNotSupportedException();
            }