ProfileGrid <= 5.9.8.4 - Missing Authorization to Authenticated (Subscriber+) Group Settings Modification
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:NTechnical Details
<=5.9.8.4What Changed in the Fix
Changes introduced in v5.9.8.5
Source Code
WordPress.org SVN# 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]: Forpm_set_group_items, specific toggle keys (e.g.,pm_groups_show_icon).
3. Code Flow
- Registration: The plugin registers AJAX actions in its initialization phase. These handlers do not check for administrative capabilities.
- Access: A Subscriber user logs into the WordPress dashboard. Even with limited access,
admin_enqueue_scriptstriggers, loading the plugin's admin scripts. - Nonce Exposure: In
admin/class-profile-magic-admin.php, the functionenqueue_scripts()callswp_localize_script()on the handleprofile-magic-license, passing an object namedpg_admin_license_settingswhich contains anonce. - 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. - Sink: The handler performs database updates via
PM_DBhandler(updatingpm_groupsorpm_fieldstables) orupdate_option()for site-wide settings.
4. Nonce Acquisition Strategy
The nonce is localized for all logged-in users in the WordPress admin area.
- Shortcode/Page: No specific shortcode is needed because the script is enqueued on admin pages like the user profile.
- Method:
- Log in as a Subscriber user.
- Navigate to
/wp-admin/profile.php. - Use
browser_evalto extract the nonce from the global JavaScript object.
- JS Identifier:
window.pg_admin_license_settings?.nonce(localized inadmin/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.
- Pre-authentication: Log in as a Subscriber.
- Nonce Extraction: Navigate to
/wp-admin/profile.phpand runbrowser_eval("pg_admin_license_settings.nonce"). - 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]
- URL:
- Alternative Payload (Ordering):
action=pm_set_group_order&order[]=2&order[]=1&nonce=[NONCE_VALUE]
6. Test Data Setup
- User: Create a user with the Subscriber role.
- Plugin State: Ensure ProfileGrid is active.
- Initial Setting: Verify the default value of
pm_groups_show_icon(usually1).wp option get pm_groups_show_icon
- 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
1or a JSON success message). - The WordPress database option
pm_groups_show_iconwill be updated from1to0. - 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:
- Identify group IDs:
wp eval 'global $wpdb; print_r($wpdb->get_results("SELECT id FROM {$wpdb->prefix}pm_groups"));' - Payload:
action=pm_set_group_order&order[]=ID_2&order[]=ID_1&nonce=[NONCE] - Verification:
wp eval 'global $wpdb; print_r($wpdb->get_results("SELECT id, ordering FROM {$wpdb->prefix}pm_groups"));'to see iforderingvalues swapped.
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
@@ -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.