CVE-2024-25625: Improper Neutralization of Special Elements in Output Used by a Downstream Component ('Injection') (CWE-74)
value: Pimcore's Admin Classic Bundle provides a Backend UI for Pimcore. A potential security vulnerability has been discovered in `pimcore/admin-ui-classic-bundle` prior to version 1.3.4. The vulnerability involves a Host Header Injection in the `invitationLinkAction` function of the UserController, specifically in the way `$loginUrl` trusts user input.  The host header from incoming HTTP requests is used unsafely when generating URLs. An attacker can manipulate the HTTP host header in requests to the /admin/user/invitationlink endpoint, resulting in the generation of URLs with the attacker's domain. In fact, if a host header is injected in the POST request, the $loginURL parameter is constructed with this unvalidated host header. It is then used to send an invitation email to the provided user. This vulnerability can be used to perform phishing attacks by making the URLs in the invitation links emails point to an attacker-controlled domain. Version 1.3.4 contains a patch for the vulnerability. The maintainers recommend validating the host header and ensuring it matches the application's domain. It would also be beneficial to use a default trusted host or hostname if the incoming host header is not recognized or is absent.
CWE description: The product constructs all or part of a command, data structure, or record using externally-influenced input from an upstream component, but it does not neutralize or incorrectly neutralizes special elements that could modify how it is parsed or interpreted when it is sent to a downstream component.
Published: 2024-02-19T16:15Z
Repository: https://github.com/pimcore/admin-ui-classic-bundle
Language: PHP
Files changed: src/Controller/Admin/UserController.php
Commit date: 2024-02-19 09:32:07 +0100
Commit message: [Security]: Prevent potential host injection in invitation link (#428)


Diff: diff --git a/src/Controller/Admin/UserController.php b/src/Controller/Admin/UserController.php
index e381768818..7a246e6ebc 100644
--- a/src/Controller/Admin/UserController.php
+++ b/src/Controller/Admin/UserController.php
@@ -23,20 +23,22 @@ 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\SystemSettingsConfig;
 use Pimcore\Tool;
 use Symfony\Component\HttpFoundation\File\UploadedFile;
 use Symfony\Component\HttpFoundation\JsonResponse;
 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;
 use Symfony\Component\Routing\Annotation\Route;
 use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
+use Symfony\Component\Routing\RouterInterface;
 use Symfony\Component\Security\Core\Validator\Constraints\UserPassword;
 use Symfony\Component\Validator\Validator\ValidatorInterface;
 use Symfony\Contracts\Translation\TranslatorInterface;
 
 /**
@@ -1158,11 +1160,11 @@ class UserController extends AdminAbstractController implements KernelController
      *
      * @return JsonResponse
      *
      * @throws \Exception
      */
-    public function invitationLinkAction(Request $request, TranslatorInterface $translator): JsonResponse
+    public function invitationLinkAction(Request $request, TranslatorInterface $translator, RouterInterface $router): JsonResponse
     {
         $success = false;
         $message = '';
 
         if ($username = $request->get('username')) {
@@ -1185,10 +1187,19 @@ class UserController extends AdminAbstractController implements KernelController
                     $user->setPassword(bin2hex(random_bytes(16)));
                     $user->save();
                 }
 
                 $token = Tool\Authentication::generateTokenByUser($user);
+
+                $domain = SystemSettingsConfig::get()['general']['domain'];
+                if (!$domain) {
+                    throw new \Exception('No main domain set in system settings, unable to generate login invitation link');
+                }
+
+                $context = $router->getContext();
+                $context->setHost($domain);
+
                 $loginUrl = $this->generateCustomUrl([
                     'token' => $token,
                     'reset' => true,
                 ]);
 

Vulnerable code:     public function invitationLinkAction(Request $request, TranslatorInterface $translator): JsonResponse
Fixed code: use Pimcore\SystemSettingsConfig;
use Symfony\Component\Routing\RouterInterface;
    public function invitationLinkAction(Request $request, TranslatorInterface $translator, RouterInterface $router): JsonResponse

                $domain = SystemSettingsConfig::get()['general']['domain'];
                if (!$domain) {
                    throw new \Exception('No main domain set in system settings, unable to generate login invitation link');
                }

                $context = $router->getContext();
                $context->setHost($domain);