CVE-2023-25828 vulnerability; history, mitigation analysis, and everything you need to know about the remote code execution (RCE) vulnerability in Pluck CMS.
CVE-2023-25828, tracked in the Black Duck KnowledgeBase™ as BDSA-2023-0370, is an authenticated remote code execution vulnerability in Pluck CMS. Pluck is a PHP-based content management system (CMS) used to set up and manage websites. Designed with ease of use and simplicity in mind, Pluck CMS is ideal for running a small website, such as a personal blog. The software has been maintained since 2005 when it was first released under the name CMSsystem, and has received 53 stars since migrating to GitHub in 2014. This vulnerability was discovered by Synopsys Cybersecurity Research Center (CyRC) researchers while auditing the Pluck CMS source code.
Review of the source code was conducted with special attention paid to features that provide file upload functionality. File uploads can be dangerous in many types of applications, but especially so in PHP applications, so this functionality was examined first.
Pluck CMS allows the administrator to upload images.
Figure 1: Uploading an image in Pluck CMS
Browsing the source code of this feature, there is no obvious vulnerability. The mime type and the file extension are checked against an allow list.
Snippet 1: data/inc/images.php
The question is, has this allow list implementation been used for all file upload functionality? Let’s examine another file upload feature, the albums module image upload. This feature is similar to the image upload functionality, except that images can be collected into albums and added all at once into web pages.
The relevant function of this feature is albums_page_admin_editalbum() in the file data/modules/albums/albums.admin.php.
Snippet 2: data/modules/albums/albums.admin.php
When a file is submitted, the mime type is checked against an allow list.
Then some processing is performed on the image file name. This determines the position of the ‘ . ’ character, separates the file name from the file extension with specific logic for when the character is not found, performs search engine optimization on the file name, and constructs full paths for the full image and image thumbnail.
Interestingly, nowhere in this process does the application limit the file extension with an allow list. This immediately raises alarms, since it means a file extension that enables the content of the uploaded file to be interpreted and executed by the underlying web server, such as .php and .phar, can be used without restriction.
There are multiple ways to exploit this behavior using a file extension and valid mime type. A web shell could replace the file contents entirely, be added to the file contents, or be included in image metadata. However, none of these techniques are applicable in this case because of logic later in the function.
Snippet 3: data/modules/albums.admin.php
The $fullimage path calculated previously is used to instantiate a SmartImage object. Resizing might also seem like an issue, but that can be avoided by providing an image with equal height and width. So let’s examine the SmartImage constructor to see what it does.
Snippet 4: data/inc/lib/SmartImage.class.php
The __construct function uses the built-in getimagesize PHP-GD function to identify the type of image being processed from the constant assigned by the function. This is accessed via $this->info[2], and a different built-in imagecreatefrom* PHP-GD function is then used to process the image depending on the value of the constant. These functions perform normalization that destroys any naively embedded web shell, such as one appended to the file or inserted into image metadata.
At this point, CyRC researchers looked into the behavior of these imagecreatefrom* functions. Focusing first on imagecreatefromjpeg, they discovered prior research that showed it is possible to embed a web shell in such a manner that it can survive the normalization. The Jellypeg tool can be used to find a location where a small piece of code survived the compression process. The tool achieves this by brute-force searching for an offset where the web shell is still contained within the file contents even after being processed by the imagecreatefromjpeg and imagejpeg functions.
Running the tool with the default configuration, a valid image with the embedded payload <?=exec($_GET[“c”])?> can be found.
Figure 2: The embedded payload
This file will allow the web shell to survive through the normalization performed by imagecreatefromjpeg in the SmartImage constructor. If uploaded with a valid mime type and .php file extension, RCE can be achieved by navigating to the file directly at the web path /pluck/data/settings/modules/albums/<album_name>/<file_name> and passing commands via the HTTP parameter ‘c’. This is a serious vulnerability, as it allows an authenticated attacker to run arbitrary commands on the underlying web server of the application.
The vendor released a patch for this vulnerability in version 4.7.16-dev5 in the commit 8aec080. This replicates the extension allow list implementation in the standard image upload functionality.
Snippet 5: data/modules/albums/albums.admin.php
However, there is an error with this patch. The highlighted characters are dangling, so they introduce a syntax error into the file. When this file is included by other pages in the application, it throws an error, disabling the albums module entirely. Another patch was released in 4.7.17 in the commit a52de9c to correct this syntax error, and this mitigates the vulnerability.
We thank Jinny Ramsmark for her work developing Jellypeg.