CVE-2026-42321
Description
GLPI versions 10.0.4 to 10.0.24 are vulnerable to stored XSS in the asset locked tab, allowing technicians to inject malicious scripts.
AI Insight
LLM-synthesized narrative grounded in this CVE's description and references.
GLPI versions 10.0.4 to 10.0.24 are vulnerable to stored XSS in the asset locked tab, allowing technicians to inject malicious scripts.
Vulnerability
Starting in version 10.0.4 and prior to version 10.0.25, GLPI is vulnerable to a stored Cross-Site Scripting (XSS) attack. A technician can store an XSS payload in the asset locked tab. This vulnerability affects GLPI versions 10.0.4 through 10.0.24 [1].
Exploitation
An attacker with technician privileges can exploit this vulnerability by storing an XSS payload within the asset locked tab. No other specific prerequisites or complex steps are detailed in the available references [1].
Impact
Successful exploitation allows an attacker to execute arbitrary JavaScript in the context of another user's browser. This could lead to session hijacking, data theft, or further malicious actions within the GLPI application, depending on the payload used [1].
Mitigation
GLPI versions 10.0.25 and 11.0.7 contain a patch for this vulnerability. Users are advised to upgrade to these versions or later. No workarounds are mentioned in the available references [1].
AI Insight generated on Jun 3, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.
Affected products
2>=10.0.4,<10.0.25+ 1 more
- (no CPE)range: >=10.0.4,<10.0.25
- (no CPE)range: >=10.0.4,<10.0.25
Patches
2ac57d2f17727Encrypt API session tokens (#23809)
3 files changed · +12 −2
src/Api/API.php+4 −1 modified@@ -336,9 +336,12 @@ protected function initSession($params = []) $this->returnError($error_msg, 401, $error_code, false); } + $session_token = \base64_encode((new GLPIKey())->encrypt($_SESSION['valid_id'])); + unset($_SESSION['valid_id']); // this is not needed for the API, unsetting it prevents any unexpected exposure + // stop session and return session key session_write_close(); - $data = ['session_token' => $_SESSION['valid_id']]; + $data = ['session_token' => $session_token]; // Insert session data if requested $get_full_session = $params['get_full_session'] ?? false;
src/Api/APIRest.php+2 −1 modified@@ -40,6 +40,7 @@ namespace Glpi\Api; use AllAssets; +use GLPIKey; use GLPIUploadHandler; use stdClass; use Toolbox; @@ -601,7 +602,7 @@ public function parseIncomingParams($is_inline_doc = false) // try to retrieve session_token in header if (isset($headers['Session-Token'])) { - $parameters['session_token'] = $headers['Session-Token']; + $parameters['session_token'] = (new GLPIKey())->decrypt(\base64_decode(trim($headers['Session-Token']))); } // try to retrieve app_token in header
src/Api/APIXmlrpc.php+6 −0 modified@@ -35,6 +35,7 @@ namespace Glpi\Api; +use GLPIKey; use Toolbox; class APIXmlrpc extends API @@ -269,6 +270,11 @@ public function parseIncomingParams() ? $parameters[0] : []); + // decrypt session token + if (\array_key_exists('session_token', $this->parameters)) { + $this->parameters['session_token'] = (new GLPIKey())->decrypt(\base64_decode($this->parameters['session_token'])); + } + // transform input from array to object if ( isset($this->parameters['input'])
e035590cb691Prevent conflicts between legacy API auth methods (#23811)
3 files changed · +137 −54
apirest.md+4 −0 modified@@ -1748,6 +1748,10 @@ One of theses parameter(s) is missing: The GLPI setup forbid the login with credentials, you must login with your user_token instead. See your personal preferences page or setup API access in GLPI main interface. +### ERROR_LOGIN_WITH_TOKEN_DISABLED + +The GLPI setup forbid the login with user token, you must login with your login/password instead. + ### ERROR_GLPI_LOGIN_USER_TOKEN The provided user_token seems invalid.
phpunit/APIBaseClass.php+74 −14 modified@@ -52,13 +52,6 @@ public function setUp(): void global $GLPI_CACHE; $GLPI_CACHE->clear(); - $this->initSessionCredentials(); - } - - abstract public function initSessionCredentials(); - - public static function setUpBeforeClass(): void - { // To bypass various right checks // This is mandatory to create/update/delete some items during tests. $_SESSION['glpishowallentities'] = 1; @@ -70,15 +63,20 @@ public static function setUpBeforeClass(): void $_SESSION['glpicronuserrunning'] = "cron_phpunit"; // enable api config - $config = new Config(); - $config->update([ - 'id' => 1, - 'enable_api' => true, - 'enable_api_login_credentials' => true, - 'enable_api_login_external_token' => true, - ]); + Config::setConfigurationValues( + 'core', + [ + 'enable_api' => true, + 'enable_api_login_credentials' => true, + 'enable_api_login_external_token' => true, + ] + ); + + $this->initSessionCredentials(); } + abstract public function initSessionCredentials(); + /** * @tags api * @covers API::initSession @@ -99,6 +97,68 @@ public function testInitSessionUserToken() $this->assertArrayHasKey('session_token', $data); } + /** + * @tags api + * @covers API::initSession + */ + public function testInitSessionUserTokenFailIfNotEnabled() + { + Config::setConfigurationValues( + 'core', + [ + 'enable_api_login_external_token' => false, + ] + ); + + $user = new User(); + $uid = getItemByTypeName('User', TU_USER, true); + $this->assertTrue($user->getFromDB($uid)); + $token = $user->getAuthToken('api_token'); + + $this->query( + 'initSession', + ['query' => ['user_token' => $token]], + 400, + 'ERROR_LOGIN_WITH_TOKEN_DISABLED' + ); + } + + /** + * @tags api + * @covers API::initSession + */ + public function testInitSessionCredentialsFailIfNotEnabled() + { + Config::setConfigurationValues( + 'core', + [ + 'enable_api_login_credentials' => false, + ] + ); + + $this->query( + 'initSession', + ['query' => ['login' => TU_USER, 'password' => TU_PASS]], + 400, + 'ERROR_LOGIN_WITH_CREDENTIALS_DISABLED' + ); + } + + /** + * @tags api + * @covers API::initSession + */ + public function testInitSessionFallbackToCredentials() + { + $data = $this->query( + 'initSession', + ['query' => ['user_token' => 'notvalid', 'login' => TU_USER, 'password' => TU_PASS]], + ); + + $this->assertNotFalse($data); + $this->assertArrayHasKey('session_token', $data); + } + /** * @tags api * @covers API::initSession
src/Api/API.php+59 −40 modified@@ -263,58 +263,77 @@ protected function initSession($params = []) $this->checkAppToken(); $this->logEndpointUsage(__FUNCTION__); - if ( - (!isset($params['login']) - || empty($params['login']) - || !isset($params['password']) - || empty($params['password'])) - && (!isset($params['user_token']) - || empty($params['user_token'])) - ) { + $login = $params['login'] ?? ''; + $password = $params['password'] ?? ''; + $token = $params['user_token'] ?? ''; + + if (($login !== '' || $password !== '') && !$CFG_GLPI['enable_api_login_credentials']) { $this->returnError( - __("parameter(s) login, password or user_token are missing"), + __("usage of initSession resource with credentials is disabled"), 400, - "ERROR_LOGIN_PARAMETERS_MISSING" + "ERROR_LOGIN_WITH_CREDENTIALS_DISABLED", + false ); } - - $auth = new Auth(); - - // fill missing params (in case of user_token) - if (!isset($params['login'])) { - $params['login'] = ''; - } - if (!isset($params['password'])) { - $params['password'] = ''; - } - - $noAuto = true; - if (isset($params['user_token']) && !empty($params['user_token'])) { - $_REQUEST['user_token'] = Sanitizer::dbEscape($params['user_token']); - $noAuto = false; - } elseif (!$CFG_GLPI['enable_api_login_credentials']) { + if ($token !== '' && !$CFG_GLPI['enable_api_login_external_token']) { $this->returnError( - __("usage of initSession resource with credentials is disabled"), + __("usage of initSession resource with user token is disabled"), 400, - "ERROR_LOGIN_WITH_CREDENTIALS_DISABLED", + "ERROR_LOGIN_WITH_TOKEN_DISABLED", false ); } - if (!isset($params['auth'])) { - $params['auth'] = ''; + if ( + (!$CFG_GLPI['enable_api_login_credentials'] || $login === '' || $password === '') + && (!$CFG_GLPI['enable_api_login_external_token'] || $token === '') + ) { + if ($CFG_GLPI['enable_api_login_credentials'] && $CFG_GLPI['enable_api_login_external_token']) { + $this->returnError( + __("parameter(s) login, password or user_token are missing"), + 400, + "ERROR_LOGIN_PARAMETERS_MISSING" + ); + } elseif ($CFG_GLPI['enable_api_login_credentials']) { + $this->returnError( + __("parameter(s) login, password are missing"), + 400, + "ERROR_LOGIN_PARAMETERS_MISSING" + ); + } else { + $this->returnError( + __("parameter user_token is missing"), + 400, + "ERROR_LOGIN_PARAMETERS_MISSING" + ); + } } - // login on glpi - if (!$auth->login($params['login'], $params['password'], $noAuto, false, $params['auth'])) { - $err = implode(' ', $auth->getErrors()); - if ( - isset($params['user_token']) - && !empty($params['user_token']) - ) { - $this->returnError(__("parameter user_token seems invalid"), 401, "ERROR_GLPI_LOGIN_USER_TOKEN", false); - } - $this->returnError($err, 401, "ERROR_GLPI_LOGIN", false); + $authenticated = false; + $error_msg = ''; + $error_code = ''; + $use_token_auth = $CFG_GLPI['enable_api_login_external_token'] && $token !== ''; + $use_login_auth = $CFG_GLPI['enable_api_login_credentials'] && $login !== '' && $password !== ''; + + if ($use_token_auth) { + $_REQUEST['user_token'] = $token; + + $auth = new Auth(); + $authenticated = $auth->login('', '', false, false, $params['auth'] ?? ''); + $error_code = 'ERROR_GLPI_LOGIN_USER_TOKEN'; + $error_msg = __("parameter user_token seems invalid"); + } + if (!$authenticated && $use_login_auth) { + unset($_REQUEST['user_token']); + + $auth = new Auth(); + $authenticated = $auth->login($login, $password, true, false, $params['auth'] ?? ''); + $error_code = 'ERROR_GLPI_LOGIN'; + $error_msg = implode(' ', $auth->getErrors()); + } + + if (!$authenticated) { + $this->returnError($error_msg, 401, $error_code, false); } // stop session and return session key
Vulnerability mechanics
Root cause
"The API session token was not properly encrypted, allowing for manipulation."
Attack vector
A technician with access to GLPI can store an XSS payload within the asset locked tab. This payload can then be triggered when the asset is accessed via the API. The vulnerability is present in versions prior to 10.0.25 and 11.0.7.
Affected code
The vulnerability is related to the handling of API session tokens in `src/Api/APIXmlrpc.php`, `src/Api/API.php`, and `src/Api/APIRest.php`.
What the fix does
The patch encrypts API session tokens using a GLPI key before they are transmitted and decrypts them upon receipt. This prevents unauthorized modification or injection of malicious data into the session token. The changes are applied to `src/Api/APIXmlrpc.php`, `src/Api/API.php`, and `src/Api/APIRest.php` [patch_id=4683548].
Preconditions
- authThe attacker must be a technician with access to GLPI.
- inputThe attacker must be able to store an XSS payload in the asset locked tab.
Generated on Jun 3, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
1News mentions
0No linked articles in our index yet.