CVE-2020-25026
Description
The TYPO3 sf_event_mgt extension versions before 4.3.1 and 5.x before 5.1.1 have broken access control, exposing participant and event data.
AI Insight
LLM-synthesized narrative grounded in this CVE's description and references.
The TYPO3 sf_event_mgt extension versions before 4.3.1 and 5.x before 5.1.1 have broken access control, exposing participant and event data.
Vulnerability
The sf_event_mgt extension for TYPO3 (Event management and registration) versions before 4.3.1 and 5.x before 5.1.1 suffers from a broken access control vulnerability [1]. The issue lies in the backend module, where insufficient access checks allowed unauthorized users to view participant data and event details, potentially leading to information disclosure.
Exploitation
An attacker with access to the TYPO3 backend (even with limited privileges) could exploit this flaw by directly accessing administrative endpoints that lacked proper permission verification. The commit [3] shows the addition of access checks using BackendUserAuthentication methods, indicating that previously, the backend module did not validate whether the user had the necessary permissions (e.g., being in the correct web mount) to view event or registration data.
Impact
Successful exploitation results in information disclosure, exposing sensitive participant data (such as names, email addresses, and registration details) and event information. This violates confidentiality and could lead to privacy breaches or further targeted attacks.
Mitigation
The vulnerability is fixed in versions 4.3.1 and 5.1.1 of the extension [1]. Users are strongly advised to update immediately. No workaround is available; the update introduces proper access control checks in the backend module.
AI Insight generated on May 21, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
derhansen/sf_event_mgtPackagist | < 4.3.1 | 4.3.1 |
derhansen/sf_event_mgtPackagist | >= 5.0.0, < 5.1.1 | 5.1.1 |
Affected products
2- TYPO3/TYPO3description
Patches
117edcbf608b2[SECURITY] Added access checks to backend module
6 files changed · +124 −8
Classes/Controller/AdministrationController.php+44 −2 modified@@ -18,6 +18,7 @@ use TYPO3\CMS\Backend\Template\Components\ButtonBar; use TYPO3\CMS\Backend\Utility\BackendUtility; use TYPO3\CMS\Backend\View\BackendTemplateView; +use TYPO3\CMS\Core\Authentication\BackendUserAuthentication; use TYPO3\CMS\Core\Imaging\Icon; use TYPO3\CMS\Core\Imaging\IconFactory; use TYPO3\CMS\Core\Localization\LanguageService; @@ -336,9 +337,14 @@ public function listAction(SearchDemand $searchDemand = null, array $overwriteDe $eventDemand->setSearchDemand($searchDemand); $eventDemand->setStoragePage($this->pid); + $events = []; + if ($this->getBackendUser()->isInWebMount($this->pid)) { + $events = $this->eventRepository->findDemanded($eventDemand); + } + $this->view->assignMultiple([ 'pid' => $this->pid, - 'events' => $this->eventRepository->findDemanded($eventDemand), + 'events' => $events, 'searchDemand' => $searchDemand, 'orderByFields' => $this->getOrderByFields(), 'orderDirections' => $this->getOrderDirections(), @@ -353,7 +359,12 @@ public function listAction(SearchDemand $searchDemand = null, array $overwriteDe */ public function exportAction($eventUid) { - $this->exportService->downloadRegistrationsCsv($eventUid, $this->settings['csvExport']); + /** @var Event $event */ + $event = $this->eventRepository->findByUid($eventUid); + if ($event) { + $this->checkEventAccess($event); + $this->exportService->downloadRegistrationsCsv($eventUid, $this->settings['csvExport']); + } exit(); } @@ -381,6 +392,7 @@ public function handleExpiredRegistrationsAction() */ public function indexNotifyAction(Event $event) { + $this->checkEventAccess($event); $customNotification = GeneralUtility::makeInstance(CustomNotification::class); $customNotifications = $this->settingsService->getCustomNotifications($this->settings); $logEntries = $this->customNotificationLogRepository->findByEvent($event); @@ -430,6 +442,7 @@ public function getNotificationRecipients(): array */ public function notifyAction(Event $event, CustomNotification $customNotification) { + $this->checkEventAccess($event); $customNotifications = $this->settingsService->getCustomNotifications($this->settings); $result = $this->notificationService->sendCustomNotification($event, $customNotification, $this->settings); $this->notificationService->createCustomNotificationLogentry( @@ -445,6 +458,26 @@ public function notifyAction(Event $event, CustomNotification $customNotificatio $this->redirect('list'); } + /** + * Checks if the current backend user has access to the PID of the event and if not, enqueue an + * access denied flash message and redirect to list view + * + * @param Event $event + * @throws \TYPO3\CMS\Extbase\Mvc\Exception\StopActionException + */ + public function checkEventAccess(Event $event) + { + if ($this->getBackendUser()->isInWebMount($event->getPid()) === null) { + $this->addFlashMessage( + $this->getLanguageService()->sL(self::LANG_FILE . 'administration.accessdenied.content'), + $this->getLanguageService()->sL(self::LANG_FILE . 'administration.accessdenied.title'), + FlashMessage::ERROR + ); + + $this->redirect('list'); + } + } + /** * Shows the settings error view */ @@ -498,4 +531,13 @@ public function getOrderByFields() 'enddate' => $this->getLanguageService()->sL(self::LANG_FILE . 'administration.orderBy.enddate') ]; } + + /** + * Returns the Backend User + * @return BackendUserAuthentication + */ + protected function getBackendUser(): BackendUserAuthentication + { + return $GLOBALS['BE_USER']; + } }
Classes/Domain/Repository/EventRepository.php+1 −1 modified@@ -144,7 +144,7 @@ protected function setOrderingsFromDemand($query, EventDemand $eventDemand) */ protected function setStoragePageConstraint($query, $eventDemand, &$constraints) { - if ($eventDemand->getStoragePage() && $eventDemand->getStoragePage() !== '') { + if ($eventDemand->getStoragePage() !== null && $eventDemand->getStoragePage() !== '') { $pidList = GeneralUtility::intExplode(',', $eventDemand->getStoragePage(), true); $constraints[] = $query->in('pid', $pidList); }
ext_emconf.php+1 −1 modified@@ -20,7 +20,7 @@ 'uploadfolder' => '0', 'createDirs' => '', 'clearCacheOnLoad' => 1, - 'version' => '5.1.0', + 'version' => '5.1.1', 'constraints' => [ 'depends' => [ 'typo3' => '10.4.2-10.4.99',
Resources/Private/Language/locallang_be.xlf+6 −0 modified@@ -381,6 +381,12 @@ <trans-unit id="administration.orderBy.enddate"> <source>Enddate</source> </trans-unit> + <trans-unit id="administration.accessdenied.title"> + <source>Access Denied</source> + </trans-unit> + <trans-unit id="administration.accessdenied.content"> + <source>Access to the given event has been denied.</source> + </trans-unit> </body> </file> </xliff> \ No newline at end of file
Tests/Functional/Repository/EventRepositoryTest.php+32 −4 modified@@ -71,19 +71,47 @@ public function findRecordsByUid() self::assertSame($events->getTitle(), 'findRecordsByUid'); } + /** + * @return array + */ + public function findDemandedRecordsByStoragePageDataProvider(): array + { + return [ + 'pid is numeric and valid' => [ + 3, + 3 + ], + 'pid is string and valid' => [ + '3', + 3 + ], + 'pid is zero' => [ + 0, + 0 + ], + 'pid not set' => [ + null, + 47 + ] + ]; + } + /** * Test if storagePage restriction in demand works - * + * @dataProvider findDemandedRecordsByStoragePageDataProvider * @test + * @param $pid + * @param $expected + * @throws \TYPO3\CMS\Extbase\Object\Exception */ - public function findDemandedRecordsByStoragePage() + public function findDemandedRecordsByStoragePage($pid, $expected) { /** @var \DERHANSEN\SfEventMgt\Domain\Model\Dto\EventDemand $demand */ $demand = $this->objectManager->get(EventDemand::class); - $demand->setStoragePage(3); + $demand->setStoragePage($pid); $events = $this->eventRepository->findDemanded($demand); - self::assertSame(3, $events->count()); + self::assertSame($expected, $events->count()); } /**
Tests/Unit/Controller/AdministrationControllerTest.php+40 −0 modified@@ -20,6 +20,7 @@ use DERHANSEN\SfEventMgt\Service\MaintenanceService; use DERHANSEN\SfEventMgt\Service\NotificationService; use DERHANSEN\SfEventMgt\Service\SettingsService; +use TYPO3\CMS\Core\Authentication\BackendUserAuthentication; use TYPO3\CMS\Core\Utility\GeneralUtility; use TYPO3\CMS\Extbase\Mvc\Controller\Argument; use TYPO3\CMS\Extbase\Mvc\Controller\Arguments; @@ -100,6 +101,10 @@ public function listActionDoesNotFetchEventsForStoragePidZero() $beUserSessionService->expects(self::any())->method('getSessionDataByKey'); $this->inject($this->subject, 'beUserSessionService', $beUserSessionService); + $mockBackendUser = $this->getMockBuilder(BackendUserAuthentication::class)->getMock(); + $mockBackendUser->expects($this->once())->method('isInWebMount')->will($this->returnValue(1)); + $GLOBALS['BE_USER'] = $mockBackendUser; + $eventRepository = $this->getMockBuilder(EventRepository::class) ->setMethods(['findDemanded']) ->disableOriginalConstructor() @@ -148,6 +153,10 @@ public function listActionDoesNotFetchEventsForStoragePidZeroAndDemand() $beUserSessionService->expects(self::once())->method('saveSessionData'); $this->inject($this->subject, 'beUserSessionService', $beUserSessionService); + $mockBackendUser = $this->getMockBuilder(BackendUserAuthentication::class)->getMock(); + $mockBackendUser->expects($this->once())->method('isInWebMount')->will($this->returnValue(1)); + $GLOBALS['BE_USER'] = $mockBackendUser; + $eventRepository = $this->getMockBuilder(EventRepository::class) ->setMethods(['findDemanded']) ->disableOriginalConstructor() @@ -196,6 +205,10 @@ public function listActionFetchesAllEventsForGivenStoragePidAndAssignsThemToView $beUserSessionService->expects(self::once())->method('saveSessionData'); $this->inject($this->subject, 'beUserSessionService', $beUserSessionService); + $mockBackendUser = $this->getMockBuilder(BackendUserAuthentication::class)->getMock(); + $mockBackendUser->expects($this->once())->method('isInWebMount')->will($this->returnValue(1)); + $GLOBALS['BE_USER'] = $mockBackendUser; + $eventRepository = $this->getMockBuilder(EventRepository::class) ->disableOriginalConstructor() ->getMock(); @@ -240,6 +253,10 @@ public function listActionUsesOverwriteDemandArrayAndAssignsItToView() $beUserSessionService->expects(self::once())->method('saveSessionData'); $this->inject($this->subject, 'beUserSessionService', $beUserSessionService); + $mockBackendUser = $this->getMockBuilder(BackendUserAuthentication::class)->getMock(); + $mockBackendUser->expects($this->once())->method('isInWebMount')->will($this->returnValue(1)); + $GLOBALS['BE_USER'] = $mockBackendUser; + $eventRepository = $this->getMockBuilder(EventRepository::class) ->disableOriginalConstructor() ->getMock(); @@ -382,6 +399,10 @@ public function indexNotifyActionAssignsExpectedObjectsToView() $mockCustomNotification = GeneralUtility::makeInstance(CustomNotification::class); + $mockBackendUser = $this->getMockBuilder(BackendUserAuthentication::class)->getMock(); + $mockBackendUser->expects($this->once())->method('isInWebMount')->will($this->returnValue(1)); + $GLOBALS['BE_USER'] = $mockBackendUser; + $recipients = $this->subject->getNotificationRecipients(); $view = $this->getMockBuilder(ViewInterface::class)->getMock(); @@ -421,10 +442,29 @@ public function notifyActionSendsNotificationsLogsAndRedirects() $mockNotificationService->expects(self::once())->method('createCustomNotificationLogentry'); $this->inject($this->subject, 'notificationService', $mockNotificationService); + $mockBackendUser = $this->getMockBuilder(BackendUserAuthentication::class)->getMock(); + $mockBackendUser->expects($this->once())->method('isInWebMount')->will($this->returnValue(1)); + $GLOBALS['BE_USER'] = $mockBackendUser; + $customNotification = new CustomNotification(); $this->subject->_set('settings', []); $this->subject->expects(self::once())->method('redirect'); $this->subject->notifyAction($event, $customNotification); } + + /** + * @test + */ + public function checkEventAccessRedirectsToListViewIfNoEventAccess() + { + $event = new Event(); + + $mockBackendUser = $this->getMockBuilder(BackendUserAuthentication::class)->getMock(); + $mockBackendUser->expects($this->once())->method('isInWebMount')->will($this->returnValue(null)); + $GLOBALS['BE_USER'] = $mockBackendUser; + + $this->subject->expects(self::once())->method('redirect'); + $this->subject->checkEventAccess($event); + } }
Vulnerability mechanics
Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
7- github.com/advisories/GHSA-g8rg-7rpr-cwr2ghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2020-25026ghsaADVISORY
- github.com/derhansen/sf_event_mgt/commit/17edcbf608b252cc1123e1279f0735f6aa28fef4ghsaWEB
- github.com/derhansen/sf_event_mgt/security/advisories/GHSA-g8rg-7rpr-cwr2ghsaWEB
- packagist.org/packages/derhansen/sf_event_mgtghsaWEB
- typo3.org/help/security-advisoriesghsax_refsource_MISCWEB
- typo3.org/security/advisory/typo3-ext-sa-2020-017ghsax_refsource_CONFIRMWEB
News mentions
0No linked articles in our index yet.