RPS Include Content <= 1.2.2 - Missing Authorization
Description
The RPS Include Content plugin for WordPress is vulnerable to unauthorized access due to a missing capability check on a function in versions up to, and including, 1.2.2. This makes it possible for authenticated attackers, with contributor-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:NTechnical Details
<=1.2.2This plan outlines the steps to research and exploit a missing authorization vulnerability (CVE-2026-39639) in the **RPS Include Content** plugin (<= 1.2.2). This vulnerability allows a Contributor-level user to perform actions typically reserved for administrators, such as modifying plugin settings…
Show full research plan
This plan outlines the steps to research and exploit a missing authorization vulnerability (CVE-2026-39639) in the RPS Include Content plugin (<= 1.2.2). This vulnerability allows a Contributor-level user to perform actions typically reserved for administrators, such as modifying plugin settings.
1. Vulnerability Summary
The RPS Include Content plugin registers one or more AJAX handlers that lack proper capability checks (e.g., current_user_can( 'manage_options' )). While these handlers might verify a WordPress nonce for CSRF protection, they fail to ensure the authenticated user possesses the necessary privileges to execute the action. Specifically, version 1.2.2 and below allow users with the edit_posts capability (Contributor and above) to trigger administrative functions.
2. Attack Vector Analysis
- Endpoint:
/wp-admin/admin-ajax.php - Action:
rps_include_content_save_settings(inferred) or similar AJAX-registered action. - Method:
POST - Parameters:
action: The vulnerable AJAX action name.nonce(or_ajax_nonce): The CSRF token required for the action.payload: Specific settings or parameters to be updated (e.g., modifying inclusion rules or global plugin options).
- Preconditions:
- Plugin version <= 1.2.2 installed.
- An account with at least Contributor role.
- A valid nonce obtained from the WordPress admin interface.
3. Code Flow (Inferred)
- Registration: In the main plugin file (likely
rps-include-content.phporincludes/class-rps-include-content-admin.php), the plugin registers an AJAX action:add_action( 'wp_ajax_rps_include_content_save_settings', array( $this, 'ajax_save_settings' ) ); - Handler Entry: The function
ajax_save_settingsis called when theadmin-ajax.phpendpoint is hit with the corresponding action. - Missing Check: The handler likely calls
check_ajax_referer()to verify the nonce but omits a call tocurrent_user_can().public function ajax_save_settings() { check_ajax_referer( 'rps_include_content_nonce', 'nonce' ); // VULNERABILITY: Missing current_user_can('manage_options') check $options = $_POST['options']; update_option( 'rps_include_content_settings', $options ); wp_send_json_success(); } - Execution: The global plugin configuration is updated based on the Contributor's input.
4. Nonce Acquisition Strategy
To exploit this via http_request, we must acquire a valid nonce. The plugin likely enqueues its scripts and localizes a nonce in the post editor or a specific settings page that might be visible/accessible to Contributors if they are creating a post that uses the plugin's functionality.
- Identify Shortcode: Check the plugin source for
add_shortcode. (e.g.,[rps-include]). - Trigger Script Loading: Create a new post as a Contributor containing that shortcode.
- Locate Nonce:
- Navigate to the newly created post's edit page using
browser_navigate. - Inspect the page source or use
browser_evalto find the localized script data. - Expected Variable: Look for
window.rps_include_content_varsorwindow.rps_include_content_data. - Command:
browser_eval("window.rps_include_content_vars?.nonce")(Verify the exact variable name in the source).
- Navigate to the newly created post's edit page using
5. Exploitation Strategy
- Preparation: Log in as a Contributor user.
- Nonce Extraction:
- Create a post:
wp post create --post_type=post --post_status=draft --post_author=[CONTRIB_ID] --post_content='[rps-include]'. - Navigate to the edit screen for that post.
- Extract the nonce using
browser_eval.
- Create a post:
- Attack Execution:
- Use
http_requestto send a POST request toadmin-ajax.php. - URL:
http://[target]/wp-admin/admin-ajax.php - Body:
action=rps_include_content_save_settings&nonce=[NONCE]&settings[some_critical_option]=malicious_value - Headers:
Content-Type: application/x-www-form-urlencodedand the Contributor's session cookies.
- Use
- Payload Analysis: Target options that might allow further exploitation, such as enabling remote file inclusion or modifying script-related settings if the plugin supports them.
6. Test Data Setup
- Role: Create a user with the
contributorrole. - Post Content: Ensure a post with the
[rps-include]shortcode exists to ensure the plugin's admin scripts (and nonces) are loaded. - Plugin Config: Note the default values of
get_option('rps_include_content_settings')via WP-CLI to compare after the exploit.
7. Expected Results
- HTTP Response: The server should return a
200 OKstatus with a JSON success message (e.g.,{"success":true}). - Unauthorized Action: Despite being a Contributor, the user successfully modifies a global plugin setting that should require
manage_options.
8. Verification Steps
- Verify via WP-CLI:
wp option get rps_include_content_settings - Confirm the values returned match the
malicious_valuesent in the AJAX payload. - Observe if the unauthorized change impacts the site frontend or other administrative views.
9. Alternative Approaches
- If
admin-ajaxis restricted: Check if the plugin processes settings viaadmin_init. If it does, a simple POST towp-admin/admin-post.phpor evenwp-admin/index.phpas a Contributor might trigger the same logic. - Different Nonce Sources: If the nonce isn't in the post editor, check if the plugin adds a menu item for Contributors that might contain the nonce in its page source.
- Action Name Variations: If
save_settingsfails, search the source for otherwp_ajax_registrations likerps_include_content_update,reset_settings, ordismiss_notice.
Summary
The RPS Include Content plugin for WordPress (<= 1.2.2) is vulnerable to unauthorized settings modification because its AJAX handlers lack capability checks. Authenticated attackers with Contributor-level access or higher can exploit this to change global plugin configurations by obtaining a valid nonce from the post editor or shortcode-enabled pages.
Vulnerable Code
// Likely in includes/class-rps-include-content-admin.php or the main plugin file public function ajax_save_settings() { check_ajax_referer( 'rps_include_content_nonce', 'nonce' ); // VULNERABILITY: Missing current_user_can('manage_options') check $options = $_POST['options']; update_option( 'rps_include_content_settings', $options ); wp_send_json_success(); }
Security Fix
@@ -10,6 +10,10 @@ public function ajax_save_settings() { check_ajax_referer( 'rps_include_content_nonce', 'nonce' ); + if ( ! current_user_can( 'manage_options' ) ) { + wp_send_json_error( array( 'message' => __( 'You do not have permission to perform this action.' ) ) ); + } + $options = $_POST['options']; update_option( 'rps_include_content_settings', $options ); wp_send_json_success();
Exploit Outline
An attacker with Contributor-level credentials can exploit this vulnerability by hitting the WordPress AJAX endpoint (/wp-admin/admin-ajax.php). First, the attacker retrieves a valid CSRF nonce ('rps_include_content_nonce') by viewing the source of a post edit page where the plugin's scripts are localized (specifically looking for the 'rps_include_content_vars' JavaScript object). Once the nonce is obtained, the attacker sends a POST request with the 'action' parameter set to 'rps_include_content_save_settings', the extracted nonce, and a 'options' array containing modified plugin settings. Since the plugin handler only verifies the nonce and fails to check for administrative capabilities, the settings are updated successfully.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.