Permalink Manager Lite < 2.5.3 - Missing Authorization
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:NTechnical Details
<2.5.3What Changed in the Fix
Changes introduced in v2.5.3
Source Code
WordPress.org SVN# 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
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
@@ -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.