CVE-2026-3143

Total Upkeep <= 1.17.1 - Missing Authorization to Unauthenticated Rollback Cancellation

mediumMissing Authorization
5.3
CVSS Score
5.3
CVSS Score
medium
Severity
1.17.2
Patched in
2d
Time to patch

Description

The Total Upkeep – WordPress Backup Plugin plus Restore & Migrate by BoldGrid plugin for WordPress is vulnerable to unauthorized modification of data due to a missing capability check on the 'wp_ajax_cli_cancel' function in all versions up to, and including, 1.17.1. This makes it possible for unauthenticated attackers to cancel a pending rollback, potentially preventing a WordPress installation from automatically reverting a failed update.

CVSS Vector Breakdown

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

Technical Details

Affected versions<=1.17.1
PublishedApril 30, 2026
Last updatedMay 1, 2026
Affected pluginboldgrid-backup

What Changed in the Fix

Changes introduced in v1.17.2

Loading patch diff...

Source Code

WordPress.org SVN
Research Plan
Unverified

This research plan targets **CVE-2026-3143**, a missing authorization vulnerability in the **Total Upkeep** plugin for WordPress. This vulnerability allows an unauthenticated attacker to cancel a pending site rollback, which is a critical safety feature that automatically restores a site if an updat…

Show full research plan

This research plan targets CVE-2026-3143, a missing authorization vulnerability in the Total Upkeep plugin for WordPress. This vulnerability allows an unauthenticated attacker to cancel a pending site rollback, which is a critical safety feature that automatically restores a site if an update fails.

1. Vulnerability Summary

  • Vulnerability: Missing Authorization (Unauthenticated data modification).
  • Vulnerable Action: cli_cancel.
  • Plugin: Total Upkeep (slug: boldgrid-backup).
  • Affected Versions: <= 1.17.1.
  • Fixed Version: 1.17.2.
  • Nature of Flaw: The plugin registers an AJAX handler (either wp_ajax_cli_cancel or wp_ajax_nopriv_cli_cancel) that calls a function to delete the boldgrid_backup_pending_rollback site option and clear related cron jobs. The handler lacks capability checks (current_user_can) and nonce validation, allowing unauthenticated requests to trigger it.

2. Attack Vector Analysis

  • Endpoint: /wp-admin/admin-ajax.php.
  • HTTP Method: POST (standard for AJAX) or GET.
  • Action Parameter: action=cli_cancel.
  • Authentication: Not required (Unauthenticated).
  • Preconditions: A "pending rollback" must exist for the exploit to have a measurable impact (i.e., the boldgrid_backup_pending_rollback site option must be set).

3. Code Flow

  1. Entry Point: An HTTP request hits admin-ajax.php?action=cli_cancel.
  2. Hook Trigger: WordPress fires the wp_ajax_nopriv_cli_cancel (or wp_ajax_cli_cancel) hook.
  3. Handler Execution: The plugin's registered handler function (likely in an admin core class, calling Boldgrid_Backup_Admin_Auto_Rollback::cancel) is executed.
  4. Vulnerable Method: Boldgrid_Backup_Admin_Auto_Rollback::cancel() (in admin/class-boldgrid-backup-admin-auto-rollback.php):
    • It calls $this->core->cron->delete_cron_entries( 'restore' ) to stop the scheduled rollback.
    • It calls $this->core->settings->delete_rollback_option() to delete the boldgrid_backup_pending_rollback site option.
  5. Sink: delete_site_option( 'boldgrid_backup_pending_rollback' ).

4. Nonce Acquisition Strategy

The vulnerability description ("Missing Authorization") and the severity (Medium, Unauthenticated) strongly suggest that no nonce is validated for this specific action.

  • Verification: If the exploit fails with a 403 or -1 response, check for nonce usage in the plugin files by searching for check_ajax_referer or wp_verify_nonce near the cli_cancel action registration.
  • Localization: If a nonce is required, it would likely be localized in boldgrid-backup-admin-backup-now.js under a variable name like boldgrid_backup_admin_backup_now (inferred from the script handle boldgrid-backup-admin-backup-now in enqueue_backup_scripts).

5. Exploitation Strategy

The goal is to prove that an unauthenticated user can delete the boldgrid_backup_pending_rollback option.

Step 1: Setup "Pending Rollback" State
An attacker can't easily trigger a real failed update, so we will simulate the state by manually creating the site option.

  • Command: wp site option update boldgrid_backup_pending_rollback '{"filepath":"/tmp/dummy-backup.zip","deadline":9999999999}' --format=json

Step 2: Send the Exploit Request

  • Tool: http_request
  • Method: POST
  • URL: {{base_url}}/wp-admin/admin-ajax.php
  • Headers: Content-Type: application/x-www-form-urlencoded
  • Body: action=cli_cancel

Step 3: Verification
Confirm that the site option has been deleted.

6. Test Data Setup

  1. Plugin Installation: Install Total Upkeep version 1.17.1.
  2. Configuration: Enable the plugin.
  3. Data Creation: Populate the rollback option to provide a target for deletion.
    wp site option update boldgrid_backup_pending_rollback '{"filepath":"/tmp/poc.zip","mode":"backup"}' --format=json
    
  4. Verification of Setup:
    wp site option get boldgrid_backup_pending_rollback
    

7. Expected Results

  • Successful Request: The server returns a 200 OK response (likely with 0, 1, or an empty response, as many AJAX handlers don't return specific data).
  • System Impact: The site option boldgrid_backup_pending_rollback is removed from the database.
  • Cron Impact: Any crons with the hook containing restore are removed (viewable via wp cron event list).

8. Verification Steps

  1. Database Check:
    wp site option get boldgrid_backup_pending_rollback
    
    Expectation: The command should return an error or empty result (Option does not exist).
  2. Cron Check:
    wp cron event list | grep restore
    
    Expectation: No entries should be found.

9. Alternative Approaches

If cli_cancel does not respond to unauthenticated requests, check if the plugin registers a REST API endpoint for the same purpose:

  • Search for register_rest_route in the plugin directory.
  • Look for routes containing rollback or cancel.
  • If a REST endpoint exists, check its permission_callback for __return_true or missing capability checks.

If the action name is slightly different, use grep to find the registration:

grep -r "wp_ajax_.*cancel" .

Verify if wp_ajax_nopriv_ is present for that action.

Research Findings
Static analysis — not yet PoC-verified

Summary

Total Upkeep (<= 1.17.1) contains a missing authorization vulnerability in its rollback cancellation mechanism. An unauthenticated attacker can cancel a pending site rollback by sending a request to the unprivileged AJAX handler, potentially preventing the site from automatically reverting to a stable state after a failed update.

Vulnerable Code

// admin/class-boldgrid-backup-admin-auto-rollback.php line 1205
	public function wp_ajax_cli_cancel() {
		$backup_id_match = ! empty( $_GET['backup_id'] ) && $this->core->get_backup_identifier() === sanitize_key( $_GET['backup_id'] ); // phpcs:ignore WordPress.CSRF.NonceVerification.NoNonceVerification

		if ( $backup_id_match ) {
			$this->cancel();
			wp_send_json_success( __( 'Rollback canceled', 'boldgrid-backup' ) );
		} else {
			wp_send_json_error( __( 'Error: Backup ID match failed', 'boldgrid-backup' ) );
		}
	}

Security Fix

diff -ru /home/deploy/wp-safety.org/data/plugin-versions/boldgrid-backup/1.17.1/admin/class-boldgrid-backup-admin-auto-rollback.php /home/deploy/wp-safety.org/data/plugin-versions/boldgrid-backup/1.17.2/admin/class-boldgrid-backup-admin-auto-rollback.php
--- /home/deploy/wp-safety.org/data/plugin-versions/boldgrid-backup/1.17.1/admin/class-boldgrid-backup-admin-auto-rollback.php	2021-07-22 18:44:38.000000000 +0000
+++ /home/deploy/wp-safety.org/data/plugin-versions/boldgrid-backup/1.17.2/admin/class-boldgrid-backup-admin-auto-rollback.php	2026-03-11 16:28:56.000000000 +0000
@@ -205,6 +205,9 @@
 
 		// Remove WP option boldgrid_backup_pending_rollback.
 		$this->core->settings->delete_rollback_option();
+
+		// Remove the one-time CLI cancel secret.
+		delete_site_option( 'boldgrid_backup_cli_cancel_secret' );
 	}
 
 	/**
@@ -1194,16 +1197,23 @@
 	/**
 	 * Callback function for canceling a pending rollback from the cli process.
 	 *
-	 * This admin-ajax call is unprovileged, so that the CLI script can make the call.
-	 * The only validation that we use is the backup identifier.
-	 * Nobody will be trying to cancel rollbacks (with a 15-minute window) anyways.
+	 * This admin-ajax call is unprivileged, so that the CLI script can make the call.
+	 * Validation requires both the backup identifier and a one-time random secret that
+	 * was generated when the restore cron job was scheduled.
 	 *
 	 * @since 1.10.7
 	 */
 	public function wp_ajax_cli_cancel() {
-		$backup_id_match = ! empty( $_GET['backup_id'] ) && $this->core->get_backup_identifier() === sanitize_key( $_GET['backup_id'] ); // phpcs:ignore WordPress.CSRF.NonceVerification.NoNonceVerification
+		// phpcs:ignore WordPress.CSRF.NonceVerification.Recommended
+		$backup_id_match = ! empty( $_GET['backup_id'] ) && $this->core->get_backup_identifier() === sanitize_key( $_GET['backup_id'] );
+
+		$stored_secret  = get_site_option( 'boldgrid_backup_cli_cancel_secret', '' );
 
-		if ( $backup_id_match ) {
+		// phpcs:ignore WordPress.CSRF.NonceVerification.Recommended
+		$secret_match = ! empty( $stored_secret ) && ! empty( $_GET['cli_cancel_secret'] ) &&
+			hash_equals( $stored_secret, sanitize_text_field( wp_unslash( $_GET['cli_cancel_secret'] ) ) );
+		
+		if ( $backup_id_match && $secret_match ) {
 			$this->cancel();
 			wp_send_json_success( __( 'Rollback canceled', 'boldgrid-backup' ) );
 		} else {

Exploit Outline

To exploit this vulnerability, an attacker targets the unprivileged AJAX action used for CLI-initiated rollback cancellations. 1. Target Endpoint: The standard WordPress AJAX endpoint `/wp-admin/admin-ajax.php`. 2. Payload: The request must include the `action` parameter set to either `cli_cancel` or `boldgrid_cli_cancel_rollback` (depending on the internal mapping) and a valid `backup_id` parameter. 3. Identifying Backup ID: The `backup_id` is an identifier generated by the plugin for the site, often discoverable or deterministic (e.g., based on hashes of site metadata). 4. Execution: By sending a GET or POST request with these parameters, an unauthenticated user triggers the `Boldgrid_Backup_Admin_Auto_Rollback::cancel()` method. 5. Impact: This method deletes the `boldgrid_backup_pending_rollback` site option and clears any scheduled restoration cron jobs. This effectively neutralizes the safety net that would have automatically restored the site if a current update process failed, leaving the site in a broken state if the update does not complete successfully.

Check if your site is affected.

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