Insufficient Session Expiration in TYPO3 Admin Tool
Description
TYPO3 is an open source web content management system. Prior to versions 9.5.34 ELTS, 10.4.29, and 11.5.11, Admin Tool sessions initiated via the TYPO3 backend user interface had not been revoked even if the corresponding user account was degraded to lower permissions or disabled completely. This way, sessions in the admin tool theoretically could have been prolonged without any limit. TYPO3 versions 9.5.34 ELTS, 10.4.29, and 11.5.11 contain a fix for the problem.
AI Insight
LLM-synthesized narrative grounded in this CVE's description and references.
TYPO3 Admin Tool sessions were not revoked after user account degradation or disablement, enabling prolonged unauthorized access.
Vulnerability
Details
CVE-2022-31050 describes a session management flaw in TYPO3, an open source CMS. Prior to versions 9.5.34 ELTS, 10.4.29, and 11.5.11, Admin Tool sessions initiated via the backend user interface were not revoked when the corresponding user account was degraded to lower permissions or disabled completely [1]. The root cause is a lack of synchronization between the admin tool session and the backend user session; the admin tool did not check the current state of the user account after the session was established [2].
Exploitation
An attacker who already possesses valid backend user credentials can initiate an Admin Tool session. If the user account is later downgraded or disabled (e.g., by an administrator), the existing Admin Tool session remains valid indefinitely. No additional authentication or network position is required beyond the initial backend login [1][3]. The attack surface is the TYPO3 backend user interface, and the prerequisite is that the attacker has or had legitimate backend access.
Impact
Successful exploitation allows an attacker to maintain unauthorized access to the TYPO3 Admin Tool, which provides extensive control over the CMS installation. This could lead to full site compromise, including data exfiltration, code execution, or defacement, even after the user account's permissions have been revoked [1][3].
Mitigation
The vulnerability is fixed in TYPO3 versions 9.5.34 ELTS, 10.4.29, and 11.5.11 [1]. The fix, as shown in the commit [2], synchronizes the admin tool session with the backend user session by storing a hash of the user session identifier and checking it on subsequent requests. Users should upgrade to the patched versions immediately. No workaround is documented.
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 |
|---|---|---|
typo3/cms-corePackagist | >= 9.0.0, < 9.5.35 | 9.5.35 |
typo3/cms-corePackagist | >= 10.0.0, < 10.4.29 | 10.4.29 |
typo3/cms-corePackagist | >= 11.0.0, < 11.5.11 | 11.5.11 |
typo3/cmsPackagist | >= 10.0.0, < 10.4.29 | 10.4.29 |
typo3/cmsPackagist | >= 11.0.0, < 11.5.11 | 11.5.11 |
Affected products
4- osv-coords3 versions
>= 9.0.0, < 9.5.35+ 2 more
- (no CPE)range: >= 9.0.0, < 9.5.35
- (no CPE)range: >= 10.0.0, < 10.4.29
- (no CPE)range: >= 9.0.0, < 9.5.35
Patches
1592387972912[SECURITY] Synchronize admin tools session with backend user session
4 files changed · +153 −4
typo3/sysext/install/Classes/Controller/BackendModuleController.php+2 −1 modified@@ -175,7 +175,8 @@ protected function getBackendUserConfirmationUri(array $parameters): Uri */ protected function setAuthorizedAndRedirect(string $controller): ResponseInterface { - $this->getSessionService()->setAuthorizedBackendSession(); + $userSession = $this->getBackendUser()->getSession(); + $this->getSessionService()->setAuthorizedBackendSession($userSession); $redirectLocation = PathUtility::getAbsoluteWebPath('install.php?install[controller]=' . $controller . '&install[context]=backend'); return new RedirectResponse($redirectLocation, 303); }
typo3/sysext/install/Classes/Middleware/Maintenance.php+15 −0 modified@@ -143,6 +143,21 @@ public function process(ServerRequestInterface $request, RequestHandlerInterface // session related actions $session = new SessionService(); + + // the backend user has an active session but the admin / maintainer + // rights have been revoked or the user was disabled or deleted in the meantime + if ($session->isAuthorizedBackendUserSession() && !$session->hasActiveBackendUserRoleAndSession()) { + // log out the user and destroy the session + $session->resetSession(); + $session->destroySession(); + $formProtection = FormProtectionFactory::get( + InstallToolFormProtection::class + ); + $formProtection->clean(); + + return new HtmlResponse('', 403); + } + if ($actionName === 'preAccessCheck') { $response = new JsonResponse([ 'installToolLocked' => !$this->checkEnableInstallToolFile(),
typo3/sysext/install/Classes/Service/SessionService.php+128 −2 modified@@ -17,8 +17,15 @@ use Symfony\Component\HttpFoundation\Cookie; use TYPO3\CMS\Core\Core\Environment; +use TYPO3\CMS\Core\Database\ConnectionPool; +use TYPO3\CMS\Core\Database\Query\Restriction\DefaultRestrictionContainer; +use TYPO3\CMS\Core\Database\Query\Restriction\RootLevelRestriction; use TYPO3\CMS\Core\Messaging\FlashMessage; use TYPO3\CMS\Core\Security\BlockSerializationTrait; +use TYPO3\CMS\Core\Session\Backend\HashableSessionBackendInterface; +use TYPO3\CMS\Core\Session\Backend\SessionBackendInterface; +use TYPO3\CMS\Core\Session\SessionManager; +use TYPO3\CMS\Core\Session\UserSession; use TYPO3\CMS\Core\SingletonInterface; use TYPO3\CMS\Core\Utility\GeneralUtility; use TYPO3\CMS\Install\Exception; @@ -188,14 +195,28 @@ public function setAuthorized() /** * Marks this session as an "authorized by backend user" one. * This is called by BackendModuleController from backend context. + * + * @param UserSession $userSession session of the current backend user */ - public function setAuthorizedBackendSession() + public function setAuthorizedBackendSession(UserSession $userSession) { + $nonce = bin2hex(random_bytes(20)); + $sessionBackend = $this->getBackendUserSessionBackend(); + // use hash mechanism of session backend, or pass plain value through generic hmac + $sessionHmac = $sessionBackend instanceof HashableSessionBackendInterface + ? $sessionBackend->hash($userSession->getIdentifier()) + : hash_hmac('sha256', $userSession->getIdentifier(), $nonce); + $_SESSION['authorized'] = true; $_SESSION['lastSessionId'] = time(); $_SESSION['tstamp'] = time(); $_SESSION['expires'] = time() + $this->expireTimeInMinutes * 60; $_SESSION['isBackendSession'] = true; + $_SESSION['backendUserSession'] = [ + 'nonce' => $nonce, + 'userId' => $userSession->getUserId(), + 'hmac' => $sessionHmac, + ]; // Renew the session id to avoid session fixation $this->renewSession(); } @@ -222,7 +243,7 @@ public function isAuthorized() * * @return bool TRUE if this session has been authorized before and initialized by a backend system maintainer */ - public function isAuthorizedBackendUserSession() + public function isAuthorizedBackendUserSession(): bool { if (!$this->hasSessionCookie()) { return false; @@ -234,6 +255,49 @@ public function isAuthorizedBackendUserSession() return !$this->isExpired(); } + /** + * Evaluates whether the backend user that initiated this admin tool session, + * has an active role (is still admin & system maintainer) and has an active backend user interface session. + * + * @return bool whether the backend user has an active role and backend user interface session + */ + public function hasActiveBackendUserRoleAndSession(): bool + { + // @see \TYPO3\CMS\Install\Controller\BackendModuleController::setAuthorizedAndRedirect() + $backendUserSession = $this->getBackendUserSession(); + $backendUserRecord = $this->getBackendUserRecord($backendUserSession['userId']); + if ($backendUserRecord === null || empty($backendUserRecord['uid'])) { + return false; + } + $isAdmin = (($backendUserRecord['admin'] ?? 0) & 1) === 1; + $systemMaintainers = array_map('intval', $GLOBALS['TYPO3_CONF_VARS']['SYS']['systemMaintainers'] ?? []); + // stop here, in case the current admin tool session does not belong to a backend user having admin & maintainer privileges + if (!$isAdmin || !in_array((int)$backendUserRecord['uid'], $systemMaintainers, true)) { + return false; + } + + $sessionBackend = $this->getBackendUserSessionBackend(); + foreach ($sessionBackend->getAll() as $sessionRecord) { + $sessionUserId = (int)($sessionRecord['ses_userid'] ?? 0); + // skip, in case backend user id does not match + if ($backendUserSession['userId'] !== $sessionUserId) { + continue; + } + $sessionId = (string)($sessionRecord['ses_id'] ?? ''); + // use persisted hashed `ses_id` directly, or pass through hmac for plain values + $sessionHmac = $sessionBackend instanceof HashableSessionBackendInterface + ? $sessionId + : hash_hmac('sha256', $sessionId, $backendUserSession['nonce']); + // skip, in case backend user session id does not match + if ($backendUserSession['hmac'] !== $sessionHmac) { + continue; + } + // backend user id and session id matched correctly + return true; + } + return false; + } + /** * Check if our session is expired. * Useful only right after a FALSE "isAuthorized" to see if this is the @@ -299,6 +363,20 @@ public function getMessagesAndFlush() return $messages; } + /** + * @return array{userId: int, nonce: string, hmac: string} backend user session references + */ + public function getBackendUserSession(): array + { + if (empty($_SESSION['backendUserSession'])) { + throw new Exception( + 'The backend user session is only available if invoked via the backend user interface.', + 1624879295 + ); + } + return $_SESSION['backendUserSession']; + } + /** * Check if php session.auto_start is enabled * @@ -323,4 +401,52 @@ protected function getIniValueBoolean($configOption) [FILTER_REQUIRE_SCALAR, FILTER_NULL_ON_FAILURE] ); } + + /** + * Fetching a user record with uid=$uid. + * Functionally similar to TYPO3\CMS\Core\Authentication\BackendUserAuthentication::setBeUserByUid(). + * + * @param int $uid The UID of the backend user + * @return array<string, int>|null The backend user record or NULL + */ + protected function getBackendUserRecord(int $uid): ?array + { + $restrictionContainer = GeneralUtility::makeInstance(DefaultRestrictionContainer::class); + $restrictionContainer->add(GeneralUtility::makeInstance(RootLevelRestriction::class, ['be_users'])); + + $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('be_users'); + $queryBuilder->setRestrictions($restrictionContainer); + $queryBuilder->select('uid', 'admin') + ->from('be_users') + ->where($queryBuilder->expr()->eq('uid', $queryBuilder->createNamedParameter($uid, \PDO::PARAM_INT))); + + $resetBeUsersTca = false; + if (!isset($GLOBALS['TCA']['be_users'])) { + // The admin tool intentionally does not load any TCA information at this time. + // The database restictions, needs the enablecolumns TCA information + // for 'be_users' to load the user correctly. + // That is why this part of the TCA ($GLOBALS['TCA']['be_users']['ctrl']['enablecolumns']) + // is simulated. + // The simulation state will be removed later to avoid unexpected side effects. + $GLOBALS['TCA']['be_users']['ctrl']['enablecolumns'] = [ + 'rootLevel' => 1, + 'deleted' => 'deleted', + 'disabled' => 'disable', + 'starttime' => 'starttime', + 'endtime' => 'endtime', + ]; + $resetBeUsersTca = true; + } + $result = $queryBuilder->executeQuery()->fetchAssociative(); + if ($resetBeUsersTca) { + unset($GLOBALS['TCA']['be_users']); + } + + return is_array($result) ? $result : null; + } + + protected function getBackendUserSessionBackend(): SessionBackendInterface + { + return GeneralUtility::makeInstance(SessionManager::class)->getSessionBackend('BE'); + } }
typo3/sysext/install/Tests/Functional/Controller/BackendModuleControllerTest.php+8 −1 modified@@ -19,6 +19,7 @@ use TYPO3\CMS\Backend\Routing\UriBuilder; use TYPO3\CMS\Backend\Template\ModuleTemplateFactory; +use TYPO3\CMS\Core\Authentication\BackendUserAuthentication; use TYPO3\CMS\Core\Core\ApplicationContext; use TYPO3\CMS\Core\Core\Environment; use TYPO3\CMS\Install\Controller\BackendModuleController; @@ -84,7 +85,13 @@ public function environmentContextIsRespectedTest(string $module): void Environment::isWindows() ? 'WINDOWS' : 'UNIX' ); - // Authorized redirect to the install tool is performed, sudo mode is not required + // Authorized redirect to the admin tool is performed + // sudo mode is not required (due to development context) + $GLOBALS['BE_USER'] = new BackendUserAuthentication(); + // using anonymous user session, which is fine for this test case + $GLOBALS['BE_USER']->initializeUserSessionManager(); + $GLOBALS['BE_USER']->user = ['uid' => 1]; + $response = $subject->{$action}(); self::assertEquals(303, $response->getStatusCode()); self::assertNotEmpty($response->getHeader('location'));
Vulnerability mechanics
Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
6- github.com/advisories/GHSA-wwjw-r3gj-39fqghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2022-31050ghsaADVISORY
- github.com/FriendsOfPHP/security-advisories/blob/master/typo3/cms/CVE-2022-31050.yamlghsaWEB
- github.com/TYPO3/typo3/commit/592387972912290c135ebecc91768a67f83a3a4dghsax_refsource_MISCWEB
- github.com/TYPO3/typo3/security/advisories/GHSA-wwjw-r3gj-39fqghsax_refsource_CONFIRMWEB
- typo3.org/security/advisory/typo3-core-sa-2022-005ghsax_refsource_MISCWEB
News mentions
0No linked articles in our index yet.