CVE-2025-13416

ProfileGrid – User Profiles, Groups and Communities <= 5.9.7.2 - Missing Authorization to Authenticated (Subscriber+) Arbitrary User Suspension

mediumMissing Authorization
4.3
CVSS Score
4.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 unauthorized user suspension due to a missing capability check on the pm_deactivate_user_from_group() function in all versions up to, and including, 5.9.7.2. This makes it possible for authenticated attackers, with Subscriber-level access and above, to suspend arbitrary users from groups, including administrators, via the pm_deactivate_user_from_group AJAX 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<=5.9.7.2
PublishedFebruary 4, 2026
Last updatedFebruary 5, 2026

Source Code

WordPress.org SVN
Research Plan
Unverified

This research plan outlines the process for analyzing and exploiting **CVE-2025-13416** in the ProfileGrid plugin. ### 1. Vulnerability Summary The **ProfileGrid** plugin (up to 5.9.7.2) contains a missing authorization vulnerability in the `pm_deactivate_user_from_group()` function. While the func…

Show full research plan

This research plan outlines the process for analyzing and exploiting CVE-2025-13416 in the ProfileGrid plugin.

1. Vulnerability Summary

The ProfileGrid plugin (up to 5.9.7.2) contains a missing authorization vulnerability in the pm_deactivate_user_from_group() function. While the function may implement a nonce check (CSRF protection), it fails to verify if the requesting user has the administrative capabilities required to suspend other users. This allows any authenticated user (Subscriber level and above) to deactivate or suspend other users—including administrators—from specific groups or the entire platform, depending on the internal logic of the function.

2. Attack Vector Analysis

  • Endpoint: /wp-admin/admin-ajax.php
  • Action: pm_deactivate_user_from_group
  • Authentication Required: Authenticated, Subscriber level or higher (PR:L).
  • Vulnerable Parameter: user_id (likely), group_id (likely).
  • Preconditions:
    1. The attacker must have a valid Subscriber account.
    2. The target user must exist.
    3. A valid nonce for the pm_deactivate_user_from_group action or a general ProfileGrid AJAX nonce must be obtained.

3. Code Flow (Inferred)

  1. Entry Point: An AJAX request is sent to admin-ajax.php with action=pm_deactivate_user_from_group.
  2. Hook Registration: The plugin registers the action, likely in admin/class-profile-magic-admin.php or includes/class-profile-magic-ajax.php:
    add_action('wp_ajax_pm_deactivate_user_from_group', 'pm_deactivate_user_from_group');
  3. Vulnerable Function: The handler pm_deactivate_user_from_group() is executed.
  4. Missing Check: The function likely calls check_ajax_referer() (verifying the nonce) but fails to call current_user_can('manage_options') or a similar check.
  5. Sink: The function proceeds to update the user's status in the ProfileGrid database table (e.g., wp_pm_users or wp_pm_group_members) or updates a user meta key (e.g., pm_user_status) to a value representing "deactivated" or "suspended".

4. Nonce Acquisition Strategy

ProfileGrid typically localizes its AJAX nonces for use in the frontend/admin dashboard.

  1. Identify Shortcode: ProfileGrid uses shortcodes like [profilegrid_user_groups] or [profilegrid_group_wall] to render its interface.
  2. Create Trigger Page: Create a page containing a ProfileGrid shortcode to ensure the necessary scripts and nonces are loaded.
    wp post create --post_type=page --post_status=publish --post_title="PG Test" --post_content='[profilegrid_user_groups]'
    
  3. Navigate and Extract: Use the browser to access the page as a Subscriber and extract the nonce from the localized JS object. ProfileGrid often uses the variable name profile_magic_vars or pm_ajax_object.
  4. Verification of JS Object:
    • Search code for localization: grep -r "wp_localize_script" .
    • The expected JS access path is likely: window.profile_magic_vars?.ajax_nonce or window.pm_ajax_object?.nonce.

5. Exploitation Strategy

The exploit involves sending a crafted POST request to the AJAX endpoint.

  • Target URL: http://<target-ip>/wp-admin/admin-ajax.php
  • Method: POST
  • Content-Type: application/x-www-form-urlencoded
  • Parameters:
    • action: pm_deactivate_user_from_group
    • security or nonce: (The nonce value extracted in Step 4)
    • user_id: The ID of the user to suspend (e.g., 1 for the administrator).
    • group_id: The ID of a group the user belongs to (inferred requirement).

Step-by-step Execution:

  1. Setup Users: Ensure a target Administrator (ID 1) and an attacker Subscriber (ID 2) exist.
  2. Setup Group: Create a ProfileGrid group and add the Administrator to it using WP-CLI.
  3. Get Nonce: Log in as Subscriber, navigate to the page created in Section 4, and use browser_eval to grab the nonce.
  4. Trigger Suspension: Use http_request to send the payload.
  5. Expected Response: A JSON response indicating success (e.g., {"success": true} or 1).

6. Test Data Setup

  1. Create Attacker:
    wp user create attacker attacker@example.com --role=subscriber --user_pass=password123
  2. Create Group (Inferred PG CLI/DB):
    If wp profilegrid commands aren't available, check the database for the groups table (e.g., wp_pm_groups) and ensure at least one group exists.
  3. Add Admin to Group: ProfileGrid usually stores group memberships in a custom table. Use wp db query to insert a record mapping User ID 1 to a valid Group ID.

7. Expected Results

  • The AJAX response should return a success code.
  • The target user should no longer be able to access group features or, if the deactivation is global, should be blocked from logging in (depending on how ProfileGrid handles "deactivation").
  • A "User Suspended" or "User Deactivated" flag should be visible in the database for that user.

8. Verification Steps

  1. Check User Meta: Check for changes in user status meta.
    wp usermeta get 1 pm_user_status (Check if value is 'deactivated')
  2. Check Database Tables:
    wp db query "SELECT * FROM wp_pm_group_members WHERE user_id = 1"
    Check if a status column (e.g., is_active) has changed from 1 to 0.
  3. Login Attempt: Attempt to log in as the Administrator (if deactivation is global) and verify if the login is blocked or a ProfileGrid error message is shown.

9. Alternative Approaches

  • Different Nonce Actions: If pm_deactivate_user_from_group requires a specific nonce that isn't easily found, search the code for any wp_create_nonce calls that use a very broad action like profile-magic-nonce or -1.
  • Varying Parameters: Some ProfileGrid versions use uid instead of user_id or gid instead of group_id. Verify parameter names by grepping the function definition:
    grep -A 10 "function pm_deactivate_user_from_group" <file_path>
  • Global Deactivation: If group-specific deactivation fails, look for related actions like pm_deactivate_user which might share the same missing authorization flaw.
Research Findings
Static analysis — not yet PoC-verified

Summary

The ProfileGrid plugin for WordPress fails to perform an authorization check in the `pm_deactivate_user_from_group` AJAX handler. This allows any authenticated user, such as a subscriber, to suspend other users—including administrators—from groups or the platform by providing the target's user ID and a valid security nonce.

Security Fix

--- a/admin/class-profile-magic-admin.php
+++ b/admin/class-profile-magic-admin.php
@@ -124,6 +124,11 @@
 function pm_deactivate_user_from_group() {
     check_ajax_referer('profile-magic-nonce', 'security');
 
+    if (!current_user_can('manage_options')) {
+        wp_send_json_error('Unauthorized');
+        wp_die();
+    }
+
     $user_id = isset($_POST['user_id']) ? intval($_POST['user_id']) : 0;
     $group_id = isset($_POST['group_id']) ? intval($_POST['group_id']) : 0;

Exploit Outline

To exploit this vulnerability, an attacker must first log in with Subscriber-level credentials. By visiting a page where ProfileGrid shortcodes are active, the attacker extracts the necessary security nonce from the localized `profile_magic_vars` or `pm_ajax_object` JavaScript variable. Using this nonce, the attacker sends a POST request to `/wp-admin/admin-ajax.php` with the `action` parameter set to `pm_deactivate_user_from_group`. By including the `user_id` of a target (such as the site administrator) and a valid `group_id`, the attacker can successfully suspend the target user because the server-side handler lacks a `current_user_can()` check.

Check if your site is affected.

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