CVE-2026-32541

Premmerce Redirect Manager <= 1.0.12 - Missing Authorization

mediumMissing Authorization
4.3
CVSS Score
4.3
CVSS Score
medium
Severity
1.0.13
Patched in
8d
Time to patch

Description

The Premmerce Redirect Manager plugin for WordPress is vulnerable to unauthorized access due to a missing capability check on a function in all versions up to, and including, 1.0.12. This makes it possible for authenticated attackers, with Subscriber-level access and above, to perform an unauthorized action.

CVSS Vector Breakdown

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

Technical Details

Affected versions<=1.0.12
PublishedMarch 20, 2026
Last updatedMarch 27, 2026

What Changed in the Fix

Changes introduced in v1.0.13

Loading patch diff...

Source Code

WordPress.org SVN
Research Plan
Unverified

# Vulnerability Research Plan: CVE-2026-32541 Premmerce Redirect Manager <= 1.0.12 ## 1. Vulnerability Summary The **Premmerce Redirect Manager** plugin (up to version 1.0.12) contains a missing authorization vulnerability. While the plugin restricts access to its main administrative interface to u…

Show full research plan

Vulnerability Research Plan: CVE-2026-32541 Premmerce Redirect Manager <= 1.0.12

1. Vulnerability Summary

The Premmerce Redirect Manager plugin (up to version 1.0.12) contains a missing authorization vulnerability. While the plugin restricts access to its main administrative interface to users with the manage_options capability, it registers several admin_post and wp_ajax hooks that fail to perform secondary capability checks within their handler functions. This allows authenticated users with low-level privileges (like Subscribers) to perform actions intended for administrators, specifically deleting redirects.

2. Attack Vector Analysis

  • Endpoint: /wp-admin/admin-post.php (for deletions) or /wp-admin/admin-ajax.php (for information leakage).
  • Primary Action: premmerce_delete_redirect (registered via admin_post hook).
  • Secondary Action: get_posts_by_string (registered via wp_ajax hook).
  • Parameters:
    • action: premmerce_delete_redirect
    • id: The integer ID of the redirect to be deleted.
    • _wpnonce: (Potentially required) CSRF token.
  • Authentication Level: Authenticated (Subscriber and above).
  • Preconditions: At least one redirect must exist in the wp_premmerce_redirects table.

3

Research Findings
Static analysis — not yet PoC-verified

Summary

The Premmerce Redirect Manager plugin for WordPress is vulnerable to unauthorized access and information disclosure due to missing capability checks and nonce verification on several AJAX and admin-post endpoints. Authenticated attackers, including those with Subscriber-level permissions, can leak the titles of private or password-protected posts and terms, or potentially delete configured redirects.

Vulnerable Code

// src/Admin/Admin.php @ line 125
public function getPostsByString()
{
    if (isset($_POST['type'])) {
        $objects = $this->model->getPostsByString($_POST);

        wp_send_json($objects);
    }
}

---

// src/RedirectModel.php @ line 166
public function getPostsByString($data)
{
    if (in_array($data['type'], array('product', 'post', 'page'))) {
        $objects = (new \WP_Query(array(
            's'           => isset($data['s'])? $data['s'] : '',
            'post_type'   => $data['type'],
            'numberposts' => 10,
        )))->posts;
    } else {
        $objects = get_terms(array(
            'hide_empty' => false,
            'search'     => isset($data['s'])? $data['s'] : '',
            'taxonomy'   => $data['type'],
        ));
    }

    return $objects;
}

---

// src/Admin/Admin.php @ line 116
add_action('admin_post_premmerce_delete_redirect', array($this, 'deleteRedirect'));
add_action('wp_ajax_get_posts_by_string', array($this, 'getPostsByString'));

Security Fix

diff -ru /home/deploy/wp-safety.org/data/plugin-versions/premmerce-redirect-manager/1.0.12/assets/admin/js/premmerce-redirect.js /home/deploy/wp-safety.org/data/plugin-versions/premmerce-redirect-manager/1.0.13/assets/admin/js/premmerce-redirect.js
--- /home/deploy/wp-safety.org/data/plugin-versions/premmerce-redirect-manager/1.0.12/assets/admin/js/premmerce-redirect.js	2018-01-11 08:14:08.000000000 +0000
+++ /home/deploy/wp-safety.org/data/plugin-versions/premmerce-redirect-manager/1.0.13/assets/admin/js/premmerce-redirect.js	2026-02-18 21:55:06.000000000 +0000
@@ -13,6 +13,7 @@
                         s: params.term,
                         type: 'product',
                         action: 'get_posts_by_string',
+                        _wpnonce: premmerceRedirect.nonce,
                     }
                 },
                 processResults: function(data) {
@@ -39,6 +40,7 @@
                         s: params.term,
                         type: 'product_cat',
                         action: 'get_posts_by_string',
+                        _wpnonce: premmerceRedirect.nonce,
                     }
                 },
                 processResults: function(data) {
@@ -65,6 +67,7 @@
                         s: params.term,
                         type: 'category',
                         action: 'get_posts_by_string',
+                        _wpnonce: premmerceRedirect.nonce,
                     }
                 },
                 processResults: function(data) {
@@ -91,6 +94,7 @@
                         s: params.term,
                         type: 'post',
                         action: 'get_posts_by_string',
+                        _wpnonce: premmerceRedirect.nonce,
                     }
                 },
                 processResults: function(data) {
@@ -117,6 +121,7 @@
                         s: params.term,
                         type: 'page',
                         action: 'get_posts_by_string',
+                        _wpnonce: premmerceRedirect.nonce,
                     }
                 },
                 processResults: function(data) {
diff -ru /home/deploy/wp-safety.org/data/plugin-versions/premmerce-redirect-manager/1.0.12/src/Admin/Admin.php /home/deploy/wp-safety.org/data/plugin-versions/premmerce-redirect-manager/1.0.13/src/Admin/Admin.php
--- /home/deploy/wp-safety.org/data/plugin-versions/premmerce-redirect-manager/1.0.12/src/Admin/Admin.php	2023-08-23 11:19:54.000000000 +0000
+++ /home/deploy/wp-safety.org/data/plugin-versions/premmerce-redirect-manager/1.0.13/src/Admin/Admin.php	2026-02-18 21:55:06.000000000 +0000
@@ -248,8 +248,20 @@
      */
     public function getPostsByString()
     {
+        if (! current_user_can('manage_options')) {
+            wp_send_json_error(array( 'message' => __('You do not have permission to perform this action.', 'premmerce-redirect') ), 403);
+        }
+
+        if (! isset($_POST['_wpnonce']) || ! wp_verify_nonce(wp_unslash($_POST['_wpnonce']), 'premmerce_redirect_search')) {
+            wp_send_json_error(array( 'message' => __('Security check failed.', 'premmerce-redirect') ), 403);
+        }
+
         if (isset($_POST['type'])) {
-            $objects = $this->model->getPostsByString($_POST);
+            $data = array(
+                'type' => sanitize_text_field($_POST['type']),
+                's'    => isset($_POST['s']) ? sanitize_text_field($_POST['s']) : '',
+            );
+            $objects = $this->model->getPostsByString($data);
 
             wp_send_json($objects);
         }
@@ -327,6 +339,9 @@
     {
         wp_enqueue_script('select2', $this->fileManager->locateAsset('admin/js/select2.min.js'));
         wp_enqueue_script('premmerce-redirect', $this->fileManager->locateAsset('admin/js/premmerce-redirect.js'));
+        wp_localize_script('premmerce-redirect', 'premmerceRedirect', array(
+            'nonce' => wp_create_nonce('premmerce_redirect_search'),
+        ));
         wp_enqueue_style('select2', $this->fileManager->locateAsset('admin/css/select2.min.css'));
         wp_enqueue_style('premmerce-redirect', $this->fileManager->locateAsset('admin/css/premmerce-redirect.css'));
diff -ru /home/deploy/wp-safety.org/data/plugin-versions/premmerce-redirect-manager/1.0.12/src/RedirectModel.php /home/deploy/wp-safety.org/data/plugin-versions/premmerce-redirect-manager/1.0.13/src/RedirectModel.php
--- /home/deploy/wp-safety.org/data/plugin-versions/premmerce-redirect-manager/1.0.12/src/RedirectModel.php	2018-08-21 13:20:46.000000000 +0000
+++ /home/deploy/wp-safety.org/data/plugin-versions/premmerce-redirect-manager/1.0.13/src/RedirectModel.php	2026-02-18 21:55:06.000000000 +0000
@@ -166,18 +166,25 @@
      */
     public function getPostsByString($data)
     {
-        if (in_array($data['type'], array('product', 'post', 'page'))) {
+        $allowed_post_types = array( 'product', 'post', 'page' );
+        $allowed_taxonomies = array( 'product_cat', 'category' );
+
+        if (in_array($data['type'], $allowed_post_types, true)) {
             $objects = (new \WP_Query(array(
-                's'           => isset($data['s'])? $data['s'] : '',
-                'post_type'   => $data['type'],
-                'numberposts' => 10,
+                's'              => isset($data['s']) ? $data['s'] : '',
+                'post_type'      => $data['type'],
+                'posts_per_page' => 10,
+                'has_password'   => false,
+                'post_status'    => 'publish',
             )))->posts;
-        } else {
+        } elseif (in_array($data['type'], $allowed_taxonomies, true)) {
             $objects = get_terms(array(
                 'hide_empty' => false,
-                'search'     => isset($data['s'])? $data['s'] : '',
+                'search'     => isset($data['s']) ? $data['s'] : '',
                 'taxonomy'   => $data['type'],
             ));
+        } else {
+            $objects = array();
         }
 
         return $objects;

Exploit Outline

The exploit targets the AJAX endpoint `get_posts_by_string` which is available to all authenticated users. 1. Authentication: Log in as a low-privileged user (Subscriber). 2. Request: Send a POST request to `/wp-admin/admin-ajax.php`. 3. Parameters: - Set `action` to `get_posts_by_string`. - Set `type` to a sensitive post type like `post`, `page`, or `product`. - Set `s` to a search term (empty or specific string). 4. Outcome: The server returns a JSON array of objects containing the IDs and titles of the matching posts. Because the vulnerable version lacks query constraints, this includes titles of posts with statuses like 'draft', 'pending', or 'private', as well as password-protected posts.

Check if your site is affected.

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