CVE-2023-23937: Unrestricted Upload of File with Dangerous Type (CWE-434)
value: Pimcore is an Open Source Data & Experience Management Platform: PIM, MDM, CDP, DAM, DXP/CMS & Digital Commerce.
The upload functionality for updating user profile does not properly validate the file content-type, allowing any authenticated user to bypass this security check by adding a valid signature (p.e. GIF89) and sending any invalid content-type. This could allow an authenticated attacker to upload HTML files with JS content that will be executed in the context of the domain. This issue has been patched in version 10.5.16. 
CWE description: The product allows the upload or transfer of dangerous file types that are automatically processed within its environment.
CVSS v3 base score: 5.4
Published: 2023-02-03T20:15Z
Repository: https://github.com/pimcore/pimcore
Language: JavaScript
Files changed: bundles/AdminBundle/Controller/Admin/UserController.php, bundles/AdminBundle/Resources/public/js/pimcore/settings/profile/panel.js, bundles/AdminBundle/Resources/public/js/pimcore/settings/user/user/settings.js
Commit date: 2023-02-01 07:32:58 +0100
Commit message: [Task]: Mime type check on Profile Avatar upload (#14125)

* Task: mime type check on avatar upload

* Task: change to use getTypeFromMimeMapping

* Task: add missing import
Diff: diff --git a/bundles/AdminBundle/Controller/Admin/UserController.php b/bundles/AdminBundle/Controller/Admin/UserController.php
index fc6dfb625596..ee7090300913 100644
--- a/bundles/AdminBundle/Controller/Admin/UserController.php
+++ b/bundles/AdminBundle/Controller/Admin/UserController.php
@@ -19,16 +19,18 @@ use Endroid\QrCode\Builder\Builder;
 use Endroid\QrCode\Writer\PngWriter;
 use Pimcore\Bundle\AdminBundle\Controller\AdminController;
 use Pimcore\Bundle\AdminBundle\HttpFoundation\JsonResponse;
 use Pimcore\Controller\KernelControllerEventInterface;
 use Pimcore\Logger;
+use Pimcore\Model\Asset;
 use Pimcore\Model\DataObject;
 use Pimcore\Model\Element;
 use Pimcore\Model\User;
 use Pimcore\Tool;
 use Scheb\TwoFactorBundle\Security\TwoFactor\Provider\Google\GoogleAuthenticatorInterface;
 use Symfony\Component\HttpFoundation\BinaryFileResponse;
+use Symfony\Component\HttpFoundation\File\UploadedFile;
 use Symfony\Component\HttpFoundation\Request;
 use Symfony\Component\HttpFoundation\Response;
 use Symfony\Component\HttpFoundation\Session\Attribute\AttributeBagInterface;
 use Symfony\Component\HttpFoundation\StreamedResponse;
 use Symfony\Component\HttpKernel\Event\ControllerEvent;
@@ -795,10 +797,19 @@ class UserController extends AdminController implements KernelControllerEventInt
 
         if ($userObj->isAdmin() && !$this->getAdminUser()->isAdmin()) {
             throw $this->createAccessDeniedHttpException('Only admin users are allowed to modify admin users');
         }
 
+        //Check if uploaded file is an image
+        $avatarFile = $request->files->get('Filedata');
+
+        $assetType = Asset::getTypeFromMimeMapping($avatarFile->getMimeType(), $avatarFile);
+
+        if (!$avatarFile instanceof UploadedFile || $assetType !== 'image') {
+            throw new \Exception('Unsupported file format.');
+        }
+
         $userObj->setImage($_FILES['Filedata']['tmp_name']);
 
         // set content-type to text/html, otherwise (when application/json is sent) chrome will complain in
         // Ext.form.Action.Submit and mark the submission as failed
 
diff --git a/bundles/AdminBundle/Resources/public/js/pimcore/settings/profile/panel.js b/bundles/AdminBundle/Resources/public/js/pimcore/settings/profile/panel.js
index 31f41d1457a0..247c3f69d197 100644
--- a/bundles/AdminBundle/Resources/public/js/pimcore/settings/profile/panel.js
+++ b/bundles/AdminBundle/Resources/public/js/pimcore/settings/profile/panel.js
@@ -248,10 +248,13 @@ pimcore.settings.profile.panel = Class.create({
                             null,
                             function () {
                                 Ext.getCmp("pimcore_profile_delete_image_" + this.currentUser.id).setVisible(true);
                                 pimcore.helpers.reloadUserImage(this.currentUser.id);
                                 this.currentUser.hasImage = true;
+                            }.bind(this),
+                            function () {
+                                Ext.MessageBox.alert(t('error'), t("unsupported_filetype"));
                             }.bind(this)
                         );
                     }.bind(this)
                 },
                 {
diff --git a/bundles/AdminBundle/Resources/public/js/pimcore/settings/user/user/settings.js b/bundles/AdminBundle/Resources/public/js/pimcore/settings/user/user/settings.js
index 7cff290a69d4..56dacbba3245 100644
--- a/bundles/AdminBundle/Resources/public/js/pimcore/settings/user/user/settings.js
+++ b/bundles/AdminBundle/Resources/public/js/pimcore/settings/user/user/settings.js
@@ -175,10 +175,13 @@ pimcore.settings.user.user.settings = Class.create({
                             null,
                             function () {
                                 Ext.getCmp("pimcore_user_delete_image_" + this.currentUser.id).setVisible(true);
                                 pimcore.helpers.reloadUserImage(this.currentUser.id);
                                 this.currentUser.hasImage = true;
+                            }.bind(this),
+                            function () {
+                                Ext.MessageBox.alert(t('error'), t("unsupported_filetype"));
                             }.bind(this)
                         );
                     }.bind(this)
                 },
                 {

Fixed code: use Pimcore\Model\Asset;
use Symfony\Component\HttpFoundation\File\UploadedFile;
        //Check if uploaded file is an image
        $avatarFile = $request->files->get('Filedata');

        $assetType = Asset::getTypeFromMimeMapping($avatarFile->getMimeType(), $avatarFile);

        if (!$avatarFile instanceof UploadedFile || $assetType !== 'image') {
            throw new \Exception('Unsupported file format.');
        }

                            }.bind(this),
                            function () {
                                Ext.MessageBox.alert(t('error'), t("unsupported_filetype"));
                            }.bind(this),
                            function () {
                                Ext.MessageBox.alert(t('error'), t("unsupported_filetype"));