The Pimcore Platform provides software for central management of corporate data. With over 100,000 clients across 56 countries, including some major vendors, it has become a trusted choice for businesses worldwide. Available in both an Enterprise subscription as well as an Open Source Community Edition with a growing community of developers and users.
We make a consistent effort to enhance the technology powering our Clean Code solution by frequently scanning open-source projects and assessing the outcomes. In the case of Pimcore, our engine reported an interesting limited directory traversal vulnerability. After analyzing the finding we found an additional SQL Injection vulnerability in the same endpoint. Leveraging those two vulnerabilities, an admin that clicks on an attacker’s crafted link will execute arbitrary code on the server.
Pimcore versions prior to 10.5.19 are susceptible to both a path traversal and an SQL injection vulnerability in the
create-csv endpoint tracked as CVE-2023-28438. The two vulnerabilities can be exploited with a single GET request. Because of this, an attacker can create a malicious link, which can cause the execution of arbitrary code when accessed by an admin.
In this section, we will discuss the technical details of the vulnerabilities and explain how an attacker could combine them to create a one-click exploit that will deploy a web shell on the server.
Scanning Pimcore with SonarCloud uncovered an interesting path traversal issue caused by passing user-controlled data as the filename parameter of
fopen. You can inspect the finding directly on SonarCloud:
The underlined feature is in the admin panel of Pimcore which enables the display of statistical reports on various aspects of the website. An admin can create custom reports, view them directly from the panel, or download the data in CSV format:
Upon further inspection of the vulnerable function
createCsvAction, we found out that the user-controlled data is passed through the
exportFile parameter. Although this endpoint is only accessible by admins, it is a GET request endpoint with no CSRF protection, thus manipulating an admin to click on a link is enough.
The value of the
exportFile parameter is appended to the web root path without prior sanitization, allowing an attacker to control the extension as well as traverse back in the folder path.
On continued inspection of the code, we can see that the user-controlled path will end up opening a file in “append” mode. Writing the
getData function’s output to it using
File in Github
Up until now, an attacker can control the CSV output file path, name, and extension. Although this allows the creation of PHP files on the server, an attacker will need to control the file content as well in order to execute arbitrary code. Here enters the second vulnerability, an SQL Injection in the
Looking at the
createCsvAction function from earlier, the inputs an attacker can control are
$filters, which are passed on to
File in Github
Two SQL queries are issued with the result of the
$baseQuery[‘count’]: a query that returns the number of results using
will be used in
$baseQuery[‘data’]: will end up in
$db->fetchAllAssociativeand fetch the results.
This is how the
getBaseQuery function that prepares those two queries looks like:
File in Github
At first glance, we noticed an injection at the
$data parameter, the SQL query's
SELECT fields are not sanitized. The
implode('`,`', $fields) can simply be escaped with backticks.
In order to control the
$fields parameter we need to set the
$filters['operator'] attribute accordingly (in the code snippet only '=' is shown but there are other options) and then the
'property' attribute will be appended to it. Immediately after a
$condition string will be created. So in order to control the
$fields value the
$condition string will be present.
However, while it seems like there is a simple SQL injection at
$condition variable is concatenated to the end of both queries (
data). And due to the quotation escaping (done using the functions
$db->quote), any field containing a backtick character (`) will be doubled and thus making the query's syntax invalid.
We can of course comment out the rest of the query (using
;) to avoid the syntax breaking
$condition. But the
$total query also has the broken
$condition, and later be used in the line
$db->fetchOne($baseQuery['count']) before fetching with the SQL Injected
data query, thus raising an exception and not executing the SQL Injection.
So we have an SQL Injection, but exploiting it will always cause a syntax error. Is there any other way to somehow ignore the
Some of you probably already noticed that before every
$condition there is the
$sql parameter, which is returned from
$this->getBaseQuery(...). If there is an SQL Injection in that function as well we can end the query before the syntax error.
buildQueryString function we found another SQL Injection sink but now using the
$drillDownFilters parameter. Though the value is being quoted, the field isn't. An attacker can use this sync to comment out the broken
$condition and execute arbitrary SQL queries.
So an attacker can control the output file and inject SQL to the function that fetches results which will end up in that file. Having the export file path pointing to a PHP file in the web root is straightforward using:
A PHP file will execute also if there is the PHP declaration randomly in the file, meaning a file doesn't have to start with
<?php, so we don't have to worry about that.
But how can an attacker exploit the SQL Injection to result in arbitrary content?
Having multiple queries, one that inserts custom data and another that fetches it is possible but makes the exploit more complicated. Going back to our SQL query, the injection is in the SELECT fields, so we can use the CASE expression.
Lastly, there are two parameters needed for the get request:
headers=trueis to output the field names to the CSV
name=Quality_Attributesis a default name of a report from the demo app (in order to execute the vulnerable function the name has to be a valid report)
Combining those 2 vulnerabilities from 3 sinks in 1 GET request an attacker could create a malicious link that will deploy a web shell on the server.
Both vulnerabilities were fixed in Pimcore version 10.5.19:
- The SQL Injection was fixed by adding
db->quoteIdentifier(...)in the field name as well.
- The path traversal was fixed by:
- Verifying that the extension is “.csv”
- Normalizing the path to prevent traversing
|2023-02-20||We reported all issues to Vendor|
|2023-03-15||Vendor released patch version 10.5.19|
|2023-03-22||CVE-2023-28438 and security advisory released|
The focus of our blog post was on our success in identifying and utilizing two distinct vulnerabilities with a single GET request, ultimately leading to code execution. This serves as a powerful demonstration of our product's capability to detect security flaws, and we also highlighted the step-by-step process we followed from analyzing the results to creating a weaponized exploit.
We would like to thank the maintainers again for the quick response and for handling the situation professionally.