GHSA-jwcc-gv4m-93x6: Pimcore has a CustomReports Share Bypass
Summary`CustomReports` uses inconsistent authorization between the report listing endpoint and the report detail endpoint.The listing flow filters reports based on report-sharing rulesThe detail flow only checks generic `reports` or `reports_config` permissionsAs 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 listThe same user was still able to retrieve the report configuration directly by nameRoot CauseThe listing flow in `getReportConfigAction()` filters reports through `loadForGivenUser()`:[[CustomReportController.php](https://github.com/pimcore/pimcore/security/advisories/pimcore-12.3.3/bundles/CustomReportsBundle/src/Controller/Reports/CustomReportController.php#L245)](pimcore-12.3.3/bundles/CustomReportsBundle/src/Controller/Reports/[CustomReportController.php](https://github.com/pimcore/pimcore/security/advisories/pimcore-12.3.3/bundles/CustomReportsBundle/src/Controller/Reports/CustomReportController.php#L252)#L245)[[CustomReportController.php](https://github.com/pimcore/pimcore/security/advisories/pimcore-12.3.3/bundles/CustomReportsBundle/src/Controller/Reports/CustomReportController.php#L253)](pimcore-12.3.3/bundles/CustomReportsBundle/src/Controller/Reports/CustomReportController.php#L252)[CustomReportController.php](pimcore-12.3.3/bundles/CustomReportsBundle/src/Controller/Reports/CustomReportController.php#L253)[[Config/Listing/Dao.php](https://github.com/pimcore/pimcore/security/advisories/pimcore-12.3.3/bundles/CustomReportsBundle/src/Tool/Config/Listing/Dao.php#L44)](pimcore-12.3.3/bundles/CustomReportsBundle/src/Tool/[Config/Listing/Dao.php](https://github.com/pimcore/pimcore/security/advisories/pimcore-12.3.3/bundles/CustomReportsBundle/src/Tool/Config/Listing/Dao.php#L52)#L44)[Config/Listing/Dao.php](pimcore-12.3.3/bundles/CustomReportsBundle/src/Tool/Config/Listing/Dao.php#L52)However, `getAction()` only checks generic permissions and then loads the report directly by name:[[CustomReportController.php](https://github.com/pimcore/pimcore/security/advisories/pimcore-12.3.3/bundles/CustomReportsBundle/src/Controller/Reports/CustomReportController.php#L146)](pimcore-12.3.3/bundles/CustomReportsBundle/src/Controller/Reports/[CustomReportController.php](https://github.com/pimcore/pimcore/security/advisories/pimcore-12.3.3/bundles/CustomReportsBundle/src/Controller/Reports/CustomReportController.php#L149)#L146)[[CustomReportController.php](https://github.com/pimcore/pimcore/security/advisories/pimcore-12.3.3/bundles/CustomReportsBundle/src/Controller/Reports/CustomReportController.php#L151)](pimcore-12.3.3/bundles/CustomReportsBundle/src/Controller/Reports/[CustomReportController.php](https://github.com/pimcore/pimcore/security/advisories/pimcore-12.3.3/bundles/CustomReportsBundle/src/Controller/Reports/CustomReportController.php#L155)#L149)[CustomReportController.php](pimcore-12.3.3/bundles/CustomReportsBundle/src/Controller/Reports/CustomReportController.php#L151)[CustomReportController.php](pimcore-12.3.3/bundles/CustomReportsBundle/src/Controller/Reports/CustomReportController.php#L155)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.ImpactAn attacker can read sensitive report metadata without authorization, including:Report nameGrouping informationDisplay and icon metadataData source configurationColumn configurationSharing settingsFrom the source code, other report endpoints such as `data`, `chart`, `create-csv`, and `download-csv` also resolve reports by name in a similar way:[[CustomReportController.php](https://github.com/pimcore/pimcore/security/advisories/pimcore-12.3.3/bundles/CustomReportsBundle/src/Controller/Reports/CustomReportController.php#L275)](pimcore-12.3.3/bundles/CustomReportsBundle/src/Controller/Reports/[CustomReportController.php](https://github.com/pimcore/pimcore/security/advisories/pimcore-12.3.3/bundles/CustomReportsBundle/src/Controller/Reports/CustomReportController.php#L284)#L275)[[CustomReportController.php](https://github.com/pimcore/pimcore/security/advisories/pimcore-12.3.3/bundles/CustomReportsBundle/src/Controller/Reports/CustomReportController.php#L313)](pimcore-12.3.3/bundles/CustomReportsBundle/src/Controller/Reports/CustomReportController.php#L284)[CustomReportController.php](pimcore-12.3.3/bundles/CustomReportsBundle/src/Controller/Reports/CustomReportController.php#L313)This report only treats unauthorized report-config retrieval as reproduced. The other execution paths should be verified separately.PreconditionsThe attacker is an authenticated backend userThe attacker has the `reports` permissionThe target report is not globally shared and is not shared with that user or the user's rolesPoCReproduction StepsCreate a low-privileged user named `auditor_customreports` with the `reports` permission.Create a report named `poc-secret-report` with:`shareGlobally = false``sharedUserNames = ['admin']`As `auditor_customreports`, request the visible report list and verify that `poc-secret-report` is absent.As the same user, call `getAction(name=poc-secret-report)` directly.Verify that the response still contains the report configuration.Reproduction command:Reproduction ResultRelevant PoC output:This shows that:The current user cannot see the report in the visible report listThe same user can still retrieve the report configuration directlyThis confirms that the share-bypass issue is practically exploitable.Security ImpactUnauthorized disclosure of report configurationDisclosure of sharing scope and internal report structurePotential leakage of data-source and query organization detailsUseful reconnaissance for follow-on unauthorized execution or export pathsRemediationAdd object-level sharing checks to `getAction()` equivalent to `loadForGivenUser()`.Centralize authorization into a single "can current user access this report?" function reused by `get`, `data`, `chart`, `create-csv`, and `download-csv`.Return `403` for unshared reports.Add regression tests to ensure that users with `reports` permission but without report-sharing access cannot retrieve report details.
CVSS v4.0: 7.1