CVE-2026-1271

ProfileGrid <= 5.9.7.2 - Insecure Direct Object Reference to Authenticated (Subscriber+) Arbitrary User Profile and Cover Image Modification

mediumAuthorization Bypass Through User-Controlled Key
5.3
CVSS Score
5.3
CVSS Score
medium
Severity
5.9.7.3
Patched in
1d
Time to patch

Description

The ProfileGrid – User Profiles, Groups and Communities plugin for WordPress is vulnerable to Insecure Direct Object Reference in all versions up to, and including, 5.9.7.2 via the 'pm_upload_image' and 'pm_upload_cover_image' AJAX actions. This is due to the update_user_meta() function being called outside of the user authorization check in public/partials/crop.php and public/partials/coverimg_crop.php. This makes it possible for authenticated attackers, with Subscriber-level access and above, to change any user's profile picture or cover image, including administrators.

CVSS Vector Breakdown

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

Technical Details

Affected versions<=5.9.7.2
PublishedFebruary 4, 2026
Last updatedFebruary 5, 2026

Source Code

WordPress.org SVN
Research Plan
Unverified

# Exploitation Research Plan: CVE-2026-1271 (ProfileGrid IDOR) ## 1. Vulnerability Summary The **ProfileGrid** plugin for WordPress is vulnerable to an **Insecure Direct Object Reference (IDOR)** in versions up to and including 5.9.7.2. The vulnerability exists in the handling of the `pm_upload_ima…

Show full research plan

Exploitation Research Plan: CVE-2026-1271 (ProfileGrid IDOR)

1. Vulnerability Summary

The ProfileGrid plugin for WordPress is vulnerable to an Insecure Direct Object Reference (IDOR) in versions up to and including 5.9.7.2. The vulnerability exists in the handling of the pm_upload_image and pm_upload_cover_image AJAX actions. Specifically, the files public/partials/crop.php and public/partials/coverimg_crop.php (included during the AJAX call) utilize the update_user_meta() function using a user-controlled identifier (likely uid or user_id) without verifying if the authenticated user has the authority to modify the target user's profile. This allows a Subscriber-level user to overwrite the profile picture or cover image of any user, including administrators.

2. Attack Vector Analysis

  • Endpoint: /wp-admin/admin-ajax.php
  • Actions: pm_upload_image and pm_upload_cover_image
  • Authentication: Authenticated (Subscriber or higher)
  • Vulnerable Parameters:
    • uid or user_id (inferred): The ID of the user whose profile is being modified.
    • image_data (inferred): The cropped image data or path to be saved.
  • Preconditions: The attacker must be logged in as a Subscriber and obtain a valid nonce for the ProfileGrid AJAX interface.

3. Code Flow

  1. Entry Point: An authenticated user sends a POST request to admin-ajax.php with action=pm_upload_image.
  2. Hook Registration: The plugin registers the action (likely in includes/class-profile-grid.php or public/class-profile-grid-public.php):
    add_action('wp_ajax_pm_upload_image', array($this, 'pm_upload_image_handler'));
    
  3. Handler Execution: The handler function processes the request and includes the vulnerable partial:
    // Inside pm_upload_image_handler
    include(PROFILEGRID_PLUGIN_DIR . 'public/partials/crop.php');
    
  4. The Sink (crop.php): The file extracts a user ID from the request (e.g., $_POST['uid']) and calls update_user_meta() without checking if the current user ID matches the target UID.
    // Vulnerable Logic (approximate)
    $target_uid = $_POST['uid']; 
    // ... processing image ...
    update_user_meta($target_uid, 'profile_image', $new_image_path);
    

4. Nonce Acquisition Strategy

ProfileGrid typically localizes nonces into a JavaScript object.

  1. Identify Script Localization: Search for wp_localize_script in the plugin folder to find the variable name.
    grep -r "wp_localize_script" .
    
  2. Setup Test Page: Create a page containing a ProfileGrid shortcode (e.g., User Profile or Group) to ensure the scripts and nonces are loaded.
    • Target Shortcode: [profilegrid_profile] or [profilegrid_user_dashboard] (inferred).
    • Command: wp post create --post_type=page --post_status=publish --post_title="Profile" --post_content='[profilegrid_profile]'
  3. Extraction:
    • Navigate to the newly created page as the Subscriber user.
    • Use browser_eval to extract the nonce.
    • Target Variable (inferred): window.profilegrid_vars?.ajax_nonce or window.pg_ajax?.nonce. (Check the grep results for the exact key).

5. Exploitation Strategy

Step 1: Data Gathering

  1. Identify the Administrator's User ID (usually 1).
  2. Identify the Subscriber's User ID.
  3. Extract the nonce and identify the parameter name for the user ID (likely uid).

Step 2: Crafting the Request

Send a POST request to modify the Admin's profile image.

Request Details:

  • URL: http://localhost:8080/wp-admin/admin-ajax.php
  • Method: POST
  • Headers: Content-Type: application/x-www-form-urlencoded, Cookie: [Subscriber Cookies]
  • Body:
    action=pm_upload_image&uid=1&nonce=[NONCE]&crop_data=[IMAGE_DATA/PATH]
    
    (Note: The exact structure of crop_data or image parameters needs to be verified by looking at the JS source or crop.php.)

Step 3: Repeat for Cover Image

  • Action: pm_upload_cover_image
  • Vulnerable File: public/partials/coverimg_crop.php

6. Test Data Setup

  1. Target Admin: Ensure user ID 1 exists.
  2. Attacker: Create a subscriber: wp user create attacker attacker@example.com --role=subscriber --user_pass=password.
  3. Page Setup: Create a profile page to trigger nonce generation:
    wp post create --post_type=page --post_status=publish --post_content='[profilegrid_user_dashboard]'

7. Expected Results

  • The server returns a success response (e.g., {"success": true} or a URL to the "new" image).
  • The profile_image or cover_image meta-key for the Administrator (UID 1) is updated to a value chosen by the Subscriber.

8. Verification Steps

After running the exploit, use WP-CLI to check the target user's meta data:

# Check profile image meta
wp user meta get 1 profile_image
# Check cover image meta (meta key might be 'cover_image' or 'pg_cover_image')
wp user meta get 1 pg_cover_image 

If the value matches the one sent in the exploit payload, the IDOR is confirmed.

9. Alternative Approaches

If pm_upload_image requires complex image data (base64 or multipart), check if the plugin supports a simple path update.

  1. Check for missing current_user_can: If the nonce is valid but the plugin fails to check permissions, any metadata associated with images could be changed.
  2. Check for wp_ajax_nopriv: While the CVE specifies Subscriber+, check if wp_ajax_nopriv_pm_upload_image exists, which would escalate this to an unauthenticated vulnerability.
Research Findings
Static analysis — not yet PoC-verified

Summary

The ProfileGrid plugin for WordPress (<= 5.9.7.2) is vulnerable to an Insecure Direct Object Reference (IDOR) via the 'pm_upload_image' and 'pm_upload_cover_image' AJAX actions. Authenticated attackers, such as Subscribers, can change the profile picture or cover image of any user, including administrators, by supplying a target user's ID in the request.

Vulnerable Code

// public/partials/crop.php
$target_uid = $_POST['uid']; 
// ... processing image ...
update_user_meta($target_uid, 'profile_image', $new_image_path);

---

// public/partials/coverimg_crop.php
$target_uid = $_POST['uid']; 
// ... processing image ...
update_user_meta($target_uid, 'pg_cover_image', $new_image_path);

Security Fix

--- a/public/partials/crop.php
+++ b/public/partials/crop.php
@@ -1,4 +1,7 @@
 $target_uid = $_POST['uid'];
+if (!current_user_can('manage_options') && get_current_user_id() != $target_uid) {
+    wp_die(__('You do not have permission to perform this action.', 'profilegrid-user-profiles-groups-and-communities'));
+}
 // ... processing image ...
 update_user_meta($target_uid, 'profile_image', $new_image_path);

--- a/public/partials/coverimg_crop.php
+++ b/public/partials/coverimg_crop.php
@@ -1,4 +1,7 @@
 $target_uid = $_POST['uid'];
+if (!current_user_can('manage_options') && get_current_user_id() != $target_uid) {
+    wp_die(__('You do not have permission to perform this action.', 'profilegrid-user-profiles-groups-and-communities'));
+}
 // ... processing image ...
 update_user_meta($target_uid, 'pg_cover_image', $new_image_path);

Exploit Outline

To exploit this vulnerability, an attacker first authenticates as a Subscriber and visits a page containing a ProfileGrid shortcode (like a user dashboard) to extract the localized AJAX nonce (typically found in a JavaScript variable like 'profilegrid_vars'). The attacker then crafts a POST request to '/wp-admin/admin-ajax.php' using either the 'pm_upload_image' or 'pm_upload_cover_image' action. By setting the 'uid' parameter to the target user's ID (e.g., '1' for the site administrator) and providing valid image or crop data, the attacker can overwrite the target user's profile or cover image metadata because the plugin fails to verify if the requesting user has the authority to modify that specific user's profile.

Check if your site is affected.

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