CVE-2026-32413

Permalink Manager Lite < 2.5.3 - Missing Authorization

mediumMissing Authorization
5.3
CVSS Score
5.3
CVSS Score
medium
Severity
2.5.3
Patched in
50d
Time to patch

Description

The Permalink Manager Lite plugin for WordPress is vulnerable to unauthorized access due to a missing capability check on a function in versions up to 2.5.3. This makes it possible for unauthenticated attackers to perform an unauthorized action.

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<2.5.3
PublishedFebruary 25, 2026
Last updatedApril 15, 2026
Affected pluginpermalink-manager

What Changed in the Fix

Changes introduced in v2.5.3

Loading patch diff...

Source Code

WordPress.org SVN
Research Plan
Unverified

# CVE-2026-32413: Permalink Manager Lite < 2.5.3 - Missing Authorization Research Plan The **Permalink Manager Lite** plugin is vulnerable to unauthorized settings modification and data manipulation due to a missing capability check in its core action processing function. While the plugin implement…

Show full research plan

CVE-2026-32413: Permalink Manager Lite < 2.5.3 - Missing Authorization Research Plan

The Permalink Manager Lite plugin is vulnerable to unauthorized settings modification and data manipulation due to a missing capability check in its core action processing function. While the plugin implements nonce verification, it fails to verify that the user performing the action has the manage_options capability. If an attacker can obtain a valid nonce (which is often leaked in the plugin's UI or frontend components), they can modify plugin settings, regenerate permastructures, or update the entire permalink database.

1. Vulnerability Summary

  • Vulnerable Function: Permalink_Manager_Actions::trigger_action()
  • File: includes/core/permalink-manager-actions.php
  • Hook: admin_init (Priority 9)
  • Problem: The function processes critical administrative actions (saving settings, updating permalinks, importing data) by verifying only a WordPress nonce (permalink-manager) without checking if the user has administrative privileges (current_user_can( 'manage_options' )).
  • Impact: Unauthenticated attackers (via admin-ajax.php) or low-privileged users who can obtain the nonce can modify the site's permalink structure, which can lead to widespread 404 errors, SEO damage, or redirection of traffic.

2. Attack Vector Analysis

  • Endpoint: /wp-admin/admin-ajax.php (triggers `admin
Research Findings
Static analysis — not yet PoC-verified

Summary

The Permalink Manager Lite plugin fails to perform an authorization check (such as current_user_can) in its trigger_action function, which is hooked to admin_init. This allows unauthenticated or low-privileged users who can obtain a valid nonce to perform sensitive administrative actions, including modifying plugin settings, changing permastructures, or updating the entire permalink database.

Vulnerable Code

// includes/core/permalink-manager-actions.php:40
public function trigger_action() {
    global $permalink_manager_after_sections_html;

    // 1. Check if the form was submitted
    if ( empty( $_POST ) ) {
        return;
    }

    // 2. Do nothing if search query is not empty
    if ( isset( $_REQUEST['search-submit'] ) || isset( $_REQUEST['filter-button'] ) ) {
        if ( wp_verify_nonce( $_POST[ 'uri_editor' ], 'permalink-manager' ) ) {
            $this->trigger_filter_action();
        }

        return;
    }

    $actions_map = array(
        'uri_editor'                     => array( 'function' => 'update_all_permalinks', 'display_uri_table' => true ),
        'permalink_manager_options'      => array( 'function' => 'save_settings' ),
        'permalink_manager_permastructs' => array( 'function' => 'save_permastructures' ),
        'import'                         => array( 'function' => 'import_custom_permalinks_uris' ),
    );

    // 3. Find the action
    foreach ( $actions_map as $action => $map ) {
        if ( isset( $_POST[ $action ] ) && wp_verify_nonce( $_POST[ $action ], 'permalink-manager' ) ) {
            // Execute the function
            $output = call_user_func( array( $this, $map['function'] ) );

            // ...
        }
    }
}

Security Fix

diff -ru /home/deploy/wp-safety.org/data/plugin-versions/permalink-manager/2.5.2.4/includes/core/permalink-manager-actions.php /home/deploy/wp-safety.org/data/plugin-versions/permalink-manager/2.5.3/includes/core/permalink-manager-actions.php
--- /home/deploy/wp-safety.org/data/plugin-versions/permalink-manager/2.5.2.4/includes/core/permalink-manager-actions.php	2026-01-09 16:45:20.000000000 +0000
+++ /home/deploy/wp-safety.org/data/plugin-versions/permalink-manager/2.5.3/includes/core/permalink-manager-actions.php	2026-02-10 14:49:46.000000000 +0000
@@ -30,16 +30,12 @@
 		}
 
 		// 2. Do nothing if search query is not empty
-		if ( isset( $_REQUEST['search-submit'] ) || isset( $_REQUEST['filter-button'] ) ) {
-			if ( wp_verify_nonce( $_POST[ 'uri_editor' ], 'permalink-manager' ) ) {
-				$this->trigger_filter_action();
-			}
-
-			return;
+		if ( isset( $_POST['uri_editor_nonce'] ) && ( isset( $_REQUEST['search-submit'] ) || isset( $_REQUEST['filter-button'] ) ) ) {
+			$this->trigger_filter_action();
 		}
 
 		$actions_map = array(
-			'uri_editor'                     => array( 'function' => 'update_all_permalinks', 'display_uri_table' => true ),
+			'uri_editor_nonce'               => array( 'function' => 'update_all_permalinks', 'display_uri_table' => true ),
 			'permalink_manager_options'      => array( 'function' => 'save_settings' ),
 			'permalink_manager_permastructs' => array( 'function' => 'save_permastructures' ),
 			'import'                         => array( 'function' => 'import_custom_permalinks_uris' ),
@@ -47,7 +43,9 @@
 
 		// 3. Find the action
 		foreach ( $actions_map as $action => $map ) {
-			if ( isset( $_POST[ $action ] ) && wp_verify_nonce( $_POST[ $action ], 'permalink-manager' ) ) {
+			$nonce = ( isset( $_POST[ $action ] ) ) ? sanitize_key( $_POST[ $action ] ) : '';
+
+			if ( ! empty( $nonce ) && wp_verify_nonce( $nonce, 'permalink-manager' ) ) {
 				// Execute the function
 				$output = call_user_func( array( $this, $map['function'] ) );
 
@@ -74,17 +72,21 @@
 	 * @return void
 	 */
 	public function trigger_filter_action() {
+		if ( empty( $_POST['uri_editor_nonce'] ) || ! wp_verify_nonce( sanitize_key( $_POST['uri_editor_nonce'] ), 'permalink-manager' ) ) {
+			return;
+		}
+
 		$query_args = array(
-			'langcode' => ! empty( $_REQUEST['langcode'] ) ? $_REQUEST['langcode'] : null,
-			'month'    => ! empty( $_REQUEST['month'] ) ? $_REQUEST['month'] : null,
-			's'        => ! empty( $_REQUEST['s'] ) ? $_REQUEST['s'] : null,
+			'langcode' => ! empty( $_REQUEST['langcode'] ) ? sanitize_key( $_REQUEST['langcode'] ) : null,
+			'month'    => ! empty( $_REQUEST['month'] ) ? sanitize_key( $_REQUEST['month'] ) : null,
+			's'        => ! empty( $_REQUEST['s'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['s'] ) ) : null,
 		);
 
 		if ( ! empty( $query_args ) ) {
 			$sendback = remove_query_arg( array_keys( $query_args ), wp_get_referer() );
 			$sendback = add_query_arg( array_filter( $query_args ), $sendback );
 
-			wp_redirect( $sendback );
+			wp_safe_redirect( $sendback );
 			exit;
 		}
 	}

Exploit Outline

The exploit targets the Permalink_Manager_Actions::trigger_action function, which is executed via the admin_init hook. An attacker needs to: 1. Obtain a valid WordPress nonce for the 'permalink-manager' action. This nonce is often available to low-privileged users who have access to the dashboard or can be leaked via certain frontend contexts. 2. Construct a POST request to any administrative URL (e.g., /wp-admin/admin-ajax.php) which triggers the admin_init hook. 3. Include an action parameter from the $actions_map in the POST body (e.g., permalink_manager_options) with the valid nonce as its value. 4. Provide the corresponding payload for the function being called (e.g., providing a list of plugin settings to modify in the POST body for the save_settings function). 5. Because the plugin only verifies the nonce and does not verify the user's capabilities, it will execute the sensitive function with administrative privileges.

Check if your site is affected.

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