欢迎访问Sonar Source中文网站!
语言选择: ∷ 

Pimcore:一键点击,两个安全漏洞

发布时间:2023-08-07浏览次数:77

Pimcore 平台提供用于集中管理企业数据的软件。它在 56 个国家拥有超过 100,000 个客户,其中包括一些主要供应商,已成为全球企业值得信赖的选择。提供企业订阅版和开源社区版,开发人员和用户社区不断壮大。


我们通过频繁扫描开源项目并评估结果,不断努力增强清洁代码解决方案的技术支持。就 Pimcore 而言,我们的引擎报告了一个有趣的有限目录遍历漏洞。分析结果后,我们在同一端点中发现了另一个 SQL 注入漏洞。利用这两个漏洞,点击攻击者精心设计的链接的管理员将在服务器上执行任意代码。

Pimcore 漏洞影响

10.5.19 之前的 Pimcore 版本容易受到跟踪为 CVE-2023-28438 的端点中的路径遍历SQL 注入漏洞的影响。create-csv这两个漏洞可以通过单个 GET 请求来利用。因此,攻击者可以创建恶意链接,该链接可能导致管理员访问时 执行任意代码。


技术细节

在本节中,我们将讨论这些漏洞的技术细节,并解释攻击者如何将它们组合起来创建一键式攻击,从而在服务器上部署 Web shell。

有限的任意文件写入和路径遍历

使用 SonarCloud 扫描 Pimcore 发现了一个有趣的路径遍历问题,该问题是由于将用户控制的数据作为fopen您可以直接在 SonarCloud 上检查结果:


SonarCloud 上亲自尝试一下!


下划线的功能位于 Pimcore 的管理面板中,可以显示网站各个方面的统计报告。管理员可以创建自定义报告,直接从面板查看它们,或下载 CSV 格式的数据:

通过进一步检查漏洞函数createCsvAction,我们发现用户控制的数据是通过admin/reports/custom-report/create-csv端点的exportFile参数传递的。尽管此端点只能由管理员访问,但它是一个没有 CSRF 保护的 GET 请求端点,因此操纵管理员单击链接就足够了。

exportFile参数的值会附加到 Web 根路径,而无需事先进行清理,从而允许攻击者控制扩展程序并遍历回文件夹路径。 

继续检查代码,我们可以看到用户控制的路径最终将以“追加”模式打开文件。getData 使用fputcsvGithub 中的文件将函数的输出写入其中

public function createCsvAction(Request $request)   {       //...       $filters = $request->get('filter') ? json_decode(urldecode($request->get('filter')), true) : null;       $drillDownFilters = $request->get('drillDownFilters', null);       //...       $result = $adapter->getData($filters, $sort, $dir, $offset * $limit, $limit, $fields, $drillDownFilters);       if (!($exportFile = $request->get('exportFile'))) {           $exportFile = PIMCORE_SYSTEM_TEMP_DIRECTORY . '/report-export-' . uniqid() . '.csv';           @unlink($exportFile);       } else {           $exportFile = PIMCORE_SYSTEM_TEMP_DIRECTORY.'/'.$exportFile;       }       $fp = fopen($exportFile, 'a');       if ($includeHeaders) {           fputcsv($fp, $fields, ';');       }       foreach ($result['data'] as $row) {           $row = Service::escapeCsvRecord($row);           fputcsv($fp, array_values($row), ';');       }       //...   }

到目前为止,攻击者可以控制 CSV 输出文件的路径、名称和扩展名。尽管这允许在服务器上创建 PHP 文件,但攻击者还需要控制文件内容才能执行任意代码。这里进入了第二个漏洞,函数中的 SQL 注入getData

第一个 SQL 注入接收器

查看createCsvAction 之前的函数,攻击者可以控制的输入是$drillDownFilters$filters,它们被传递到getBaseQuery
Github 中的文件

 public function getData($filters, $sort, $dir, $offset, $limit, $fields = null, $drillDownFilters = null)   {       $db = Db::get();       $baseQuery = $this->getBaseQuery($filters, $fields, false, $drillDownFilters);       //...       if ($baseQuery) {           $total = $db->fetchOne($baseQuery['count']);           //...           $sql = $baseQuery['data'] . $order;           //...           $data = $db->fetchAllAssociative($sql);      //...   }

使用该函数的结果发出两个 SQL 查询getBaseQuery

  1. $baseQuery[‘count’]:返回结果数的查询COUNT(*)
    将用于$db->fetchOne.

  2. $baseQuery[‘data’]: 将最终进入$db->fetchAllAssociative并获取结果。


getBaseQuery准备这两个查询的函数如下所示:Github
中的文件

protected function getBaseQuery($filters, $fields, $ignoreSelectAndGroupBy = false, $drillDownFilters = null, $selectField = null)   {	//...       $sql = $this->buildQueryString($this->config, $ignoreSelectAndGroupBy, $drillDownFilters, $selectField);       //...               foreach ($filters as $filter) {                   $operator = $filter['operator'];                   //..                   switch ($operator) {			//..                       case '=':                           $fields[] = $filter['property'];                           $condition[] = $db->quoteIdentifier($filter['property']) . ' = ' . $db->quote($value);    //...           $total = 'SELECT COUNT(*) FROM (' . $sql . ') AS somerandxyz WHERE ' . $condition;           if ($fields && !$extractAllFields) {               $data = 'SELECT `' . implode('`,`', $fields) . '` FROM (' . $sql . ') AS somerandxyz WHERE ' . $condition;           }//...       return [           'data' => $data,           'count' => $total,       ];   }

乍一看,我们注意到$data参数注入,SQL 查询的SELECT字段没有被清理。可以implode('`,`', $fields)简单地用反引号来转义。


为了控制$fields参数,我们需要相应地设置$filters['operator']属性(在代码片段中仅显示“=”,但还有其他选项),然后'property'将属性附加到它。紧接着$condition将创建一个字符串。因此,为了控制该字符串将出现的 $fields值。$condition


然而,虽然看起来在 处有一个简单的 SQL 注入$data,但该变量被连接到两个查询 (和)$condition的末尾由于引号转义(使用函数和完成),任何包含反引号字符 (`) 的字段都将加倍,从而使查询的语法无效。countdata$db->quoteIdentifier$db->quote


我们当然可以注释掉查询的其余部分(使用--or ;)以避免语法破坏$condition但是该$total查询也有损坏的, 并稍后在使用 SQL 注入查询获取之前的$condition行中使用,从而引发异常并且不执行 SQL 注入。$db->fetchOne($baseQuery['count'])data

第二个 SQL 注入接收器

所以我们有一个 SQL 注入,但是利用它总是会导致语法错误。还有其他方法可以忽略该$condition字符串吗?


你们中的一些人可能已经注意到,每个前面$condition都有一个$sql参数,该参数是从$this->getBaseQuery(...)如果该函数中也存在 SQL 注入,我们可以在语法错误之前结束查询。

protected function buildQueryString($config, $ignoreSelectAndGroupBy = false, $drillDownFilters = null, $selectField = null)   {       //...       if ($drillDownFilters) {           $havingParts = [];           $db = Db::get();           foreach ($drillDownFilters as $field => $value) {               if ($value !== '' && $value !== null) {                   $havingParts[] = "$field = " . $db->quote($value);               }           }           if ($havingParts) {               $sql .= ' HAVING ' . implode(' AND ', $havingParts);           }       }       return $sql;   }


审核该buildQueryString函数后,我们发现了另一个 SQL 注入接收器,但现在使用该$drillDownFilters参数。尽管该值已被引用,但该字段并未被引用。攻击者可以使用此同步注释掉损坏的内容$condition并执行任意 SQL 查询。

开发——将一切连接在一起

因此,攻击者可以控制输出文件并将 SQL 注入到获取结果的函数中,结果最终将出现在该文件中。使用以下命令可以直接将导出文件路径指向 Web 根目录中的 PHP 文件: 

../../../../../../../../var/www/html/public/webshell.php

如果文件中随机存在 PHP 声明,PHP 文件也会执行,这意味着文件不必以 开头<?php,因此我们不必担心这一点。 


但是攻击者如何利用 SQL 注入来生成任意内容呢?

有多个查询,一个插入自定义数据,另一个获取数据是可能的,但会使漏洞利用变得更加复杂。回到我们的 SQL 查询,注入是在 SELECT 字段中,因此我们可以使用CASE 表达式


最后,get请求需要两个参数: 

  • headers=true是将字段名称输出到CSV

  • name=Quality_Attributes是演示应用程序中报告的默认名称(为了执行易受攻击的函数,该名称必须是有效的报告)


将来自 3 个接收器的这 2 个漏洞组合到 1 个 GET 请求中,攻击者可以创建一个恶意链接,该链接将在服务器上部署 Web shell。

修补

这两个漏洞均已在 Pimcore 版本 10.5.19 中修复:

  • 通过添加字段名称也修复了SQL 注入。db->quoteIdentifier(...)

$havingParts[] = ($db->quoteIdentifier($field) ." = " . $db->quote($value));
  • 路径遍历通过以下方式修复

    • 验证扩展名是否为“.csv”

    • 规范化路径以防止遍历 

$exportFileName = basename($exportFileName);if(!str_ends_with($exportFileName, ".csv")) {      throw new InvalidArgumentException($exportFileName . " is not a valid csv file.");}return PIMCORE_SYSTEM_TEMP_DIRECTORY . '/' . $exportFileName;


微信扫码微信扫码 关注我们

  • 24小时咨询热线180-210-69380

  • 移动电话180-210-69380

Copyright © 2022 All Rights Reserved. 地址:上海市浦东新区崮山路538号808 苏ICP123456 XML地图