CVE-2025-62098

Portfolio Gallery <= 1.4.8 - Missing Authorization

mediumMissing Authorization
4.3
CVSS Score
4.3
CVSS Score
medium
Severity
Unpatched
Patched in
N/A
Time to patch

Description

The Portfolio Gallery plugin for WordPress is vulnerable to unauthorized access due to a missing capability check on a function in versions up to, and including, 1.4.8. 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<=1.4.8
PublishedDecember 31, 2025
Last updatedJanuary 5, 2026
Affected plugingallery-portfolio
Research Plan
Unverified

# Research Plan: CVE-2025-62098 - Portfolio Gallery <= 1.4.8 Missing Authorization ## 1. Vulnerability Summary The **Portfolio Gallery – Responsive Image Gallery** plugin (slug: `gallery-portfolio`) for WordPress is vulnerable to **Missing Authorization** in versions up to and including 1.4.8. The …

Show full research plan

Research Plan: CVE-2025-62098 - Portfolio Gallery <= 1.4.8 Missing Authorization

1. Vulnerability Summary

The Portfolio Gallery – Responsive Image Gallery plugin (slug: gallery-portfolio) for WordPress is vulnerable to Missing Authorization in versions up to and including 1.4.8. The vulnerability exists because certain AJAX handlers (likely registered in the admin context) do not perform a current_user_can() check before executing sensitive actions. While these handlers may check for a WordPress nonce to prevent CSRF, they do not restrict the action to administrative users, allowing any authenticated user (starting from the Subscriber role) to execute the function.

2. Attack Vector Analysis

  • Endpoint: /wp-admin/admin-ajax.php
  • Action: pg_save_gallery_ajax (inferred) or pg_delete_gallery_ajax (inferred).
  • HTTP Parameter: action, nonce, and action-specific data (e.g., id, settings).
  • Authentication: Authenticated, Subscriber-level access or higher.
  • Preconditions: The attacker must have a valid login and a valid nonce for the specific action.

3. Code Flow

  1. Registration: The plugin registers AJAX handlers during initialization, typically via add_action( 'wp_ajax_...', ... ).
  2. Entry Point: A Subscriber user sends a POST request to admin-ajax.php with the vulnerable action.
  3. Execution: WordPress invokes the callback function (e.g., pg_save_gallery_ajax_callback (inferred)).
  4. Verification (Partial): The function calls check_ajax_referer() or wp_verify_nonce(). Since the nonce is often localized for all logged-in users, the Subscriber passes this check.
  5. Vulnerability (The Sink): The function proceeds to perform a database operation (e.g., update_post_meta, wp_delete_post, or update_option) without calling current_user_can( 'manage_options' ).

4. Nonce Acquisition Strategy

Nonces in this plugin are typically exposed via wp_localize_script. Since the vulnerability involves authenticated users, the nonce is generated for the current user's ID.

  1. Identify Script Trigger: The plugin scripts are enqueued when a gallery is being edited or when a gallery is displayed via shortcode.
  2. Create Trigger Page:
    # Create a gallery first (using WP-CLI to ensure one exists)
    wp post create --post_type=portfolio_gallery --post_title="Target Gallery" --post_status=publish
    # The ID of this post will be used for the exploit
    
    # Create a page with the shortcode
    wp post create --post_type=page --post_title="Nonce Page" --post_status=publish --post_content='[portfolio_gallery]'
    
  3. Acquire Nonce via Browser:
    • Use browser_navigate to the newly created page.
    • Execute browser_eval to find the localization object.
    • Target Object (inferred): window.pg_ajax_vars or window.huge_it_portfolio_vars.
    • Command: browser_eval("window.pg_ajax_vars?.nonce") or browser_eval("window.pg_ajax_obj?.nonce").

5. Exploitation Strategy

We will attempt to modify the title or settings of an existing gallery.

  1. Step 1: Preparation. Identify a target gallery ID using wp post list --post_type=portfolio_gallery.
  2. Step 2: Nonce Collection. Log in as a Subscriber and visit a page where the plugin's JS is loaded to extract the pg_ajax_nonce (inferred).
  3. Step 3: Exploit Request. Send a POST request to modify the gallery.

Sample Request (using http_request):

{
  "method": "POST",
  "url": "http://localhost:8080/wp-admin/admin-ajax.php",
  "headers": {
    "Content-Type": "application/x-www-form-urlencoded"
  },
  "body": "action=pg_save_gallery_ajax&nonce=[NONCE_VALUE]&id=[GALLERY_ID]&title=Exploited_Title"
}

(Note: Parameters like id and title are inferred based on standard gallery plugin naming conventions and must be verified against the source code during execution.)

6. Test Data Setup

  1. Admin User: Required to create the initial gallery.
  2. Subscriber User:
    wp user create attacker attacker@example.com --role=subscriber --user_pass=password123
  3. Sample Gallery:
    wp post create --post_type=portfolio_gallery --post_title="Original Title" --post_status=publish
  4. Shortcode Page:
    wp post create --post_type=page --post_content='[portfolio_gallery]' --post_status=publish

7. Expected Results

  • Response: The server returns a success code (e.g., {"success":true} or 1).
  • Impact: The gallery title or configuration in the database is changed, despite the request coming from a Subscriber who should not have "manage_options" or gallery editing capabilities.

8. Verification Steps

After the exploit request, verify the change using WP-CLI:

# Check if the title was modified
wp post get [GALLERY_ID] --field=post_title

# If the exploit targeted post_meta/settings
wp post meta list [GALLERY_ID]

9. Alternative Approaches

  • Delete Action: If pg_save_gallery_ajax is not the vulnerable function, check for pg_delete_gallery_ajax or pg_update_order_ajax.
  • Import Functionality: Some versions of this plugin have a portfolio_gallery_import_sample_data action which often lacks authorization.
  • REST API: Check if the plugin registers any REST routes via register_rest_route without a permission_callback.
  • Search Pattern: If inferred names fail, use the agent to search the plugin directory:
    grep -r "wp_ajax_" . to find all registered actions and check their callbacks for current_user_can.
Research Findings
Static analysis — not yet PoC-verified

Summary

The Portfolio Gallery – Responsive Image Gallery plugin for WordPress is vulnerable to unauthorized access due to missing capability checks on administrative AJAX functions in versions up to 1.4.8. This allows authenticated attackers with subscriber-level permissions to perform restricted actions, such as modifying or saving gallery settings, by bypassing role-based access control.

Exploit Outline

To exploit this vulnerability, an attacker first authenticates with a Subscriber-level account and navigates to a page where the plugin's scripts are loaded (e.g., a page containing the [portfolio_gallery] shortcode) to extract the 'pg_ajax_nonce' from the localized 'pg_ajax_vars' JavaScript object. Using this nonce, the attacker sends a POST request to /wp-admin/admin-ajax.php with the 'action' parameter set to the vulnerable function (e.g., 'pg_save_gallery_ajax') and the target gallery ID. Because the backend code verifies the nonce but fails to perform a 'current_user_can()' check, the administrative action is executed despite the attacker's low privilege level.

Check if your site is affected.

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