Ultimate Member <= 2.11.1 - Authenticated (Subscriber+) Stored Cross-Site Scripting via DOM Gadgets
Description
The Ultimate Member – User Profile, Registration, Login, Member Directory, Content Restriction & Membership Plugin plugin for WordPress is vulnerable to Stored Cross-Site Scripting via the user description field in all versions up to, and including, 2.11.1 due to insufficient input sanitization and output escaping. This makes it possible for authenticated attackers, with subscriber level access and above, to inject arbitrary web scripts in pages that will execute whenever a user accesses an injected page. The vulnerability is only exploitable when "HTML support for user description" is enabled in Ultimate Member settings.
CVSS Vector Breakdown
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:L/I:L/A:NTechnical Details
<=2.11.1What Changed in the Fix
Changes introduced in v2.11.2
Source Code
WordPress.org SVNtip`, reads `title` attribute, and uses `.html()` to render it. 4. **Nonce Strategy**: Navigate to profile edit page, extract `_wpnonce` from the form. 5. **Exploitation Strategy**: POST to profile page with the payload. 6. **Test Data**: Enable `profile_desc_html` in `um_options`. …
Show full research plan
tip, reads titleattribute, and uses.html()to render it. 4. **Nonce Strategy**: Navigate to profile edit page, extract_wpnoncefrom the form. 5. **Exploitation Strategy**: POST to profile page with the payload. 6. **Test Data**: Enableprofile_desc_htmlinum_options`.
7. Verification: Check user meta and trigger the XSS.
*Action Check:* Ultimate Member profile updates often use `POST` directly to the profile URL.
The URL: `/[base-profile-path]/user/[username]/?um_action=edit`.
The `base-profile-path` is usually `/user/`.
*Specific Setting Key:*
In `um_options` array: `profile_desc_html`.
To set it:
```bash
wp option patch update um_options profile_desc_html 1
```
*Payload for verification:*
`<div class="um-tip" title="<img src=x onerror=alert(1)>">XSS</div>`
The `<` will be handled correctly by the browser's attribute parsing and then the JS gadget.
*Final check on the JS Sink:*
In `assets/js/um-members.js`: `UM.common.tipsy.init()`.
In the real `um-scripts.js`
Summary
The Ultimate Member plugin is vulnerable to Stored Cross-Site Scripting via the user description field when the 'HTML support for user description' setting is enabled. Authenticated attackers with Subscriber-level access can inject malicious HTML payloads using the '.um-tip' DOM gadget class. When the user profile is viewed, the plugin's tooltips JavaScript (Tipsy) processes the gadget and renders its 'title' attribute as unsanitized HTML, leading to execution of arbitrary scripts.
Vulnerable Code
/* assets/js/um-members.js */ /* This function triggers the vulnerable tooltip initialization which renders attributes as HTML */ function um_build_template(e,r){ // ... jQuery(document).trigger("um_build_template",[e,r]),jQuery(window).trigger("resize"),UM.common.tipsy.init()) } --- /* assets/css/um-styles.css line 558 */ .um-tip { margin: 0 0 0 8px; cursor: pointer; display: inline-block; position: relative; top: 3px; }
Security Fix
@@ -900,14 +900,14 @@ max-width: 300px; } -.um-search-area .um-search-field { +.um .um-form.um-search-area .um-search-field { width: 100%; margin: 0; - padding-left: 25px; + padding-left: 25px !important; } -.rtl .um-search-area .um-search-field { - padding-right: 25px; +.rtl .um .um-form.um-search-area .um-search-field { + padding-right: 25px !important; padding-left: initial; }
Exploit Outline
1. Authenticate as a Subscriber-level user. 2. Ensure the setting 'HTML support for user description' is enabled in the plugin's configuration (key 'profile_desc_html'). 3. Navigate to the user profile edit page (e.g., /user/[username]/?um_action=edit) and extract the required '_wpnonce'. 4. Inject a payload into the user description field that utilizes the '.um-tip' DOM gadget. A working payload structure is: <div class="um-tip" title="<img src=x onerror=alert(1)>">Hover me</div>. 5. Save the profile changes. The payload is stored in user metadata. 6. When any user (including administrators) views the attacker's profile page, the UM.common.tipsy.init() JavaScript function processes the '.um-tip' element, reads the 'title' attribute, and renders it as HTML, resulting in script execution.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.