CVE-2026-32539

PublishPress Revisions: Duplicate Posts, Submit, Approve and Schedule Content Changes <= 3.7.23 - Unauthenticated SQL Injection

highImproper Neutralization of Special Elements used in an SQL Command ('SQL Injection')
7.5
CVSS Score
7.5
CVSS Score
high
Severity
3.7.24
Patched in
7d
Time to patch

Description

The PublishPress Revisions: Duplicate Posts, Submit, Approve and Schedule Content Changes plugin for WordPress is vulnerable to SQL Injection in versions up to, and including, 3.7.23 due to insufficient escaping on the user supplied parameter and lack of sufficient preparation on the existing SQL query. This makes it possible for unauthenticated attackers to append additional SQL queries into already existing queries that can be used to extract sensitive information from the database.

CVSS Vector Breakdown

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

Technical Details

Affected versions<=3.7.23
PublishedMarch 20, 2026
Last updatedMarch 26, 2026
Affected pluginrevisionary

What Changed in the Fix

Changes introduced in v3.7.24

Loading patch diff...

Source Code

WordPress.org SVN
Research Plan
Unverified

```markdown # Exploitation Research Plan: CVE-2026-32539 (PublishPress Revisions SQL Injection) ## 1. Vulnerability Summary The PublishPress Revisions plugin (<= 3.7.23) contains an unauthenticated SQL injection vulnerability. The issue originates from the `Revisionary_Archive_List_Table` and `Revi…

Show full research plan
# Exploitation Research Plan: CVE-2026-32539 (PublishPress Revisions SQL Injection)

## 1. Vulnerability Summary
The PublishPress Revisions plugin (<= 3.7.23) contains an unauthenticated SQL injection vulnerability. The issue originates from the `Revisionary_Archive_List_Table` and `Revisionary_List_Table` classes (and associated global filters), where user-supplied parameters from `$_REQUEST` are insufficiently sanitized (using `sanitize_text_field` instead of `prepare`) and then concatenated into SQL queries. Specifically, parameters like `origin_post_type` and `s` are used to build a `$base_query` which is then interpolated into further SQL statements. Because the plugin failed to properly enforce capabilities for the Revision Archive and Queue screens in affected versions, these endpoints—and the underlying vulnerable queries—are accessible to unauthenticated attackers.

## 2. Attack Vector Analysis
- **Endpoint**: `/wp-admin/admin.php?page=revisionary-archive` (The Past Revisions screen).
- **Vulnerable Parameter**: `origin_post_type` (and potentially `s`).
- **Authentication**: Unauthenticated (due to improper capability enforcement in version 3.7.23).
- **Action**: A GET or POST request to the admin page with an SQL injection payload in the `origin_post_type` parameter.

## 3. Code Flow
1. **Entry Point**: A request is made to `
Research Findings
Static analysis — not yet PoC-verified

Summary

The PublishPress Revisions plugin for WordPress is vulnerable to unauthenticated SQL injection due to improper preparation of user-supplied parameters like 'origin_post_type' and 's' within the revision archive and queue list tables. Because version 3.7.23 and earlier failed to enforce administrative capabilities for these screens, unauthenticated attackers can extract sensitive database information by sending crafted requests to the plugin's administration endpoints.

Vulnerable Code

// File: admin/class-list-table-archive.php
// Lines 530-532
if( isset( $_REQUEST['origin_post_type'] ) && ! empty( $_REQUEST['origin_post_type'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
    $args['origin_post_type'] = sanitize_text_field( $_REQUEST['origin_post_type'] ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
}

// Lines 535-545
$base_query = $this->do_query( $args );

// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
$results = $wpdb->get_results(
    $wpdb->prepare(
        "{$base_query} LIMIT %d,%d", // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
        $offset,
        $per_page
    )
);

// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
$total_items = $wpdb->get_var(
    "SELECT COUNT(*) as total_items FROM ($base_query) as total_items_subquery" // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
);

Security Fix

diff -ru /home/deploy/wp-safety.org/data/plugin-versions/revisionary/3.7.23/admin/admin_rvy.php /home/deploy/wp-safety.org/data/plugin-versions/revisionary/3.7.24/admin/admin_rvy.php
--- /home/deploy/wp-safety.org/data/plugin-versions/revisionary/3.7.23/admin/admin_rvy.php	2026-01-08 21:35:36.000000000 +0000
+++ /home/deploy/wp-safety.org/data/plugin-versions/revisionary/3.7.24/admin/admin_rvy.php	2026-02-18 20:55:34.000000000 +0000
@@ -228,10 +228,11 @@
 
 	 function fltAdminBodyClass($classes) {
 
+		// phpcs:ignore WordPress.Security.NonceVerification.Recommended, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
 		if (!empty($_REQUEST['page']) && in_array($_REQUEST['page'], ['revisionary-settings', 'rvy-net_options', 'rvy-default_options', 'revisionary-q', 'revisionary-deletion', 'revisionary-archive'])) {
 			$classes .= ' revisionary';
 			
-			switch ($_REQUEST['page']) {
+			switch ($_REQUEST['page']) {	// phpcs:ignore WordPress.Security.NonceVerification.Recommended
 				case 'revisionary-archive':
 					$classes .= ' revisionary-archive';
 					break;
diff -ru /home/deploy/wp-safety.org/data/plugin-versions/revisionary/3.7.23/admin/class-list-table-archive.php /home/deploy/wp-safety.org/data/plugin-versions/revisionary/3.7.24/admin/class-list-table-archive.php
--- /home/deploy/wp-safety.org/data/plugin-versions/revisionary/3.7.23/admin/class-list-table-archive.php	2026-01-08 21:35:36.000000000 +0000
+++ /home/deploy/wp-safety.org/data/plugin-versions/revisionary/3.7.24/admin/class-list-table-archive.php	2026-02-18 20:55:34.000000000 +0000
@@ -674,12 +674,12 @@
 
 					printf(
 						esc_html__('Edit of %s', 'revisionary'),
-						"<span title='$this->active_revision_title'>" . $status_label . '</span>'
+						"<span title='" . esc_attr($this->active_revision_title) . "'>" . esc_html($status_label) . '</span>'
 					);
 
 				} elseif ($this->parent_from_revision_workflow) {
 					printf("<span title='%s'>%s</span>",
-						$this->from_revision_title,
+						esc_html($this->from_revision_title),
 						esc_html__('Edit of published Revision', 'revisionary')
 					);
 				} elseif ($this->direct_edit) {

Exploit Outline

1. **Identify Vulnerable Endpoint**: Target the Past Revisions screen via `/wp-admin/admin.php?page=revisionary-archive`. 2. **Verify Accessibility**: Confirm that the page is accessible without authentication (fixed in 3.7.24 by enforcing the `view_revision_archive` capability). 3. **Inject Payload**: Send a GET or POST request to the endpoint with a SQL injection payload in the `origin_post_type` parameter (e.g., `origin_post_type=post' AND (SELECT 1 FROM (SELECT(SLEEP(5)))a)-- -`). 4. **Observe Response**: Use time-based blind injection or boolean-based techniques to infer database contents from the response latency or page output. 5. **Data Extraction**: Automate the process to dump sensitive information like administrator password hashes or configuration details from the `wp_users` and `wp_options` tables.

Check if your site is affected.

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