JetBackup <= 3.1.19.8 - Authenticated (Administrator+) Arbitrary Directory Deletion via Path Traversal in 'fileName' Parameter
Description
The JetBackup – Backup, Restore & Migrate plugin for WordPress is vulnerable to Path Traversal leading to Arbitrary Directory Deletion in versions up to and including 3.1.19.8. This is due to insufficient input validation on the fileName parameter in the file upload handler. The plugin sanitizes the fileName parameter using sanitize_text_field(), which removes HTML tags but does not prevent path traversal sequences like '../'. The unsanitized filename is then directly concatenated in Upload::getFileLocation() without using basename() or validating the resolved path stays within the intended directory. When an invalid file is uploaded, the cleanup logic calls dirname() on the traversed path and passes it to Util::rm(), which recursively deletes the entire resolved directory. This makes it possible for authenticated attackers with administrator-level access to traverse outside the intended upload directory and trigger deletion of critical WordPress directories such as wp-content/plugins, effectively disabling all installed plugins and causing severe site disruption.
CVSS Vector Breakdown
CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:U/C:H/I:N/A:NTechnical Details
What Changed in the Fix
Changes introduced in v3.1.20.3
Source Code
WordPress.org SVNThis research plan outlines the steps to exploit a Path Traversal vulnerability in JetBackup <= 3.1.19.8, which allows an authenticated administrator to delete arbitrary directories on the WordPress server. ### 1. Vulnerability Summary * **Vulnerability:** Path Traversal leading to Arbitrary Dire…
Show full research plan
This research plan outlines the steps to exploit a Path Traversal vulnerability in JetBackup <= 3.1.19.8, which allows an authenticated administrator to delete arbitrary directories on the WordPress server.
1. Vulnerability Summary
- Vulnerability: Path Traversal leading to Arbitrary Directory Deletion.
- Location: The
fileNameparameter in the file upload handler, specifically used inUpload::getFileLocation()and cleaned up viaUtil::rm(dirname(...)). - Cause: The plugin uses
sanitize_text_field()on thefileNameparameter. While this removes HTML tags, it does not strip path traversal sequences (../). The resulting path is concatenated without validation orbasename(). When the plugin determines the "uploaded" file is invalid (e.g., not a.taror.tar.gzfile), it attempts to clean up by recursively deleting the parent directory of the resolved path.
2. Attack Vector Analysis
- Endpoint:
/wp-admin/admin-ajax.php - Action:
jetbackup(The plugin routes its AJAX calls through this central action). - Sub-action (Call):
Upload(Inferred from the pattern seen insrc/JetBackup/Ajax/Calls/AddToQueue.php). - Vulnerable Parameter:
fileName - Authentication: Required (Administrator or higher).
- Preconditions: A valid nonce for the
jetbackupaction must be obtained.
3. Code Flow
- Entry Point: An authenticated administrator sends a POST request to
admin-ajax.php?action=jetbackupwithcall=Upload. - Input Processing:
src/JetBackup/Ajax/Calls/Upload::execute()(inferred) retrieves thefileNameusing a wrapper aroundsanitize_text_field(). - Path Resolution: The plugin calls
JetBackup\Upload\Upload::getFileLocation(), which concatenates a base temporary directory with the user-providedfileName(e.g.,wp-content/uploads/jetbackup/temp/+../../../../wp-content/plugins/poc.txt). - Validation Failure: The plugin checks if the file is a valid backup (
.taror.tar.gz). If the payload sent is plain text or invalid, validation fails. - Triggering Sink: Upon validation failure, the plugin attempts to clean up the temporary data. It calls
dirname()on the resolved traversal path:dirname('/var/www/html/wp-content/plugins/poc.txt')→/var/www/html/wp-content/plugins/. - The Sink: The resulting directory path is passed to
JetBackup\Entities\Util::rm(), which recursively deletes the entire directory.
4. Nonce Acquisition Strategy
JetBackup localizes its configuration and nonces into a JavaScript object named window.PAGE.
- Identify Trigger: The plugin scripts load on the JetBackup admin pages. The main slug is
backup. - Navigation: Use
browser_navigateto/wp-admin/admin.php?page=backup. - Extraction: Execute JavaScript to retrieve the nonce:
browser_eval("window.PAGE?.nonce") - Verification: Check if
window.PAGE.nonceexists. If not, check for other keys likewindow.PAGE.ajax_nonceorwindow.JB_NONCE.
5. Exploitation Strategy
We will target a dummy directory in the uploads folder to avoid breaking the test environment.
Step 1: Test Data Setup
Create a target directory and a file inside it to verify deletion.
wp eval "wp_mkdir_p(WP_CONTENT_DIR . '/uploads/exploit-target/subdir');"
wp eval "file_put_contents(WP_CONTENT_DIR . '/uploads/exploit-target/poc.txt', 'test');"
Step 2: Obtain Nonce
Navigate to the JetBackup dashboard and extract the nonce as described in Section 4.
Step 3: Trigger Directory Deletion
Send a POST request to the AJAX endpoint with the traversal payload. We will simulate a chunked upload completion with an invalid file to trigger the cleanup logic.
- URL:
http://localhost:8080/wp-admin/admin-ajax.php - Content-Type:
application/x-www-form-urlencoded - Body:
(Note:action=jetbackup call=Upload nonce=[NONCE] fileName=../../../../wp-content/uploads/exploit-target/poc.txt fileUploadId=exploit_session_01 fileSize=10 chunk=bm90X2FfdGFyX2ZpbGU= isLastChunk=1chunkis base64 for "not_a_tar_file".isLastChunk=1ensures the validation and subsequent cleanup logic are triggered).
6. Expected Results
- Response: The server should return a JSON response, likely containing an error message from
exceptions.json:"Invalid backup file provided. Only .tar or .tar.gz files are allowed.". - Filesystem Impact: The directory
/wp-content/uploads/exploit-target/and all its contents (includingpoc.txt) should be recursively deleted.
7. Verification Steps
After the HTTP request, verify the directory is gone using WP-CLI:
# This should return an error or empty if the directory was deleted
wp eval "echo is_dir(WP_CONTENT_DIR . '/uploads/exploit-target') ? 'exists' : 'deleted';"
8. Alternative Approaches
If the Upload call is not directly accessible or different parameters are required:
- Target
AddToQueue: Useaction=jetbackup&call=AddToQueue. Providetype=2(Restore),fileNamewith traversal, and afileUploadId. The_queueRestore()method might trigger the sameUploadobject logic when it fails to find a valid backup at the traversed path. - Target Plugin Directory: If the dummy directory approach fails, target a non-essential directory like
wp-content/languages/pluginsto prove impact. - Check for
fileManagerparameters: Some versions of JetBackup use afileManagerarray inAddToQueue. If the upload path fails, try injecting the traversal intofolderList[]orfileManager[]parameters used during extraction/cleanup phases.
Summary
JetBackup for WordPress is vulnerable to arbitrary directory deletion because its file upload handler fails to sanitize the 'fileName' parameter against path traversal. An authenticated administrator can exploit this by providing a path that traverses out of the temporary upload directory, which the plugin then recursively deletes during a cleanup phase after the uploaded file fails format validation.
Vulnerable Code
// In src/JetBackup/Ajax/Calls/AddToQueue.php (and similarly in the missing Upload call) private function _getFileName():string { return $this->getUserInput('fileName', '', UserInput::STRING); } --- // In src/JetBackup/Upload/Upload.php (Inferred from research plan/description) public function getFileLocation() { return $this->getTempDir() . DIRECTORY_SEPARATOR . $this->fileName; } --- // In the file upload cleanup logic (Inferred from research plan/description) if (!$this->isValidBackup($file)) { Util::rm(dirname($file)); }
Security Fix
@@ -75,6 +75,7 @@ "Added to queue!": "Added to queue!", "No upload file was provided": "No upload file was provided", "No upload file size was provided": "No upload file size was provided", + "Invalid filename: ": "Invalid filename: ", "Invalid upload id was provided": "Invalid upload id was provided", "Failed writing chunk to upload file. Error: %s": "Failed writing chunk to upload file. Error: %s", "Invalid backup file provided. Only .tar or .tar.gz files are allowed.": "Invalid backup file provided. Only .tar or .tar.gz files are allowed.", // Inferred logic fix applied in 3.1.20.3: // Validation added to fileName to ensure it does not contain path traversal characters (../) // or using basename() before path concatenation.
Exploit Outline
1. Authentication: Log in to the WordPress admin dashboard as a user with Administrator privileges. 2. Nonce Acquisition: Navigate to the JetBackup dashboard (e.g., /wp-admin/admin.php?page=backup) and extract the AJAX nonce from the localized JavaScript variable `window.PAGE.nonce`. 3. Identify Target: Choose a target directory for deletion (e.g., `wp-content/plugins/some-plugin`). 4. Trigger Deletion: Send a POST request to `/wp-admin/admin-ajax.php?action=jetbackup` with `call=Upload`. 5. Payload Shaping: - Set `fileName` to a traversal path reaching the target directory: `../../../../wp-content/plugins/some-plugin/poc.txt`. - Set `isLastChunk=1` and provide an invalid backup file chunk (e.g., plain text instead of a TAR archive). 6. Cleanup Trigger: The plugin will receive the 'chunk', attempt to validate it as part of a backup file, and fail. The error handler then calls `Util::rm()` on the `dirname()` of the traversed path, recursively deleting the target directory and all its contents.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.