CVE-2026-4607

ProfileGrid <= 5.9.8.4 - Missing Authorization to Authenticated (Subscriber+) Group Settings Modification

mediumMissing Authorization
4.3
CVSS Score
4.3
CVSS Score
medium
Severity
5.9.8.5
Patched in
2d
Time to patch

Description

The ProfileGrid – User Profiles, Groups and Communities plugin for WordPress is vulnerable to authorization bypass in all versions up to, and including, 5.9.8.4. This is due to the plugin not properly verifying that a user is authorized to perform an action via the pm_set_group_order, pm_set_group_items, and pm_set_field_order AJAX actions. This makes it possible for authenticated attackers, with Subscriber-level access and above, to modify site-wide ProfileGrid group settings including group menu order, group list order, group icon display, and field ordering.

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<=5.9.8.4
PublishedMay 12, 2026
Last updatedMay 13, 2026

What Changed in the Fix

Changes introduced in v5.9.8.5

Loading patch diff...

Source Code

WordPress.org SVN
Research Plan
Unverified

# Research Plan: ProfileGrid Authorization Bypass (CVE-2026-4607) ## 1. Vulnerability Summary The **ProfileGrid** plugin (<= 5.9.8.4) fails to perform authorization checks in several AJAX handlers responsible for site-wide configuration. While these actions are intended for administrators, they are…

Show full research plan

Research Plan: ProfileGrid Authorization Bypass (CVE-2026-4607)

1. Vulnerability Summary

The ProfileGrid plugin (<= 5.9.8.4) fails to perform authorization checks in several AJAX handlers responsible for site-wide configuration. While these actions are intended for administrators, they are registered via wp_ajax_ without accompanying current_user_can() checks. This allows any authenticated user (including Subscriber-level accounts) to modify global plugin settings, such as group ordering, field ordering, and display preferences for the "All Groups" page.

2. Attack Vector Analysis

  • Endpoint: /wp-admin/admin-ajax.php
  • Vulnerable Actions:
    • pm_set_group_order (reorders groups)
    • pm_set_group_items (toggles site-wide group display settings)
    • pm_set_field_order (reorders fields within a group)
  • Authentication: Authenticated (Subscriber or higher)
  • Payload Parameters:
    • action: The AJAX action name.
    • nonce: A WordPress nonce (obtained from localized JS).
    • order[]: For ordering actions (array of IDs).
    • [setting_name]: For pm_set_group_items, specific toggle keys (e.g., pm_groups_show_icon).

3. Code Flow

  1. Registration: The plugin registers AJAX actions in its initialization phase. These handlers do not check for administrative capabilities.
  2. Access: A Subscriber user logs into the WordPress dashboard. Even with limited access, admin_enqueue_scripts triggers, loading the plugin's admin scripts.
  3. Nonce Exposure: In admin/class-profile-magic-admin.php, the function enqueue_scripts() calls wp_localize_script() on the handle profile-magic-license, passing an object named pg_admin_license_settings which contains a nonce.
  4. Execution: The attacker sends a POST request to admin-ajax.php. The handler verifies the nonce but fails to verify if the user is an administrator.
  5. Sink: The handler performs database updates via PM_DBhandler (updating pm_groups or pm_fields tables) or update_option() for site-wide settings.

4. Nonce Acquisition Strategy

The nonce is localized for all logged-in users in the WordPress admin area.

  1. Shortcode/Page: No specific shortcode is needed because the script is enqueued on admin pages like the user profile.
  2. Method:
    • Log in as a Subscriber user.
    • Navigate to /wp-admin/profile.php.
    • Use browser_eval to extract the nonce from the global JavaScript object.
  3. JS Identifier: window.pg_admin_license_settings?.nonce (localized in admin/class-profile-magic-admin.php).

5. Exploitation Strategy

We will exploit pm_set_group_items to modify a site-wide setting (disabling group icons), as it is the easiest to verify without complex database setup.

  1. Pre-authentication: Log in as a Subscriber.
  2. Nonce Extraction: Navigate to /wp-admin/profile.php and run browser_eval("pg_admin_license_settings.nonce").
  3. Request:
    • URL: http://localhost:8888/wp-admin/admin-ajax.php
    • Method: POST
    • Content-Type: application/x-www-form-urlencoded
    • Body:
      action=pm_set_group_items&pm_groups_show_icon=0&nonce=[NONCE_VALUE]
      
  4. Alternative Payload (Ordering):
    • action=pm_set_group_order&order[]=2&order[]=1&nonce=[NONCE_VALUE]

6. Test Data Setup

  1. User: Create a user with the Subscriber role.
  2. Plugin State: Ensure ProfileGrid is active.
  3. Initial Setting: Verify the default value of pm_groups_show_icon (usually 1).
    • wp option get pm_groups_show_icon
  4. Content: (Optional for pm_set_group_order) Create two groups using the UI or WP-CLI if needed to test ordering.

7. Expected Results

  • The AJAX request should return a success status (often 1 or a JSON success message).
  • The WordPress database option pm_groups_show_icon will be updated from 1 to 0.
  • This change will reflect site-wide on any page displaying ProfileGrid groups.

8. Verification Steps

After the exploitation request, use WP-CLI to check the updated state:

# Check the site-wide setting for group icons
wp option get pm_groups_show_icon
# Expected output: 0 (if the exploit worked)

9. Alternative Approaches

If pm_set_group_items is not the correct action name (though based on pg_init_all_groups_page_visibility_defaults it is highly likely), target pm_set_group_order:

  1. Identify group IDs: wp eval 'global $wpdb; print_r($wpdb->get_results("SELECT id FROM {$wpdb->prefix}pm_groups"));'
  2. Payload: action=pm_set_group_order&order[]=ID_2&order[]=ID_1&nonce=[NONCE]
  3. Verification: wp eval 'global $wpdb; print_r($wpdb->get_results("SELECT id, ordering FROM {$wpdb->prefix}pm_groups"));' to see if ordering values swapped.
Research Findings
Static analysis — not yet PoC-verified

Summary

The ProfileGrid plugin for WordPress is vulnerable to a missing authorization check on several AJAX actions, including pm_set_group_order, pm_set_group_items, and pm_set_field_order. This allows authenticated users with Subscriber-level permissions to bypass intended administrative restrictions and modify global plugin configurations, such as group ordering and site-wide display settings.

Vulnerable Code

// admin/class-profile-magic-admin.php around line 870

	public function profile_magic_set_field_order() {
		include 'partials/set-fields-order.php';
		die;
	}

	public function profile_magic_set_group_order() {
		include 'partials/set-groups-order.php';
		die;
	}

	public function profile_magic_set_group_items() {
		include 'partials/set-groups-order.php';
		die;
	}

Security Fix

--- /home/deploy/wp-safety.org/data/plugin-versions/profilegrid-user-profiles-groups-and-communities/5.9.8.4/admin/class-profile-magic-admin.php	2026-03-12 09:28:08.000000000 +0000
+++ /home/deploy/wp-safety.org/data/plugin-versions/profilegrid-user-profiles-groups-and-communities/5.9.8.5/admin/class-profile-magic-admin.php	2026-03-26 10:46:24.000000000 +0000
@@ -868,20 +868,33 @@
 
 
 	public function profile_magic_set_field_order() {
+		$this->pg_validate_reorder_ajax_request();
 		include 'partials/set-fields-order.php';
 		die;
 	}
 
 	public function profile_magic_set_group_order() {
+		$this->pg_validate_reorder_ajax_request();
 		include 'partials/set-groups-order.php';
 		die;
 	}
 
 	public function profile_magic_set_group_items() {
+		$this->pg_validate_reorder_ajax_request();
 		include 'partials/set-groups-order.php';
 		die;
 	}
 
+	private function pg_validate_reorder_ajax_request() {
+		if ( ! current_user_can( 'manage_options' ) ) {
+			wp_send_json_error( esc_html__( 'Unauthorized', 'profilegrid-user-profiles-groups-and-communities' ), 403 );
+		}
+
+		if ( ! check_ajax_referer( 'ajax-nonce', 'nonce', false ) ) {
+			wp_send_json_error( esc_html__( 'Failed security check', 'profilegrid-user-profiles-groups-and-communities' ), 403 );
+		}
+	}

Exploit Outline

The exploit involves an authenticated user (Subscriber+) retrieving a valid nonce from the global 'pg_admin_license_settings' object, which is localized in the WordPress admin area (e.g., on /wp-admin/profile.php). The attacker then sends a POST request to /wp-admin/admin-ajax.php using actions like 'pm_set_group_items' or 'pm_set_group_order'. Since the backend handlers for these actions fail to check if the user has the 'manage_options' capability, they process the requests, allowing the attacker to change site-wide settings—such as toggling group icons or reordering group lists—by providing the appropriate parameters and the leaked nonce.

Check if your site is affected.

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