GLPI is a popular open-source ITSM and Service Center software used in many companies with mid-large IT assets inventory.
Vulnerabilities in systems like GLPI are extremely juicy for the attackers since it provides a rich source of information regarding internal IT assets. Such vulnerabilities also potentially enable lateral movement and establishing persistence in corporate networks, e.g through implanting backdoors/ransomware to the inventory of monitored hosts using GLPI FusionInventory plugin’s software deployment functionality.
Inspired by our recent external penetration test of the client’s network (SMB Software Development Company), our team decided to run some further research of the publicly available GLPI source code and has discovered a new vulnerability in this system.
Additionally, in the following posts, we are planning to share our experience regarding typical misconfigurations in GLPI systems deployed and how to prevent them.
Vulnerability summary
Product: GLPI
Vulnerable version: <=9.5.3
Tested: v9.5.3, 2021-02-14
Category: Limited PHP remote code execution
Vulnerability type: CWE-470: Use of Externally-Controlled Input to Select Classes or Code (‘Unsafe Reflection’)
CVE ID: CVE-2021-21327
Description
Any non-authenticated user can remotely instantiate an object of the class existing in the GLPI environment that can be used to carry out malicious attacks, or to start a “POP chain”.
Initial entry point
Many entry points exist in the GLPI and their plugins, where untrusted user input is passed to the getItemForItemtype
function missing input and authorization checks, so just one example is shown to demonstrate the issue in the dropdownConnect.php
as an entry point.
The dropdownConnect.php
passes parameter fromtype
from the external POST
request to the getItemForItemtype
function without any authorization checks :
//--- file dropdownConnect.php: if (!isset($_POST['fromtype']) || !($fromitem = getItemForItemtype($_POST['fromtype']))) { exit(); }
Next, the untrusted user’s input is transmitted as an argument to the getItemForItemtype
function which is responsible for object instantiation with an input class name without proper validation:
//--- file dbutils.class.php, function getItemForItemtype($itemtype) if (class_exists($itemtype)) { return new $itemtype(); } //handle namespaces if (substr($itemtype, 0, \strlen(NS_GLPI)) === NS_GLPI) { $itemtype = stripslashes($itemtype); if (class_exists($itemtype)) { return new $itemtype(); } }
As a result of such unvalidated user input, when passing an existing class name (e.g. Glpi\Console\Application
) a new object of this class is created executing its constructor e.g. magic __construct()
PHP method if declared. Subsequently, when a PHP object gets destroyed, its __destruct()
method is executed.
CVE-2021-21327 exploit
Issue a request:
POST /ajax/dropdownConnect.php HTTP/1.1 Host: glpi Content-Type: application/x-www-form-urlencoded; charset=UTF-8 Connection: close fromtype=ClassName
where ClassName
is a class name existing in a GLPI environment in the expected format to pass name validation checks, e.g. Glpi\Foo\Bar
for the GLPI core classes, PluginFooBar
for a GLPI Plugin.
Timeline
- 2021.02.14: Report sent (initial severity reported as High)
- 2021.02.16: Feedback received (triaged as Moderate)
- 2021.02.16: Initial fix deployed
- 2021.02.16: Review requested
- 2021.02.16: Suggestions sent
- 2021.02.26 CVE-2021-21327 issued
- 2021.03.02 Fixes committed in the main branch
- 2021.03.03 GLPI 9.5.4 is released
Security impact analysis
This vulnerability impacts the integrity and availability of the GLPI system since a malicious user can remotely call random constructors or destructors which might contain some sensitive/heavy-loading/destructive operations.
The root cause of this vulnerability is based on the usage of reflection functionality: in many places, PHP code dynamically instantiates classes, e.g. using class name as a parameter, which can be also fed from the user input.
Proposed mitigation for this class of vulnerabilities
In the systems secured by design such behaviour should be restricted completely since a user’s (both authenticated or non-authenticated) input is not trusted. Alternatively, a strict white-list of such reflection usage should be introduced to minimize misuse potential.