CVE-2026-0702 WordPress VidShop Plugin <= 1.1.4 is vulnerable to a high priority SQL Injection
CVE-2026-0702 WordPress VidShop Plugin <= 1.1.4 is vulnerable to a high priority SQL Injection

VidShop Plugin Vulnerable to SQL Injection
Overview
- Published: 2026-01-28
- CVE-ID: CVE-2026-0702
- CVSS: 7.5 High
- Affected Plugin: VidShop Plugin
- Affected Versions: <= 1.1.4
- Vulnerability Type: High priority SQL Injection
- CWE: CWE-89 Improper Neutralization of Special Elements used in an SQL Command
Description
The VidShop – Shoppable Videos for WooCommerce plugin for WordPress is vulnerable to time-based SQL Injection via the ‘fields’ parameter in all versions up to, and including, 1.1.4 due to insufficient escaping on the user supplied parameter and lack of sufficient preparation on the existing SQL query. This makes it possible for unauthenticated attackers to append additional SQL queries into already existing queries that can be used to extract sensitive information from the database.
Patch And Commit Analysis

Based on the development change log, version 1.1.4 was identified as the vulnerable version, and version 1.1.5 was selected to perform a patch differential analysis. The changelog explicitly mentions “Added column name validation in Query Builder” alongside the API improvements.
Vulnerability Analysis (Version 1.1.4)
Analysis of the code diffs reveals a critical chain of vulnerabilities stemming from a logic flaw in the Query Builder and a lack of validation in the Controller.
Root Cause: Insecure Query Builder Logic
- In includes/Utils/Query_Builder.php (Version 1.1.4), the select() method accepted an array of columns without validation. Crucially, the internal logic allowed raw SQL injection if the column name contained specific keywords like AS or . to support aliases. This design flaw meant that if an attacker could pass a string containing AS to the select() method, the builder would not escape it, executing it as raw SQL.
1 | public function select( $columns = array( '*' ) ) { |
Entry Point: Lack of Input Sanitization
- In includes/REST_API/V1/Videos_Controller.php (Version 1.1.4), the API parameters fields and ids were defined without sanitize_callback. This allowed unvalidated user input to be passed directly to the vulnerable Query Builder.
1 | public function get_items( $request ) { |
Patch Analysis (Version 1.1.5)
In version 1.1.5, the vendor applied a Defense in Depth strategy, fixing the vulnerability at both the Entry Point (Controller) and the Root Cause (Query Builder).
Layer 1: Hardening the Root Cause (Query Builder)

As seen in the Query_Builder.php diff, the developer implemented strict validation logic to prevent bypasses:
is_valid_column_name(): A new protected method was added using Regular Expressions (preg_match) to enforce strict naming conventions:- Standard columns must match
/^[a-zA-Z_][a-zA-Z0-9_]*$/(Alphanumeric and underscores only). - Aliases using AS are now strictly parsed to ensure both the column and the alias are safe, preventing the injection of special characters like
parentheses () or comments --. - sanitize_columns(): This method filters the input array using the validator above.
- select() Update: The select method now explicitly calls $this->sanitize_columns() before processing the query, ensuring that even if bad data reaches this layer, it is neutralized.
Layer 2: Securing the Entry Point (Controller)



In Videos_Controller.php, the developer implemented input validation to reject malicious requests early:
- Whitelist Implementation: Added
get_allowed_fields()to define a hardcoded list of permissible columns (e.g., id, title). - Sanitization Callbacks: Added
sanitize_fields_paramandsanitize_ids_paramto the API route definition. This ensures that the fields parameter is stripped of any values not in the whitelist, and ids are forced to integers using absint. - Prepared Statements: In the
get_itemsmethod, raw SQL concatenation in theORDER BYclause was replaced with$wpdb->prepare, using %d placeholders for IDs.
Data Flow Analysis: Sink-to-Source Trace
The Sink (Vulnerability enforcement point)
- Location: includes/Utils/Query_Builder.php
- Method: to_sql()
This is where the final SQL statement is built and prepared to be sent to the Database. The vulnerability lies in the column name checking logic.
1 | if ( strpos( $column, '.' ) !== false || stripos( $column, ' as ' ) !== false ) { |
Explanation: If the $column variable contains the keyword AS or a dot, it will bypass the backticks and be appended directly to the SELECT statement.
Propagation (Data propagation)
- Location: includes/Utils/Query_Builder.php
- Method: select()
Malicious data from the Controller is transferred to the Query Builder here.
1 | public function select( $columns = array( '*' ) ) { |
The Source (Input data source)
- Location: includes/REST_API/V1/Videos_Controller.php
- Method: get_items()
This is the entry point (Entry Point), where hackers inject payload into the system via API Request.
1 | // Receive raw input from request (Tainted Data) |

Proof Of Concept POC
The vulnerability is a Time-Based Blind SQL Injection affecting the fields parameter. To confirm the flaw, we compare the response time of a legitimate request against a request containing a SQL SLEEP() command.
Prerequisite: The VidShop plugin must have at least one video created in the dashboard. If the database table is empty, the SQL query will return an empty set immediately, and the SLEEP() function will not execute.


First, we send a standard GET request to the API endpoint asking for valid columns (id and title).
- Request: GET /wp-json/vsfw/v1/videos?fields=id,title
- Observation: The server processes the request normally.
- Response Time: Approximately 1.1 seconds.

Next, we inject a** time-based payload**. We append (SELECT SLEEP(5)) AS injection to the fields parameter. The AS keyword is critical here, as it triggers the specific logic flaw in the Query_Builder that bypasses backtick escaping.
- Payload: (SELECT SLEEP(5)) AS injection
- Request: GET /wp-json/vsfw/v1/videos?fields=id,title,(SELECT+SLEEP(5))+AS+injection
- Observation: The server executes the injected SQL command, causing the database to sleep for 5 seconds before returning the response.
- Response Time: Approximately 6.1 seconds (1.1s baseline + 5s sleep).
The significant delay of exactly 5 seconds in the second request confirms that the arbitrary SQL command was successfully executed by the database. This proves the existence of an unauthenticated SQL Injection vulnerability in the fields parameter.