Introduction

Microweber is an open-source content management system (CMS) that aims to simplify building websites with a drag and drop interface. It’s a PHP-based CMS with built-in blogging and e-commerce capabilities. Microweber also has a modular expansion system which, along with other features, is managed via an admin panel.

A directory traversal vulnerability was discovered in the Backup restore functionality, that allows an attacker to write arbitrary files to the file system in the web server’s user context via a specifically crafted zip file.

The Vulnerability

The zip slip vulnerability was discovered while exploring functionality within the Backup.php script. The related advisory summary can be found here.

In Backup.php on line 335, the path to the temporary zip extraction directory is defined within the current cache path of the application, and then passed in as the target_dir for extraction.

Backup.php - Line 335

Following the execution path, the actual extraction of the zip occurs within the native_unzip method starting on line 185 of Unzip.php. The directory traversal attack occurs is on line 240 of Unzip.php, where the $target_file_to_save variable is defined.

Unzip.php - Line 240

Zip files allow file names to be defined somewhat arbitrarily, which means that the $name variable is controllable by the user and potentially includes relative paths (e.g. ../). Defining $target_file_to_save with the string concatenation $target_dir . $name makes this vulnerable to a Zip Slip directory traversal attack in the absence of additional file path validation or sanitization. The result of the string concatenation is opened as the output file for the archive entry contents on line 252.

Unzip.php - Line 252

Impact

The impact of this vulnerability is that arbitrary paths can be provided within the zip such as ../../../../payload.php and allow arbitrary files contained within the zip to be written to arbitrary directories on the server in the user context of the web server. While the default proof-of-concept writes a php file within the web root for code execution, an attacker can write arbitrary files outside of the web root in the user context of the web server as well. Additionally, the extracted filenames are not sanitized against the dangerous file extension list, enabling an extension filter bypass.

Exploitation

The following steps walk through the necessary sequence to exploit the zip slip directory traversal vulnerability in order to gain remote code execution (RCE). A proof-of-concept exploit script is available here.

Step 1 - Login

In order to invoke all necessary APIs, a valid administrator user session must be present. Thus, an administrator username and password must be provided.

Step 2 - Create a malicious zip file

The zip slip approach is derived from ptoomey3’s evilarc project.

The proof-of-concept default payload is phpinfo() and is created using the Python native zipfile module. The payload is saved to <webroot>/userfiles/cache/ by default:

<?php phpinfo(); ?>

The equivalent command to create the zip with evilarc is:

python evilarc.py -o unix -d 4 -f payload.zip payload.php -p userfiles/modules

Step 3 - Upload zip

Now that a malicious zip file has been created, it needs to be uploaded to the server. The file is uploaded as a generic file via the /plupload endpoint, and then moved into the backup directory in steps 4 & 5.

Step 4 - Determine webroot file path

Knowing the webroot is necessary in the subsequent step as an absolute filepath is required to move the file into the backup directory. The webroot of the site is determined the ?debug=true output on the landing page, matching against the DefaultController.php path. Everything preceding /src is the webroot on the server file system.

Step 5 - Move the uploaded to backup

A specific API endpoint /api/Microweber/Utils/Backup/move_uploaded_file_to_backup exists to move files into the backup directory, which is used to move the uploaded zip file into the backup directory.

Step 6 - Restore the backup

Now that the malicious zip file is uploaded and moved into place, it’s time to extract it and exploit the zip slip directory traversal vulnerability.

Utilizing the /api/Microweber/Utils/Backup/restore endpoing with id=payload.zip triggers an insecure extraction of the zip file.

The Microweber/Utils/Backup/restore function will attempt to extract the provided zip filename from the backup directory, but does not properly sanitize extracted filenames to prevent a zip slip.

Since the directory created to extract the zip files is within the webroot with a consistent depth of 4 from the root (/storage/cache/backup_restore/<md5 hash>/), a directory traversal of depth 4 will yield the webroot for a standard installation.

Step 7 - Profit! (i.e. Remote Code Execution)

As much as everyone loves a good phpinfo page, shell_exec is a much more interesting function to have access to via a remote client.

Upload a shell_exec payload:

./microweber_rce.py --hostname "http://microwebertest.com" --username "admin" --password "password123" --payload '<?php if (isset($_REQUEST["fexec"])) {echo "<pre>" . shell_exec($_REQUEST["fexec"]) . "</pre>";} ?>'

Execute whoami:

http://microwebertest.com/userfiles/cache/payload.php?fexec=whoami

With shell_exec, an attacker can now download an execute an additional exploit and execute it. Or instead of shell_exec, and attacker could upload a PHP reverse shell directly.

The Fix

Microweber responded very quickly and had a patch committed within a few of hours of verifying the vulnerability. The patch addresses the vulnerability by skipping filenames containing .. in the backup, and was applied to both the zip_open and gzinflate extraction execution trees.