CVE-2026-8996

Backup and Staging by WP Time Capsule <= 1.22.26 - Missing Authorization to Authenticated (Subscriber+) Sensitive Information Exposure via download_recent_decrypted_file_wptc Function

mediumMissing Authorization
6.5
CVSS Score
6.5
CVSS Score
medium
Severity
1.22.27
Patched in
1d
Time to patch

Description

The Backup and Staging by WP Time Capsule plugin for WordPress is vulnerable to Sensitive Information Exposure in all versions up to, and including, 1.22.26 via the download_recent_decrypted_file_wptc. This makes it possible for authenticated attackers, with subscriber-level access and above, to extract download the most recently admin-decrypted SQL database backup, which typically contains password hashes, user credentials, and other sensitive site configuration data stored in the 'recent_decrypted_file' option. Exploitation requires that an administrator has previously performed a decrypt action, causing the decrypted SQL backup file to exist in the plugin's upload directory; without this prior admin action, there is no file to serve.

CVSS Vector Breakdown

CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:N
Attack Vector
Network
Attack Complexity
Low
Privileges Required
Low
User Interaction
None
Scope
Unchanged
High
Confidentiality
None
Integrity
None
Availability

Technical Details

Affected versions<=1.22.26
PublishedJuly 8, 2026
Last updatedJuly 9, 2026
Affected pluginwp-time-capsule

What Changed in the Fix

Changes introduced in v1.22.27

Loading patch diff...

Source Code

WordPress.org SVN
Research Plan
Unverified

# Exploitation Research Plan: CVE-2026-8996 ## 1. Vulnerability Summary The **Backup and Staging by WP Time Capsule** plugin (up to version 1.22.26) is vulnerable to **Sensitive Information Exposure**. The plugin implements an AJAX handler `download_recent_decrypted_file_wptc` which is intended to …

Show full research plan

Exploitation Research Plan: CVE-2026-8996

1. Vulnerability Summary

The Backup and Staging by WP Time Capsule plugin (up to version 1.22.26) is vulnerable to Sensitive Information Exposure. The plugin implements an AJAX handler download_recent_decrypted_file_wptc which is intended to allow administrators to download SQL database backups after they have been decrypted for restoration or cloning. However, the function fails to perform adequate authorization checks (e.g., current_user_can('manage_options')). Consequently, any authenticated user with Subscriber-level permissions or higher can trigger this function to download the most recently decrypted SQL backup, which contains sensitive information including WordPress user password hashes, site configuration, and database structure.

2. Attack Vector Analysis

  • Endpoint: /wp-admin/admin-ajax.php
  • Action: download_recent_decrypted_file_wptc
  • Method: GET or POST (WordPress AJAX handlers typically support both, but the name implies a download/GET context).
  • Authentication: Authenticated (Subscriber-level or higher).
  • Vulnerable Parameter: The action parameter triggers the code; the file path is retrieved internally from the database option recent_decrypted_file.
  • Precondition: An administrator must have previously used the "Decrypt" feature on an encrypted database backup. This action populates the recent_decrypted_file option and ensures the decrypted file exists in the plugin's temporary storage directory.

3. Code Flow

  1. Entry Point: The request is sent to admin-ajax.php with action=download_recent_decrypted_file_wptc.
  2. Hook Registration: The plugin registers the action (likely in Classes/WptcBackup/HooksHandler.php or Classes/Common/HooksHandler.php - inferred):
    add_action('wp_ajax_download_recent_decrypted_file_wptc', 'download_recent_decrypted_file_wptc');
  3. Vulnerable Function Execution: The function download_recent_decrypted_file_wptc is called.
  4. Option Retrieval: The code calls get_option('recent_decrypted_file') to retrieve the absolute path of the most recently decrypted SQL file.
  5. Information Exposure (Sink): The function uses readfile() or similar file output functions to send the contents of the SQL file to the user's browser without checking if the current user has the manage_options capability.

4. Nonce Acquisition Strategy

The WP Time Capsule plugin typically localizes its AJAX settings and nonces into a JavaScript object. To find the correct nonce:

  1. Identify Script Localization: The plugin usually enqueues scripts on its admin pages. We can find the nonce by creating a page that loads the plugin context or by accessing a default admin dashboard as a subscriber (where some WPTC scripts may still load).
  2. Create Trigger Content: If the script only loads on specific pages, we can use WP-CLI to create a page with a WPTC-related shortcode if any exist (e.g., [wptc_test]).
  3. Browser Extraction:
    • Navigate to the WordPress dashboard (/wp-admin/index.php) as a Subscriber.
    • Use browser_eval to search for the WPTC AJAX object.
    • Common Variable Name: window.wptc_ajax_object (inferred from common plugin patterns).
    • Common Nonce Key: window.wptc_ajax_object.nonce.
  4. Check for Nonce Enforcement: If download_recent_decrypted_file_wptc was designed for direct file download, it might lack a nonce check entirely, as the primary vulnerability is the missing authorization.

5. Exploitation Strategy

  1. Pre-Exploit Setup (Internal Site): As an admin, run a backup and use the "Decrypt DB" feature to ensure the recent_decrypted_file option is set.
  2. Authentication: Obtain session cookies for a user with Subscriber role.
  3. Discovery: Retrieve the nonce if required (see Section 4).
  4. Execution: Use the http_request tool to perform a GET request to the AJAX endpoint.
    • URL: https://[target]/wp-admin/admin-ajax.php?action=download_recent_decrypted_file_wptc
    • Headers: Include Cookie: [subscriber_cookies]
  5. Payload Extraction: If successful, the response body will contain a raw SQL dump of the WordPress database.

6. Test Data Setup

To verify the vulnerability, the environment must be prepared:

  1. Install Plugin: Install version 1.22.26 of Backup and Staging by WP Time Capsule.
  2. Create Users:
    • Admin: admin_user / password
    • Subscriber: attacker_sub / password
  3. Configure Plugin: As Admin, connect a cloud storage (or use local/staging mode) and perform at least one backup with Database Encryption enabled.
  4. Trigger Decryption: In the WPTC dashboard, find the database backup and select the "Decrypt" option. This will place a file in wp-content/uploads/tCapsule/backups/.../filename.sql.
  5. Confirm Option: Use WP-CLI to verify the path is stored:
    wp option get recent_decrypted_file

7. Expected Results

  • Successful Exploitation: The http_request returns a 200 OK status, and the response body begins with SQL dump headers (e.g., INSERT INTO wp_users ... or -- WordPress MySQL dump).
  • Failed Exploitation (Patched): The response is 403 Forbidden or 0 (WordPress default for unauthorized AJAX).

8. Verification Steps

After the HTTP request, confirm the data is valid:

  1. Inspect Response: Check the response body for the string wp_users.
  2. Check for Password Hashes: Verify that the output contains the password hash of the admin user (e.g., strings starting with $P$B).
  3. WP-CLI Verification: Confirm the file downloaded matches the one stored in the path found via wp option get recent_decrypted_file.

9. Alternative Approaches

  • Path Traversal Check: If the function accepts a filename parameter (e.g., ?file=...), check if it's vulnerable to path traversal (e.g., ../../../../wp-config.php). Based on the CVE, the path is likely hardcoded to the option value, but this should be checked.
  • Direct Access: Check if the decrypted file path (retrieved from the option via WP-CLI for testing) is directly accessible via a URL (e.g., https://[target]/wp-content/uploads/tCapsule/backups/.../dump.sql). The plugin should ideally use an .htaccess or index.html to block this, but the AJAX handler provides a direct bypass.
Research Findings
Static analysis — not yet PoC-verified

Summary

The Backup and Staging by WP Time Capsule plugin for WordPress is vulnerable to Sensitive Information Exposure via the `download_recent_decrypted_file_wptc` function. Due to missing authorization and nonce checks, authenticated attackers with subscriber-level access or higher can download the site's most recently decrypted SQL database backup, provided an administrator has recently performed a decryption action.

Vulnerable Code

// wp-time-capsule.php L3767
function download_recent_decrypted_file_wptc(){
	if ( !is_admin() ) {
		return ;
	}

	$wptc_file_path = WPTC_Factory::get('config')->get_option('recent_decrypted_file');

	wptc_log($wptc_file_path,'-----------$wptc_file_path----------------');

Security Fix

--- /home/deploy/wp-safety.org/data/plugin-versions/wp-time-capsule/1.22.26/wp-time-capsule.php	2026-05-07 05:18:24.000000000 +0000
+++ /home/deploy/wp-safety.org/data/plugin-versions/wp-time-capsule/1.22.27/wp-time-capsule.php	2026-06-10 05:33:14.000000000 +0000
@@ -3687,7 +3687,9 @@
 
 	WPTC_Factory::get('config')->set_option('recent_decrypted_file', $result['fullpath']);
 
-	$result['message'] = "Decryption Completed. <a href=" . network_admin_url() . "?page=wp-time-capsule-settings&download=1#wp-time-capsule-tab-advanced>Download your file here</a>. After downloaded <a href='#' id='wptc-clear-all-decrypt-files'>click here</a> to delete the file for security reason.";
+	$wptc_download_decrypt_nonce = wp_create_nonce( 'download_decrypt' );
+
+	$result['message'] = "Decryption Completed. <a href=" . network_admin_url() . "?page=wp-time-capsule-settings&download=1&wptc_download_decrypt_nonce=" . $wptc_download_decrypt_nonce . "#wp-time-capsule-tab-advanced>Download your file here</a>. After downloaded <a href='#' id='wptc-clear-all-decrypt-files'>click here</a> to delete the file for security reason.";
 
 	wptc_die_with_json_encode($result);
 }
@@ -3767,6 +3769,14 @@
 		return ;
 	}
 
+	if (! isset( $_GET['wptc_download_decrypt_nonce'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_GET['wptc_download_decrypt_nonce'] ) ), 'download_decrypt' ) ) {
+		return;
+	}
+
+	if(!is_user_logged_in() || !current_user_can('manage_options')){
+		return;
+	}
+
 	$wptc_file_path = WPTC_Factory::get('config')->get_option('recent_decrypted_file');
 
 	wptc_log($wptc_file_path,'-----------$wptc_file_path----------------');

Exploit Outline

The exploit targets the AJAX action 'download_recent_decrypted_file_wptc'. An attacker needs authenticated access (Subscriber level or higher) and must send a request to /wp-admin/admin-ajax.php with the 'action' parameter set to 'download_recent_decrypted_file_wptc'. The vulnerability exists because the function only checks is_admin(), which is always true for AJAX requests, and fails to check user capabilities (current_user_can) or nonces. If an administrator has recently used the plugin's decryption tool, the plugin will serve the decrypted SQL database backup file stored in the 'recent_decrypted_file' option directly to the attacker.

Check if your site is affected.

Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.