Ayoub ELMOKHTAR

Offensive Security Engineering @noon

CVE-2024-34716 – The Deceptive PNG Trap: Breaking Down the PNG-Driven Chain from XSS to Remote Code Execution on PrestaShop (<=8.1.5) | Ayoub ELMOKHTAR

CVE-2024-34716 – The Deceptive PNG Trap: Breaking Down the PNG-Driven Chain from XSS to Remote Code Execution on PrestaShop (<=8.1.5)

April 12, 2024

If you’re unfamiliar with PrestaShop (https://github.com/PrestaShop/PrestaShop), think of it as a comprehensive toolbox designed for creating an online store. This platform enables you to construct your storefront, display your merchandise, interact with customers, and process payments. Its cost-free nature and the ability to enhance its functionality with various add-ons make it a popular choice among retailers.

In this detailed research, we embark on an explorative journey into uncovering a fascinating series of vulnerabilities chained together to obtain remote code execution. This chain allows attackers to gain control over server instances hosting PrestaShop <= 8.1.5. The significance of this exploration lies in the vulnerability of PrestaShop, a widely adopted open-source e-commerce solution that powers a substantial fraction of online retail platforms worldwide. Its popularity and extensive feature set make it an attractive target for cybercriminals and for vulnerability research as well.

Setting Up Our Testing Environment Using Docker

Because the project is available on Docker, the testing setup was straightforward. It didn’t take long to set up and start my vulnerability research. Here’s a basic guide:

  1. Download PrestaShop Docker Image: Pull the official PrestaShop image from Docker Hub by running the command:
    docker pull prestashop/prestashop
    
  2. Run PrestaShop Container: Start your PrestaShop container using Docker.
    docker run -ti --name prestashop-sec-ops -e PS_INSTALL_AUTO=1 -e PS_FOLDER_ADMIN=admin123 -e PS_FOLDER_INSTALL=install123 -e DB_SERVER=127.0.0.1 -e DB_PASSWD=admin456 -p 8080:80 prestashop/prestashop
    
  3. PrestaShop developers also offer a quick guide to set things up via Docker on https://github.com/PrestaShop/PrestaShop?tab=readme-ov-file#docker-compose

Exploitation Details

As previously mentioned, to achieve Remote Code Execution (RCE), it’s necessary to chain several security vulnerabilities that can then be compiled to weaponize the exploit. During my review of the /contact-us page, I observed a specific attachment feature designed to enable customers to contact customer-support agents for issues that require visual aid for better understanding and quick help.

However, submitting a PNG image as an attachment that embeds HTML/JavaScript code results in it being interpreted and rendered by the browser on the backend as HTML/JavaScript, instead of as a .PNG file. Leveraging this issue, I crafted an HTML document containing JavaScript code to automate a Cross-Site Request Forgery (CSRF) attack against the PrestaShop administration panel. This script initiates a GET request to the PrestaShop admin panel to retrieve an initial token from the response HTML. It then uses this token to make another GET request to the same URL, appending the token as a query parameter to extract a CSRF token from an input field within the HTML response <input type="hidden" id="import_theme__token" name="import_theme[_token]" value="random_string_token"/>. With both tokens acquired, I construct a form data payload to submit a POST request to POST /admin/index.php/improve/design/themes/import. This request aims to import a malicious theme from my server.

Exploiting the XSS Vulnerability

  1. Create a malicious PNG file (test.png) containing an HTML payload designed to execute JavaScript code. Example payload: <script>alert(document.domain)</script>.

  2. As an unauthenticated user, navigate to the http://prestashop:8000/contact-us page, fill out the contact form, and make sure you provide an email ID that is already registered. Attach the malicious test.png file and submit the form.

  3. A Customer Service agent receives a notification including the malicious attachment.

image

When the CS agent views the attachment, the XSS payload is executed, establishing the initial foothold for further exploitation.

image

Escalating to Remote Code Execution

Create a malicious theme by downloading any theme and adding a malicious PHP file that triggers a connection. e.g., https://github.com/pentestmonkey/php-reverse-shell/blob/master/php-reverse-shell.php I attached a zip theme file that was used for testing and the same ZIP contains a .htaccess file with the content below, which will enforce and allow access to the directory where we host our reverse_shell.php. It’s basically to allow everyone to view or interact with the contents of the directory without restrictions, because if we didn’t do this, we wouldn’t be able to access our reverse_shell.php that grants us a reverse shell and we would get forbidden instead.

<IfModule mod_authz_core.c>
 Require all granted
</IfModule>
  1. Change http://<server-ip>:[port]/malicious_theme.zip to your instance that hosts your malicious theme.
  2. Run the exploit.py script to automate the delivery of exploit.html through the /contact-us page, while attaching the malicious HTML file.
  3. When the CS agent opens the Attachment on the message that will be received, the attacker will receive a command shell on the instance running PrestaShop.

Automatic Exploitation

  1. Download the weaponized CVE-2024-34716 proof of concept from https://github.com/aelmokhtar/CVE-2024-34716_PoC using this terminal command: git clone git@github.com:aelmokhtar/CVE-2024-34716_PoC.git
  2. Serve your modified theme.zip over HTTP, and modify the exploit.html according to your needs.
  3. Run pip install -r requirements.txt; python CVE-2024-34716_PoC/exploit.py and follow the prompts to input the target URL, email address, message content, and path to your exploit.html file. The script handles token extraction, session handling, and submission of the malicious form data.
  4. The script initiates a Netcat listener on the attacker’s machine to catch the reverse shell, providing direct command-line access to the server.
  5. Once the CS agent opens the Attachment link, you will be prompted with a shell command to control the vulnerable machine instance hosting PrestaShop.

    Video Proof of Concept:

Remediation

The vulnerability was caused by improper handling of uploaded files, specifically the lack of validation and sanitation of file contents, which allowed attackers to upload files with embedded HTML/JavaScript code that could be executed when viewed by an administrator or customer service agent. This issue was compounded by insufficient MIME type enforcement and the absence of security headers to prevent browsers from interpreting files as something other than their declared content type. The implemented remediation involved validating file extensions against a whitelist, enforcing correct MIME types, setting security headers (such as X-Content-Type-Options: nosniff) to prevent MIME type sniffing, and ensuring non-image files are downloaded as attachments rather than executed, while image files are handled with BinaryFileResponse to guarantee proper display.

The code below defines a list of allowed file extensions and their corresponding MIME types. This ensures only specific types of files are accepted.

private const allowedExtensions = [
    'txt' => 'text/plain',
    'rtf' => 'application/rtf',
    'doc' => 'application/msword',
    'docx' => 'application/msword',
    'pdf' => 'application/pdf',
    'zip' => 'multipart/x-zip',
    'png' => 'image/png',
    'jpeg' => 'image/jpeg',
    'gif' => 'image/gif',
    'jpg' => 'image/jpeg',
    'webp' => 'image/webp',
];

private const allowedImageExtensions = [
    'png' => 'image/png',
    'jpeg' => 'image/jpeg',
    'gif' => 'image/gif',
    'jpg' => 'image/jpeg',
    'webp' => 'image/webp',
];

And then it checks the file name for a valid extension. It ensures there are not too many extensions (which could be a sign of an attack) and that the extension is in the allowed list.

$fileExtensions = explode('.', $fileName);
if (count($fileExtensions) > 2) {
    throw new PrestaShopException('Too many extensions for ' . $fileName);
} elseif (!array_key_exists($fileExtensions[1], self::allowedExtensions)) {
    throw new PrestaShopException('Invalid extension for ' . $fileName);
}

If the file is not an image, it is treated as an attachment, ensuring it is downloaded rather than executed inline. The X-Content-Type-Options header is set to nosniff to prevent MIME type sniffing by the browser, but if the file is an image, it is served

with BinaryFileResponse, ensuring the correct content type header is set. This guarantees the image is displayed properly.

} else {
    try {
        $response = new BinaryFileResponse($this->uploadDir + $fileName);
    } catch (FileNotFoundException $e) {
        throw new NotFoundHttpException();
    }

    $response->headers->set('Content-type', self::allowedExtensions[$fileExtensions[1]]);
}

prestashop\SecuredFileReaderController.php

Disclosure Timeline

[09 April 2024]:

[10 April 2024]:

[14 May 2024]:

References