CVE-2026-39639

RPS Include Content <= 1.2.2 - Missing Authorization

mediumMissing Authorization
4.3
CVSS Score
4.3
CVSS Score
medium
Severity
Unpatched
Patched in
N/A
Time to patch

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: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.2.2
PublishedFebruary 14, 2026
Last updatedMay 7, 2026
Affected pluginrps-include-content
Research Plan
Unverified

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…

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)

  1. Registration: In the main plugin file (likely rps-include-content.php or includes/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' ) );
    
  2. Handler Entry: The function ajax_save_settings is called when the admin-ajax.php endpoint is hit with the corresponding action.
  3. Missing Check: The handler likely calls check_ajax_referer() to verify the nonce but omits a call to current_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();
    }
    
  4. 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.

  1. Identify Shortcode: Check the plugin source for add_shortcode. (e.g., [rps-include]).
  2. Trigger Script Loading: Create a new post as a Contributor containing that shortcode.
  3. Locate Nonce:
    • Navigate to the newly created post's edit page using browser_navigate.
    • Inspect the page source or use browser_eval to find the localized script data.
    • Expected Variable: Look for window.rps_include_content_vars or window.rps_include_content_data.
    • Command: browser_eval("window.rps_include_content_vars?.nonce") (Verify the exact variable name in the source).

5. Exploitation Strategy

  1. Preparation: Log in as a Contributor user.
  2. 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.
  3. Attack Execution:
    • Use http_request to send a POST request to admin-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-urlencoded and the Contributor's session cookies.
  4. 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 contributor role.
  • 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 OK status 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

  1. Verify via WP-CLI:
    wp option get rps_include_content_settings
    
  2. Confirm the values returned match the malicious_value sent in the AJAX payload.
  3. Observe if the unauthorized change impacts the site frontend or other administrative views.

9. Alternative Approaches

  • If admin-ajax is restricted: Check if the plugin processes settings via admin_init. If it does, a simple POST to wp-admin/admin-post.php or even wp-admin/index.php as 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_settings fails, search the source for other wp_ajax_ registrations like rps_include_content_update, reset_settings, or dismiss_notice.
Research Findings
Static analysis — not yet PoC-verified

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

--- a/includes/class-rps-include-content-admin.php
+++ b/includes/class-rps-include-content-admin.php
@@ -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.