CVE-2025-15030

User Profile Builder <= 3.15.1 - Unauthenticated Privilege Escalation via Account Takeover

criticalUnverified Password Change
9.8
CVSS Score
9.8
CVSS Score
critical
Severity
3.15.2
Patched in
31d
Time to patch

Description

The User Profile Builder – Beautiful User Registration Forms, User Profiles & User Role Editor plugin for WordPress is vulnerable to privilege escalation via account takeover in all versions up to, and including, 3.15.1. This is due to the plugin not properly validating a user's identity prior to updating their password. This makes it possible for unauthenticated attackers to change arbitrary user's passwords, including administrators, and leverage that to gain access to their account.

CVSS Vector Breakdown

CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H
Attack Vector
Network
Attack Complexity
Low
Privileges Required
None
User Interaction
None
Scope
Unchanged
High
Confidentiality
High
Integrity
High
Availability

Technical Details

Affected versions<=3.15.1
PublishedJanuary 12, 2026
Last updatedFebruary 11, 2026
Affected pluginprofile-builder

Source Code

WordPress.org SVN
Research Plan
Unverified

# Research Plan: CVE-2025-15030 - Profile Builder Account Takeover ## 1. Vulnerability Summary The **User Profile Builder** plugin (<= 3.15.1) contains a critical privilege escalation vulnerability. The core issue lies in the front-end profile editing logic, specifically within the `wppb_edit_profi…

Show full research plan

Research Plan: CVE-2025-15030 - Profile Builder Account Takeover

1. Vulnerability Summary

The User Profile Builder plugin (<= 3.15.1) contains a critical privilege escalation vulnerability. The core issue lies in the front-end profile editing logic, specifically within the wppb_edit_profile_handler (or similar handler for the [wppb-edit-profile] shortcode). The plugin fails to verify two critical things before updating a user's password:

  1. Authentication: It does not ensure the request is from a logged-in user.
  2. Authorization (IDOR): It does not ensure that the user ID being updated (user_id parameter) matches the ID of the currently authenticated user.

This allows an unauthenticated attacker to change the password of any user, including administrators, by knowing their user ID (typically 1 for the first admin).

2. Attack Vector Analysis

  • Endpoint: Any page containing the [wppb-edit-profile] shortcode, or the admin-ajax.php endpoint if the handler is registered there.
  • Method: POST
  • Parameters:
    • user_id: The ID of the target user (e.g., 1).
    • pass1: The new password.
    • pass2: Confirmation of the new password.
    • wppb_edit_profile: The nonce variable required to pass the security check.
    • action: (If AJAX) wppb_edit_profile.
  • Preconditions:
    • An administrator user must exist (default ID 1).
    • A page with the [wppb-edit-profile] shortcode must be active, OR the attacker must be able to trigger the init hook handler.

3. Code Flow

  1. Entry Point: During the init or wp_loaded hook, the plugin initializes assets/lib/wppb/front-end/edit-profile.php.
  2. Hook Registration: The function wppb_edit_profile_handler() is registered to handle profile updates.
  3. Vulnerable Logic:
    • The function checks if $_POST['wppb_edit_profile'] (the nonce) is set.
    • It verifies the nonce using wp_verify_nonce( $_POST['wppb_edit_profile'], 'wppb_edit_profile_nonce' ).
    • The Flaw: If the nonce is valid for the current user session (even for an unauthenticated user ID 0), the code proceeds.
    • It retrieves the target user ID from $_POST['user_id'] or $_GET['edit_user'].
    • It calls wp_update_user() or wp_set_password() using the pass1 value from $_POST.
    • There is no check like if ( ! is_user_logged_in() ) or if ( $current_user_id !== $target_user_id ) before the password update occurs.

4. Nonce Acquisition Strategy

The plugin uses nonces to protect form submissions. To exploit this unauthenticated, we need a nonce generated for a logged-out user (UID 0).

  1. Identify Shortcode: The plugin uses [wppb-edit-profile].
  2. Create Test Page:
    wp post create --post_type=page --post_title="Edit Profile" --post_status=publish --post_content='[wppb-edit-profile]'
    
  3. Extract Nonce:
    • Navigate to the newly created page. Even if the page content says "You must be logged in," the plugin may still enqueue scripts or hidden fields containing nonces.
    • Use browser_eval to look for the nonce in the localized JS or the form:
      // Check for common PB JS objects
      window.wppb_ajax_nonce || document.querySelector('input[name="wppb_edit_profile"]')?.value
      
    • The specific key is likely wppb_edit_profile (the input name) and the action used to create it is wppb_edit_profile_nonce.

5. Exploitation Strategy

  1. Target User ID: Identify the admin ID (usually 1).
  2. Request Construction:
    • URL: The URL of the page containing the [wppb-edit-profile] shortcode.
    • Headers: Content-Type: application/x-www-form-urlencoded
    • Payload:
      wppb_edit_profile=[NONCE]&user_id=1&pass1=Password123!&pass2=Password123!&action=wppb_edit_profile
      
  3. Execution: Use http_request to send the payload.
  4. Success Indicator: A redirect or a success message indicating the profile was updated, and subsequent inability to login with the old admin password.

6. Test Data Setup

  1. Admin User: Ensure an admin user exists (e.g., username admin, ID 1).
  2. Vulnerable Page:
    • Create a page with the Edit Profile shortcode:
      wp post create --post_type=page --post_status=publish --post_content='[wppb-edit-profile]'
  3. Settings: Ensure "Allow users to edit their profiles" is enabled in Profile Builder settings (usually enabled by default).

7. Expected Results

  • The HTTP response should indicate a successful form submission (e.g., HTTP 302 redirect or a 200 OK with "Profile updated" in the body).
  • The administrator's password in the database will be changed to Password123!.

8. Verification Steps

  1. Check via WP-CLI: Attempt to verify the admin's credentials:
    wp user check-password admin Password123!
    
  2. Database Check: Verify the user_pass hash has changed in the wp_users table for ID 1.
  3. Login Test: Use browser_navigate to attempt login at /wp-login.php with the new credentials.

9. Alternative Approaches

  • AJAX Endpoint: If the shortcode POST fails, try hitting admin-ajax.php with action=wppb_edit_profile and the same parameters.
  • Username instead of ID: Some versions of PB might accept username or email instead of user_id. Try adding username=admin to the payload.
  • Password Recovery Bypass: If the edit-profile route is strictly blocked for guests, investigate assets/lib/wppb/front-end/recover-password.php. A similar IDOR might exist where the key parameter (reset token) is not properly validated, allowing a password change if the key is sent as an empty string.
Research Findings
Static analysis — not yet PoC-verified

Summary

The User Profile Builder plugin for WordPress is vulnerable to an unauthenticated account takeover because the profile editing handler fails to verify if the requester is authenticated or authorized to update a specific user. By exploiting a publicly accessible nonce, an attacker can submit a password change request for any user ID, including administrative accounts.

Vulnerable Code

// assets/lib/wppb/front-end/edit-profile.php
function wppb_edit_profile_handler() {
    if ( isset( $_POST['wppb_edit_profile'] ) && wp_verify_nonce( $_POST['wppb_edit_profile'], 'wppb_edit_profile_nonce' ) ) {
        $user_id = ( isset( $_POST['user_id'] ) ) ? intval( $_POST['user_id'] ) : ( ( isset( $_GET['edit_user'] ) ) ? intval( $_GET['edit_user'] ) : 0 );
        
        if ( ! empty( $user_id ) ) {
            // ... (other profile field updates)
            
            if ( ! empty( $_POST['pass1'] ) && $_POST['pass1'] === $_POST['pass2'] ) {
                wp_set_password( $_POST['pass1'], $user_id );
            }
        }
    }
}

Security Fix

--- assets/lib/wppb/front-end/edit-profile.php
+++ assets/lib/wppb/front-end/edit-profile.php
@@ -120,6 +120,15 @@
 function wppb_edit_profile_handler() {
     if ( isset( $_POST['wppb_edit_profile'] ) && wp_verify_nonce( $_POST['wppb_edit_profile'], 'wppb_edit_profile_nonce' ) ) {
+        if ( ! is_user_logged_in() ) {
+            return;
+        }
+
+        $current_user_id = get_current_user_id();
         $user_id = ( isset( $_POST['user_id'] ) ) ? intval( $_POST['user_id'] ) : ( ( isset( $_GET['edit_user'] ) ) ? intval( $_GET['edit_user'] ) : 0 );
+
+        if ( $user_id !== $current_user_id && ! current_user_can( 'edit_users' ) ) {
+            return;
+        }
+
         if ( ! empty( $user_id ) ) {
             if ( ! empty( $_POST['pass1'] ) && $_POST['pass1'] === $_POST['pass2'] ) {
                 wp_set_password( $_POST['pass1'], $user_id );

Exploit Outline

The exploit methodology involves four main steps: 1. Locating a front-end page that contains the [wppb-edit-profile] shortcode. 2. Extracting the 'wppb_edit_profile' nonce value from the page source as an unauthenticated visitor. 3. Submitting a POST request to that page containing the stolen nonce, a target 'user_id' (typically 1 for the default administrator), and new values for 'pass1' and 'pass2'. 4. Because the plugin does not verify that the logged-in user matches the target ID, the wp_set_password function is called for the specified user, allowing the attacker to take over the account.

Check if your site is affected.

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