[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"$fzMMWUKTMTAdIKs59CEH2FcuXHihPjqWmp5xeOa1k89E":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-12165","contest-gallery-authenticated-author-privilege-escalation-via-registryuserrole-parameter","Contest Gallery \u003C= 30.0.2 - Authenticated (Author+) Privilege Escalation via 'RegistryUserRole' Parameter","The Contest Gallery – Upload & Vote Photos, Media, Sell with PayPal & Stripe plugin for WordPress is vulnerable to Privilege Escalation in all versions up to, and including, 30.0.2 via the `RegistryUserRole` parameter. This is due to the plugin's admin menu being registered at the `edit_posts` capability level — granting Contributor-level users access to the plugin's admin pages and a valid `cg_admin` nonce — while the option-saving handler in `change-options-and-sizes.php` performs no `current_user_can()` capability check beyond `check_admin_referer('cg_admin')`, and the `RegistryUserRole` value is processed only through `sanitize_text_field()` and `htmlentities()` without restriction to an allowlist of permitted role names. This makes it possible for authenticated attackers, with author-level access and above, to overwrite the plugin's stored `RegistryUserRole` option with `administrator`, which the `cg_create_wp_user_from_google_user` function then reads back from the `contest_gal1ery_registry_and_login_options` database table without any allowlist validation and passes directly to `wp_update_user()`, effectively promoting a newly registered Google sign-in account to Administrator.","contest-gallery",null,"\u003C=30.0.2","30.0.3","high",8.8,"CVSS:3.1\u002FAV:N\u002FAC:L\u002FPR:L\u002FUI:N\u002FS:U\u002FC:H\u002FI:H\u002FA:H","Improper Privilege Management","2026-06-16 20:57:12","2026-06-17 09:30:59",[19],"https:\u002F\u002Fwww.wordfence.com\u002Fthreat-intel\u002Fvulnerabilities\u002Fid\u002F69b909da-b1b0-4dab-916c-908511f6556f?source=api-prod",1,[22,23,24,25,26,27,28,29],"changelog.txt","functions\u002Fgeneral\u002Fcg-general-functions.php","functions\u002Fgeneral\u002Fcg-get-version.php","functions\u002Fgeneral\u002Fcg-user-functions.php","functions\u002Fgeneral\u002Fregistry\u002Fupdate\u002Fcg-registry-update-functions.php","functions\u002Fgoogle\u002Fcg-create-wp-user-from-google-user.php","index.php","readme.txt","researched",false,3,"This exploitation research plan targets **CVE-2026-12165**, an authenticated privilege escalation vulnerability in the **Contest Gallery** plugin (\u003C= 30.0.2).\n\n### 1. Vulnerability Summary\nThe vulnerability exists due to a combination of improper capability checks and insufficient input validation. The plugin registers its admin menu using the `edit_posts` capability, which is granted to users with the **Contributor** role and above. This allows these users to access the plugin's administration pages and retrieve a valid `cg_admin` nonce. \n\nThe option-saving logic (specifically handling the `RegistryUserRole` parameter) uses `check_admin_referer('cg_admin')` to prevent CSRF but fails to verify if the current user has the authority to change system-level roles (i.e., it lacks a `current_user_can('manage_options')` check). Furthermore, the `RegistryUserRole` is saved to the database without being validated against an allowlist of safe roles. An attacker can set this to `administrator`. When a new user registers via the Google Sign-In feature, the function `cg_create_wp_user_from_google_user` reads this value and promotes the new user to an Administrator.\n\n### 2. Attack Vector Analysis\n*   **Vulnerable Endpoint:** The settings update page\u002Fhandler (likely `admin.php?page=...` involving the logic in `change-options-and-sizes.php`).\n*   **Vulnerable Parameter:** `options[registry-login][RegistryUserRole]` (or similar POST structure processed by `cg_update_registry_and_login_options_v14`).\n*   **Authentication Level:** Authenticated, **Contributor** role or higher (requires `edit_posts` capability).\n*   **Preconditions:** The Google Sign-In feature must be available (enabled in version 30.0.2+).\n\n### 3. Code Flow\n1.  **Entry Point:** An authenticated Contributor user accesses the Contest Gallery settings in the WordPress dashboard.\n2.  **Nonce Retrieval:** The settings page enqueues scripts\u002Fforms that include the `cg_admin` nonce.\n3.  **Option Update:** The user sends a POST request to the options handler.\n4.  **Insecure Processing:** The handler (referencing `functions\u002Fgeneral\u002Fregistry\u002Fupdate\u002Fcg-registry-update-functions.php`) calls `cg_update_registry_and_login_options_v14`.\n    *   It validates the nonce via `check_admin_referer('cg_admin')`.\n    *   It does **not** check `current_user_can('manage_options')`.\n5.  **Persistence:** The `RegistryUserRole` value is saved into the table `{prefix}contest_gal1ery_registry_and_login_options`.\n6.  **Escalation Sink:** In `functions\u002Fgoogle\u002Fcg-create-wp-user-from-google-user.php`:\n    *   The function retrieves the role: `$RegistryUserRole = $wpdb->get_var( \"SELECT RegistryUserRole FROM $tablename_registry_and_login_options WHERE GeneralID = 1\");`\n    *   It creates a new user and updates their role: `wp_update_user( array( 'ID' => $WpUserId, 'role' => $RegistryUserRole ) );`\n\n### 4. Nonce Acquisition Strategy\nThe `cg_admin` nonce is required. Since the menu is registered with `edit_posts`, a Contributor can access the admin pages.\n1.  Log in as a Contributor.\n2.  Navigate to the Contest Gallery settings page (e.g., `wp-admin\u002Fadmin.php?page=contest-gallery-options`).\n3.  Extract the nonce from the HTML or localized JS variables.\n4.  **Action:** Use `browser_eval` to find the nonce if it's localized, or inspect the form for a hidden input named `_wpnonce`.\n\n### 5. Exploitation Strategy\nThe goal is to modify the `RegistryUserRole` to `administrator`.\n\n**Step 1: Update the Role Option**\n*   **Tool:** `http_request`\n*   **Method:** POST\n*   **URL:** `http:\u002F\u002Flocalhost:8080\u002Fwp-admin\u002Fadmin.php?page=contest-gallery-options` (Verify the exact page slug via `wp menu list`).\n*   **Headers:** `Content-Type: application\u002Fx-www-form-urlencoded`\n*   **Body Parameters:**\n    *   `_wpnonce`: `[Extracted_cg_admin_nonce]`\n    *   `_wp_http_referer`: `\u002Fwp-admin\u002Fadmin.php?page=contest-gallery-options`\n    *   `options[registry-login][RegistryUserRole]`: `administrator`\n    *   Other required form fields (e.g., `LogoutLink`, `BackToGalleryLink`) should be sent with their current values to avoid breaking the plugin.\n\n**Step 2: Trigger Privilege Escalation (Conceptual)**\n*   A new user registers via Google Sign-In. The plugin will now use the malicious `RegistryUserRole` value to assign the 'administrator' role.\n\n### 6. Test Data Setup\n1.  Install Contest Gallery version 30.0.2.\n2.  Create a user with the **Contributor** role:\n    `wp user create attacker attacker@example.com --role=contributor --user_pass=password`\n3.  Ensure the plugin is active and initialized.\n\n### 7. Expected Results\n*   The POST request returns a `302 Redirect` or a success message.\n*   The database value for `RegistryUserRole` in the `contest_gal1ery_registry_and_login_options` table is changed to `administrator`.\n\n### 8. Verification Steps\nAfter the HTTP request, use WP-CLI to verify the change in the custom table:\n```bash\nwp db query \"SELECT RegistryUserRole FROM $(wp db prefix)contest_gal1ery_registry_and_login_options WHERE GeneralID = 1\"\n```\nIf the output is `administrator`, the escalation vector is confirmed.\n\n### 9. Alternative Approaches\nIf the `cg_admin` nonce is not available on the main options page, check the \"Registry & Login\" sub-page specifically, as the vulnerability is tied to the `RegistryUserRole` parameter which resides there. If direct file access to `change-options-and-sizes.php` is possible (check for `ABSPATH` guards), that may be a secondary vector, though the admin menu registration is the primary intended path for this exploit.","The Contest Gallery plugin for WordPress is vulnerable to privilege escalation because its administration settings are accessible to users with the 'edit_posts' capability (Author level) and lack proper role validation. An attacker can overwrite the 'RegistryUserRole' setting with 'administrator', which causes the plugin to grant administrative privileges to any new user who subsequently registers through the Google Sign-In feature.","\u002F\u002F v10\u002Fv10-admin\u002Foptions\u002Fchange-options-and-sizes.php\n\ncheck_admin_referer('cg_admin');\n\n\u002F\u002F ... \n\n        if($dbVersion>=14){\u002F\u002F for new galleries after 14\n            $RegistryUserRole = '';\n            $RegistryUserRoleForRegistryAndLoginOptions = sanitize_text_field(htmlentities((isset($_POST['RegistryUserRole'])) ? $_POST['RegistryUserRole'] : ''));\n        }else{\u002F\u002F for older galleries before 14\n            $RegistryUserRole = sanitize_text_field(htmlentities((isset($_POST['RegistryUserRole'])) ? $_POST['RegistryUserRole'] : ''));\n            $RegistryUserRoleForRegistryAndLoginOptions = '';\n        }\n\n---\n\n\u002F\u002F functions\u002Fgoogle\u002Fcg-create-wp-user-from-google-user.php\n\n        $RegistryUserRole = $wpdb->get_var( \"SELECT RegistryUserRole FROM $tablename_registry_and_login_options WHERE GeneralID = 1\");\n\n\u002F\u002F ... \n\n            wp_update_user( array( 'ID' => $WpUserId, 'role' => $RegistryUserRole ) );","--- \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fcontest-gallery\u002F30.0.2\u002Fv10\u002Fv10-admin\u002Foptions\u002Fchange-options-and-sizes.php\t2026-06-11 19:34:18.000000000 +0000\n+++ \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fcontest-gallery\u002F30.0.3\u002Fv10\u002Fv10-admin\u002Foptions\u002Fchange-options-and-sizes.php\t2026-06-14 10:05:40.000000000 +0000\n@@ -13,6 +13,10 @@\n \u002F\u002Fecho 'This request contained ' . count( $_POST, COUNT_RECURSIVE ) . ' POST vars, ' . count( $_GET ) . ' GET vars, and ' . count( $_COOKIE ) . ' Cookies.';\n \u002F\u002Fdie;\n \n+if(!is_user_logged_in() || !current_user_can('publish_posts') || !cg_user_has_backend_access()){\n+    cg_die_missing_backend_access();\n+}\n+\n check_admin_referer('cg_admin');\n \n $id = absint($_GET['option_id']);\n@@ -1236,10 +1240,24 @@\n         $CheckCookieAlertMessage = (isset($_POST['CheckCookieAlertMessage'])) ? contest_gal1ery_htmlentities_and_preg_replace($_POST['CheckCookieAlertMessage']) : $CheckCookieAlertMessage;\n \n         if($dbVersion>=14){\u002F\u002F for new galleries after 14\n+            $currentRegistryUserRole = $wpdb->get_var( \"SELECT RegistryUserRole FROM $tablename_registry_and_login_options WHERE GeneralID = 1\");\n+        }else{\u002F\u002F for older galleries before 14\n+            $currentRegistryUserRole = $unsavingValues->RegistryUserRole;\n+        }\n+        $currentRegistryUserRole = cg_get_safe_registry_user_role($currentRegistryUserRole,$dbVersion);\n+\n+        if(current_user_can('manage_options')){\n+            $postedRegistryUserRole = (isset($_POST['RegistryUserRole'])) ? $_POST['RegistryUserRole'] : $currentRegistryUserRole;\n+            $safeRegistryUserRole = cg_get_safe_registry_user_role($postedRegistryUserRole,$dbVersion);\n+        }else{\n+            $safeRegistryUserRole = $currentRegistryUserRole;\n+        }\n+\n+        if($dbVersion>=14){\u002F\u002F for new galleries after 14\n             $RegistryUserRole = '';\n-            $RegistryUserRoleForRegistryAndLoginOptions = sanitize_text_field(htmlentities((isset($_POST['RegistryUserRole'])) ? $_POST['RegistryUserRole'] : ''));\n+            $RegistryUserRoleForRegistryAndLoginOptions = $safeRegistryUserRole;\n         }else{\u002F\u002F for older galleries before 14\n-            $RegistryUserRole = sanitize_text_field(htmlentities((isset($_POST['RegistryUserRole'])) ? $_POST['RegistryUserRole'] : ''));\n+            $RegistryUserRole = $safeRegistryUserRole;\n             $RegistryUserRoleForRegistryAndLoginOptions = '';\n         }","The exploit requires an authenticated user with at least Author-level permissions (or any user possessing the 'edit_posts' capability). \n\n1. Log in to the WordPress admin panel as an Author.\n2. Navigate to the Contest Gallery settings page to retrieve a valid 'cg_admin' nonce (localized in JavaScript or present in the form).\n3. Send a POST request to the plugin's settings handler (v10\u002Fv10-admin\u002Foptions\u002Fchange-options-and-sizes.php) with the following payload:\n    - `_wpnonce`: The extracted 'cg_admin' nonce.\n    - `RegistryUserRole`: 'administrator'.\n4. This action overwrites the plugin's internal setting for default registration roles without checking if the requester has 'manage_options' capabilities.\n5. To complete the escalation, the attacker (or a victim) registers a new account using the Google Sign-In feature. The plugin retrieves the malicious 'administrator' role from the database and assigns it to the new user via `wp_update_user()`.","gemini-3-flash-preview","2026-06-25 23:02:49","2026-06-25 23:03:41",{"type":42,"vulnerable_version":43,"fixed_version":11,"vulnerable_browse":44,"vulnerable_zip":45,"fixed_browse":46,"fixed_zip":47,"all_tags":48},"plugin","30.0.2","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Fcontest-gallery\u002Ftags\u002F30.0.2","https:\u002F\u002Fdownloads.wordpress.org\u002Fplugin\u002Fcontest-gallery.30.0.2.zip","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Fcontest-gallery\u002Ftags\u002F30.0.3","https:\u002F\u002Fdownloads.wordpress.org\u002Fplugin\u002Fcontest-gallery.30.0.3.zip","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Fcontest-gallery\u002Ftags"]