GLPI – SQL injection through dynamic reports

GLPI - SQL injection through dynamic reports

This post details the journey of our Research Team in uncovering a SQL Injection vulnerability in GLPI, an open-source ITSM tool predominantly used in Brazil and France.
这篇文章详细介绍了我们的研究团队发现 GLPI 中的 SQL 注入漏洞的旅程,GLPI 是一种主要在巴西和法国使用的开源 ITSM 工具。

We observed many companies using GLPI, and we decided to focus on finding vulnerabilities that could allow access to users’ credentials and potentially some Remote Code Execution (RCE)
我们观察到许多公司都在使用 GLPI,因此我们决定专注于寻找可能允许访问用户凭据和潜在远程代码执行 (RCE) 的漏洞

As an 0pen-source software, we downloaded its entire source code for an in-depth code review analysis and some manual dynamic testing. This approach helped us understand the application structure leading to insights on potential vulnerabilities for credential disclosure and RCE.
作为一款 0pen-source 软件,我们下载了它的全部源代码,进行了深入的代码审查分析和一些手动动态测试。这种方法帮助我们了解了应用程序结构,从而深入了解了凭据泄露和 RCE 的潜在漏洞。

GLPI - SQL injection through dynamic reports

Currently, over 5,000 GLPI instances are exposed to the internet, presenting a significant research opportunity.
目前,超过 5,000 个 GLPI 实例暴露在互联网上,这提供了一个重要的研究机会。

Initial Approach 初始方法

The first step was to download the full source code from GitHub.
第一步是从 GitHub 下载完整的源代码。

GLPI is written in PHP, Twig (Template Engine), and JavaScript for client-side actions.
GLPI 是用 PHP、Twig(模板引擎)和 JavaScript 编写的,用于客户端操作。

GLPI - SQL injection through dynamic reports

Code Review Insights 代码审查见解

During our review, we used regex/grep to identify common vulnerabilities such as SQL Injection (SQLi), Cross-Site Scripting (XSS), and Command Injection. We looked for typical coding errors like direct usage of $_GET, $_POST, $_REQUEST in critical functions like “system”, “mysqli_query”, “eval”, and “echo”.
在审查过程中,我们使用 regex/grep 来识别常见漏洞,例如 SQL 注入 (SQLi)、跨站点脚本 (XSS) 和命令注入。我们寻找典型的编码错误,例如在“system”、“mysqli_query”、“eval”和“echo”等关键函数中直接使用 $_GET、$_POST、$_REQUEST。

This method is sometimes effective, but less so for applications with advanced security measures like SAST/DAST solutions integrated into their CI/CD pipelines.
此方法有时有效,但对于具有高级安全措施(如 SAST/DAST 解决方案)集成到其 CI/CD 管道中的应用程序来说,效果不太好。

For such mature applications, we had to delve deeper into the source code, tracing data flow and user-controlled input to uncover points where malicious code could be injected.

GLPI - SQL injection through dynamic reports

Things we find by searching for $_GET, $_POST, and others, as I mentioned earlier, can be seen in the screenshot above, where we identify a case of reflected XSS. However, this still doesn’t help us achieve our main goal, which is to gain access to user credentials or achieve Remote Code Execution (RCE) in the application
正如我之前提到的,我们通过搜索 $_GET、$_POST 等找到的东西可以在上面的屏幕截图中看到,我们在其中识别了一个反射 XSS 的情况。但是,这仍然无法帮助我们实现主要目标,即访问用户凭据或在应用程序中实现远程代码执行 (RCE)

Another aspect we always like to analyze in a PHP application is whenever there’s a string concatenation with a variable. This often presents a good opportunity for vulnerabilities such as SQL injection, XSS (as seen in the previous screenshot), and other types of vulnerabilities
我们总是喜欢在 PHP 应用程序中分析的另一个方面是,每当有字符串与变量连接时。这通常为漏洞提供了很好的机会,例如 SQL 注入、XSS(如前面的屏幕截图所示)和其他类型的漏洞

By searching for: ‘.$ or “.$ in Visual Studio Code, will return every instance where a concatenation occurs in the code, as shown in the screenshot below.
通过在 Visual Studio Code 中搜索:“.$”或“.$”,将返回代码中发生串联的每个实例,如下面的屏幕截图所示。

GLPI - SQL injection through dynamic reports

Do you agree that it’s terrible to analyze all occurrences, right? One thing I also like to do is to ‘collapse’ [the view] to display only the file names, and I always go to the files that draw more attention.

GLPI - SQL injection through dynamic reports

Now it becomes easier to decide which files to look at first. A file that catches attention is:

GLPI - SQL injection through dynamic reports

Files named ‘Database’ or ‘DB’, I analyzed all these files, looking for dangerous concatenations that could lead to compromising the application.

GLPI - SQL injection through dynamic reports

The first result of the research seemingly could lead to an XSS (Cross-Site Scripting) vulnerability. However, when we approach it this way, directly searching for vulnerabilities, we can’t ascertain if it is indeed exploitable, because we still don’t know whether the input in question is controlled by user input or not.

In this case, the variable $rand is defined a little further up in the same file as follows:
在本例中,变量 $rand 在同一文件中进一步定义,如下所示:

GLPI - SQL injection through dynamic reports

It was created in a way that we can not have control over this aspect.

After spending several hours analyzing files and persisting, I arrive at this function.

GLPI - SQL injection through dynamic reports

When I came across this function, I immediately thought: We have a SQL Injection.
当我遇到这个函数时,我立即想到:我们有一个 SQL 注入。

Let’s analyze this. We have a variable $end that is being sent as a parameter to this function, and this variable does not undergo any kind of concatenation. It is sent directly to the QueryExpression class. In this case, we don’t even need to examine what QueryExpression does, because it won’t see our injection happening. The injection occurs earlier, during the process of concatenating the string with the variable $end.
让我们来分析一下。我们有一个变量作为参数发送到这个函数,这个变量 $end 不经历任何形式的串联。它直接发送到 QueryExpression 班级。在这种情况下,我们甚至不需要检查什么 QueryExpression ,因为它不会看到我们的注射发生。注入发生在将字符串与变量 $end 连接的过程中。

Connecting the sink to the source

As I mentioned earlier, it’s common to identify vulnerabilities that are not “reachable” through user input, and therefore, they are not considered “vulnerabilities” because they are not exploitable. To identify a potential injection point, we need to locate all the places where a call is made to the “getDateCriteria” function passing the $end parameter.
正如我之前提到的,通过用户输入识别无法“访问”的漏洞是很常见的,因此,它们不被视为“漏洞”,因为它们不可利用。为了识别潜在的注入点,我们需要找到调用传递 $end 参数的“getDateCriteria”函数的所有位置。

Here’s the issue, though: there are two functions with the same name.

GLPI - SQL injection through dynamic reports

The advantage is that the getDateCriteria function in the “db.function.php” file calls the getDateCriteria function from the DbUtils class without performing any validation either.
优点是“db.function.php”文件中的 getDateCriteria 函数从 DbUtils 类中调用函数, getDateCriteria 而无需执行任何验证。

In other words, any interaction with this function name will lead to the same vulnerability, so it’s not as much of a problem as it might seem…

GLPI - SQL injection through dynamic reports

We have more than 40 occurrences of calls to the function; our mission will be to analyze each of these calls and check if, in any case, there is a $end variable that is user-controlled.
我们对函数的调用次数超过 40 次;我们的任务是分析这些调用中的每一个,并检查在任何情况下是否存在用户控制的 $end 变量。

Analyzing the calls in the “Stat.php” file, we can see that there are several instances where a $end variable is passed directly to the vulnerable function:
分析“Stat.php”文件中的调用,我们可以看到有几个实例将 $end 变量直接传递给易受攻击的函数:

GLPI - SQL injection through dynamic reports

Now, we need to identify where this call originated from and how we can reach the various “cases” within the “switch-case” structure. Several variables could render the vulnerability unexploitable.

GLPI - SQL injection through dynamic reports

At the top of this same file, we can identify that this “switch-case” is within a function called “constructEntryValues”. This static function receives the variable $end as its fourth parameter, and we can see that the variable does not undergo any kind of modification or validation against injection within this function either. So, up to this point, our potential injection point remains viable.
在同一文件的顶部,我们可以确定这个“switch-case”位于一个名为“constructEntryValues”的函数中。这个静态函数接收变量 $end 作为其第四个参数,我们可以看到该变量也没有在此函数中进行任何形式的修改或验证。因此,到目前为止,我们潜在的注入点仍然可行。

Our next mission is to identify where the constructEntryValues the function is invoked within the code.
我们的下一个任务是确定函数在代码中的调用位置 constructEntryValues 。

GLPI - SQL injection through dynamic reports

We have only 18 occurrences of calls to the static function, and we can see that several of these calls are happening within the same “Stat” file, using the static call with self::constructEntryValues
我们只有 18 次对静态函数的调用,我们可以看到其中几个调用发生在同一个“Stat”文件中,使用静态调用 self::constructEntryValues

GLPI - SQL injection through dynamic reports

Analyzing the calls, we can see that the “fourth” parameter, which is renamed in the code to $end, was originally called $date2 before entering constructEntryValues. Therefore, from this level upwards in the application, we can consider the variable $date2 as a potential point of exploitation for this SQL Injection.
分析调用,我们可以看到,在代码中重命名为 $end 的“fourth”参数最初是在输入 constructEntryValues 之前调用 $date2 的。因此,从应用程序中的这个级别开始,我们可以将该变量 $date2 视为此 SQL 注入的潜在利用点。

GLPI - SQL injection through dynamic reports

Most of the occurrences of the call to the constructEntryValues function are within the showTable function, which receives the variable $date2 as its fourth parameter. This variable, in turn, also does not undergo any kind of modification before being sent to the vulnerable function.

Following the trail of the poorly written code, we have managed to identify all the calls to the “showTable” function.

GLPI - SQL injection through dynamic reports

As we trace our way to the “top” of the data flow, we notice a decreasing number of occurrences of calls to the functions corresponding to the vulnerability in this particular source code. Now, we’re down to only 5 results, and of those, only 4 are actual calls to the function.
当我们追踪到数据流的“顶部”时,我们注意到对与此特定源代码中的漏洞相对应的函数的调用次数在减少。现在,我们只剩下 5 个结果,其中只有 4 个是对函数的实际调用。

GLPI - SQL injection through dynamic reports

Examining the first occurrence, we need to pay close attention to what is happening. We’ll see that the fourth parameter in the call to the ShowTable function is $param['date2']. In this case, it is now an array, and we need to control this item of the array. It is defined a bit higher up in the code, so let’s look closely at what’s happening there.
检查第一次发生的情况,我们需要密切关注正在发生的事情。我们将看到对 ShowTable 函数调用中的第四个参数是 $param['date2'] 。在这种情况下,它现在是一个数组,我们需要控制数组的这个项目。它在代码中的定义更高一些,所以让我们仔细看看那里发生了什么。

GLPI - SQL injection through dynamic reports

Okay, let’s break it down:

  1. The code sets the variable $itemtype using user-controlled input.
    该代码使用用户控制的输入设置变量 $itemtype 。
  2. There’s a check to see if the display_type parameter is being sent by the user.
    有一个检查,看看 display_type 参数是否由用户发送。
  3. After this validation, a “switch-case” statement checks if the option in $itemtype (which is user-controlled) is “KnowbaseItem” or “Stat”. To reach the vulnerable function, we need the value of $itemtype to be “Stat”.
    在此验证之后,“switch-case”语句将检查(由用户控制)中的 $itemtype 选项是“KnowbaseItem”还是“Stat”。为了达到易受攻击的函数,我们需要 $itemtype 的值为 “Stat”。
  4. In the final step, there is a validation to check if the item_type_param parameter is being sent by the user. If it is, it is passed to the function decodeArrayFromInput. At first glance, you might think, “Here, they must be validating the user input, and we probably won’t be able to execute our SQL injection.”
    在最后一步中,将进行验证以检查用户是否正在发送参数 item_type_param 。如果是,则将其传递给函数 decodeArrayFromInput 。乍一看,你可能会想,“在这里,他们必须验证用户输入,我们可能无法执行我们的 SQL 注入。

Let’s take a closer look at what’s happening in this decodeArrayFromInput function.
让我们仔细看看这个 decodeArrayFromInput 函数中发生了什么。

GLPI - SQL injection through dynamic reports

That’s an intriguing discovery. The absence of validation in the decodeArrayFromInput function, combined with its behavior of decoding the value sent in “base64” and then performing a json_decode, presents an interesting opportunity.
这是一个有趣的发现。 decodeArrayFromInput 函数中没有验证,再加上它对“base64”中发送的值进行解码,然后执行 json_decode 的行为提供了一个有趣的机会。

The GET parameter item_type_param being a JSON string encoded in base64 is actually an advantageous situation. This is because any SQL Injection payload that we craft can be encoded in base64, which might bypass potential Web Application Firewall (WAF) protections.
GET 参数 item_type_param 是以 base64 编码的 JSON 字符串,实际上是一种有利的情况。这是因为我们构建的任何 SQL 注入有效负载都可以以 base64 编码,这可能会绕过潜在的 Web 应用程序防火墙 (WAF) 保护。

Since the payload will be encrypted in base64, it may not be easily detected by security systems looking for typical SQL Injection patterns. This opens up a potentially exploitable vulnerability in the system.
由于有效负载将以 base64 加密,因此寻找典型 SQL 注入模式的安全系统可能不容易检测到它。这会在系统中打开一个可能被利用的漏洞。

Putting All the pieces together

Now is the time to integrate everything we’ve gathered so far and correlate it with a dynamic test of the application, meaning testing it while it’s running. Here’s how we can proceed:

GLPI - SQL injection through dynamic reports

Now that we are examining the application in its running state, and with the knowledge that the file containing the vulnerability is “report.dynamic.php”, it becomes easier to understand the functionality by observing the page.

GLPI - SQL injection through dynamic reports

Seeing the call being executed as anticipated in your analysis is a significant step. The parameter “item_type” being sent as “Stat” and the “item_type_param” being sent in “base64” aligns with your earlier findings.

GLPI - SQL injection through dynamic reports

When decoding the base64, we can identify the JSON object, also as predicted in the static analysis. We know that the injection point is “date2,” so we need to change the value of “date2” to extract data from this application’s database.
在解码 base64 时,我们可以识别 JSON 对象,这也如静态分析中预测的那样。我们知道注入点是“date2”,因此我们需要更改“date2”的值以从此应用程序的数据库中提取数据。

In this case, we can see that it is not possible to use any tool to automate the process, as the result of the query will be rendered and displayed in the PDF. Before simply injecting the famous ‘or 1=1 –‘, it’s important to understand one key point: the parameter value must not generate an exception.
在这种情况下,我们可以看到不可能使用任何工具来自动化该过程,因为查询的结果将呈现并显示在 PDF 中。在简单地注入著名的“or 1=1 –”之前,重要的是要了解一个关键点:参数值不得生成异常。

For this, it’s necessary to fully understand what is happening in the application’s code. Remember, the injection point is:

GLPI - SQL injection through dynamic reports

The result would be something like: “ADDDATE(‘2023-12-15’, INTERVAL 1 DAY)”.
结果类似于:“ADDDATE(’2023-12-15’, INTERVAL 1 DAY)”。

In this case, we need to inject a single quote to close the value of ADDDATE, complete the query, close the parentheses of ADDDATE, and then perform our injection. We need to achieve something like
在这种情况下,我们需要注入一个引号来关闭 ADDDATE 的值,完成查询,关闭 ADDDATE 的括号,然后执行注入。我们需要实现类似

2023-02-18', INTERVAL 1 DAY))))) UNION SELECT 1,2,(select concat(name,'-',password) from glpi_users limit 1),4;-- -

The query in question will extract the user’s name and password from the database and display this data in the PDF that will be rendered. Our JSON ends up looking something like the following:
有问题的查询将从数据库中提取用户名和密码,并在将要呈现的 PDF 中显示此数据。我们的 JSON 最终如下所示:

{"type":"user","date1":"2022-02-19","date2":"2023-02-19', INTERVAL 1 DAY))))) UNION SELECT 1,2,(select concat(name,'-',password) from glpi_users limit 1),4;-- -","value2":0,"start":0}
GLPI - SQL injection through dynamic reports
JSON with syntax highlight
具有语法突出显示的 JSON

Remembering that we need to encode the JSON in base64 before sending it in the URL, then, after encoding the JSON and passing the parameter, we can see the result inside the PDF with the username GLPI and the password encrypted with bcrypt.
请记住,我们需要先以 base64 对 JSON 进行编码,然后再将其发送到 URL 中,然后,在对 JSON 进行编码并传递参数后,我们可以在 PDF 中看到结果,用户名为 GLPI,密码为 bcrypt 加密。

GLPI - SQL injection through dynamic reports

From this moment, it is possible to execute any SQL command on the GLPI instance, read any data in the database, and, depending on the setup, read-protected files from the server. In some scenarios, it’s even feasible to write a webshell and gain access to the server (RCE).
从这一刻起,可以在 GLPI 实例上执行任何 SQL 命令,读取数据库中的任何数据,并根据设置从服务器读取受保护的文件。在某些情况下,编写 webshell 并获取对服务器 (RCE) 的访问权限甚至是可行的。

GLPI - SQL injection through dynamic reports

As a proof of concept, it was possible to demonstrate the same impact on the SaaS service of TecLib (the company responsible for the software).
作为概念验证,可以证明对 TecLib(负责软件的公司)的 SaaS 服务有同样的影响。

An important point to mention is that: This attack only works for authenticated users; however, the user does not need to have administrative privileges to execute this attack.

A basic user with a “tech” level, in this case, could carry out this attack and gain access to all the data within the GLPI server.
在这种情况下,具有“技术”级别的基本用户可以执行此攻击并访问 GLPI 服务器中的所有数据。

Timeline 时间线

February 19, 2023 – Reported via
2023 年 2 月 19 日 – 通过 报道

February 22, 2023 – TecLib developers ask for more information
2023 年 2 月 22 日 – TecLib 开发人员要求提供更多信息

February 22, 2023 – Sent some working PoC on TecLib SaaS with screenshots and step-by-step instructions
2023 年 2 月 22 日 – 在 TecLib SaaS 上发送了一些工作 PoC,其中包含屏幕截图和分步说明

February 24, 2023 – TecLib developers confirm the issue
2023 年 2 月 24 日 – TecLib 开发人员确认了该问题

March 29, 2023 –  CVE assigned – CVE-2023-28838
2023 年 3 月 29 日 – 已分配 CVE – CVE-2023-28838

April 5, 2023 – TecLib developers marked this as fixed in 10.0.7 with commit 3b1f59
2023 年 4 月 5 日 – TecLib 开发人员在 10.0.7 中通过提交 3b1f59 将此问题标记为已修复

April 5, 2023 – TecLib developers release an advisory (SQL injection through dynamic reports)
2023 年 4 月 5 日 – TecLib 开发人员发布公告(通过动态报告注入 SQL)

原文始发于Carlos Vieira:GLPI – SQL injection through dynamic reports

版权声明:admin 发表于 2024年1月18日 下午9:19。
转载请注明:GLPI – SQL injection through dynamic reports | CTF导航