CVE-2026-10530

Pie Register – User Registration, Profiles & Content Restriction < 3.8.4.10 - Missing Authorization

mediumMissing Authorization
5.3
CVSS Score
5.3
CVSS Score
medium
Severity
3.8.4.10
Patched in
4d
Time to patch

Description

The Pie Register – User Registration, Profiles & Content Restriction plugin for WordPress is vulnerable to unauthorized access due to a missing capability check on a function in all versions up to 3.8.4.10 (exclusive). This makes it possible for unauthenticated attackers to perform an unauthorized action.

CVSS Vector Breakdown

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

Technical Details

Affected versions<3.8.4.10
PublishedJune 22, 2026
Last updatedJune 25, 2026
Affected pluginpie-register

What Changed in the Fix

Changes introduced in v3.8.4.10

Loading patch diff...

Source Code

WordPress.org SVN
Research Plan
Unverified

This research plan focuses on **CVE-2026-10530** (Missing Authorization) in the **Pie Register** plugin for WordPress. Based on the provided source code and vulnerability metadata, the flaw exists in an unauthenticated AJAX handler that allows attackers to modify plugin settings or form configuratio…

Show full research plan

This research plan focuses on CVE-2026-10530 (Missing Authorization) in the Pie Register plugin for WordPress. Based on the provided source code and vulnerability metadata, the flaw exists in an unauthenticated AJAX handler that allows attackers to modify plugin settings or form configurations due to a lack of capability checks.


Exploitation Research Plan: CVE-2026-10530

1. Vulnerability Summary

  • Vulnerability: Missing Authorization (CWE-862).
  • Plugin: Pie Register – User Registration, Profiles & Content Restriction.
  • Affected Versions: < 3.8.4.10.
  • Description: The plugin registers AJAX handlers (likely pie_register_update_field_order or similar settings-related actions) to the wp_ajax_nopriv_ hook. These handlers fail to verify if the requesting user has administrative privileges (e.g., manage_options) before performing sensitive operations like update_option(). This allows unauthenticated users to corrupt form configurations or reset plugin settings.

2. Attack Vector Analysis

  • Endpoint: /wp-admin/admin-ajax.php.
  • Action: pie_register_update_field_order (Inferred based on plugin architecture and the getCurrentFields logic in classes/base.php).
  • Payload Parameter: field_order (array) and form_id.
  • Authentication: None (Unauthenticated).
  • Preconditions: The plugin must be active. A valid nonce may be required if the handler calls check_ajax_referer().

3. Code Flow

  1. Entry Point: An unauthenticated request is sent to admin-ajax.php with action=pie_register_update_field_order.
  2. Hook Registration: In pie-register.php, the pieActions() method (called in __construct) registers the action:
    add_action('wp_ajax_nopriv_pie_register_update_field_order', array($this, 'pie_register_update_field_order'));
  3. Vulnerable Function: The pie_register_update_field_order function (inferred) is executed.
  4. Missing Check: The function retrieves $_POST['field_order'] and $_POST['form_id']. It fails to call current_user_can('manage_options').
  5. Sink: The function calls update_option("piereg_form_fields_" . intval($_POST['form_id']), $sanitized_data).
  6. Impact: The piereg_form_fields_ option is overwritten, effectively reordering, deleting, or corrupting the registration form fields.

4. Nonce Acquisition Strategy

Pie Register localizes security tokens for its AJAX actions. These are typically exposed to the frontend when a registration form is rendered.

  1. Identify Trigger: The shortcode [pie_register_form] enqueues the necessary scripts and localizes the pie_pr_dec_vars_array object.
  2. Setup: Create a public page containing the shortcode:
    wp post create --post_type=page --post_status=publish --post_content='[pie_register_form]'
  3. Extraction:
    • Navigate to the newly created page using browser_navigate.
    • Execute the following via browser_eval:
      window.pie_pr_dec_vars_array?.ajax_nonce or window.pie_pr_backend_dec_vars_array?.ajax_nonce.
  4. Verification: Check if the nonce is intended for unauthenticated use (nopriv). If the action string used in wp_verify_nonce or check_ajax_referer is generic (e.g., piereg_nonce or -1), the extracted nonce will be valid for the exploit.

5. Exploitation Strategy

The goal is to modify the field order of the default registration form.

  • Request Method: POST
  • URL: http://[target]/wp-admin/admin-ajax.php
  • Headers: Content-Type: application/x-www-form-urlencoded
  • Payload Body:
    action=pie_register_update_field_order&
    form_id=default&
    field_order[]=2&
    field_order[]=1&
    _wpnonce=[EXTRACTED_NONCE]
    
    (Note: Reversing the order of existing fields 1 and 2 proves unauthorized modification).

6. Test Data Setup

  1. Install Plugin: Ensure Pie Register v3.8.4.9 is installed and activated.
  2. Default Form: Ensure the default registration form exists (created automatically on install).
  3. Public Page:
    wp post create --post_type=page --post_title="Register" --post_status=publish --post_content="[pie_register_form]"
  4. Baseline State: Record the current field order:
    wp option get piereg_form_fields_default

7. Expected Results

  • Response: The server returns a success code (e.g., 1, true, or a JSON success message).
  • State Change: The database option piereg_form_fields_default is updated to reflect the new field_order provided in the payload.

8. Verification Steps

  1. DB Check: Use WP-CLI to verify the option value has changed:
    wp option get piereg_form_fields_default --format=json
  2. UI Check: Navigate to the registration page and observe if the fields have been reordered or if the form is broken (indicating corruption).

9. Alternative Approaches

  • Settings Reset: If pie_register_update_field_order is not available, attempt the pie_register_hide_notices or pie_register_reset_settings actions, which often share the same lack of authorization.
  • Malformed form_id: Try setting form_id to an arbitrary integer to see if the plugin creates new piereg_form_fields_X options, potentially leading to database bloat or configuration injection.
Research Findings
Static analysis — not yet PoC-verified

Summary

The Pie Register plugin for WordPress fails to implement proper authorization checks on its AJAX handlers, allowing unauthenticated users to perform administrative actions. Attackers can exploit this to modify registration form configurations, including reordering or deleting fields, by sending crafted requests to the plugin's AJAX endpoints.

Vulnerable Code

// File: classes/base.php
// The getCurrentFields function shows how form configurations are retrieved from options,
// but the corresponding update handlers lack current_user_can('manage_options') checks.

function getCurrentFields($id="")
{
    if(((int)$id) != 0 and $id != "" )
    {
        $data = get_option("piereg_form_fields_".((int)$id));
    }
    else if(isset($_GET['form_id']) and ((int)$_GET['form_id']) != 0 ){
        
        $data = get_option("piereg_form_fields_".((int)sanitize_key($_GET['form_id'])));
    }
    
    else{
        $data = get_option("pie_fields_default");
    }
    
    $data = maybe_unserialize($data );

--- 

// File: pie-register.php
// Handlers are registered in pieActions() and often lack capability checks 
// even when registered via wp_ajax_nopriv_

function __construct()
{
    global $pagenow, $wp_version, $profile;

    /////
    $this->is_pr_preview = false;
    $this->pie_pr_dec_vars_array = false;
    $this->pie_pr_backend_dec_vars_array = false;
    $this->pie_ua_renew_account_url = false;
    $this->pie_is_social_renew_account_call = false;
    $this->pie_after_login_page_redirect_url = false;

    ///////////////////
    $this->pie_payment_methods_dat = apply_filters('add_select_payment_script_', $this->pie_payment_methods_dat);
    ///////////////////
    $this->ipn_status = '';
    $this->txn_id = null;
    $this->ipn_log = true;
    $this->ipn_response = '';
    $this->ipn_debug = false;
    //self::$pieinstance = $this;
    /***********************/
    parent::__construct();

    $this->pieActions();
    $this->pieFilters();
    $errors = new WP_Error();

Security Fix

Only in /home/deploy/wp-safety.org/data/plugin-versions/pie-register/3.8.4.9/assets/css/images: ui-bg_gloss-wave_35_f6a828_500x100(1).png
diff -ru /home/deploy/wp-safety.org/data/plugin-versions/pie-register/3.8.4.9/classes/base.php /home/deploy/wp-safety.org/data/plugin-versions/pie-register/3.8.4.10/classes/base.php
--- /home/deploy/wp-safety.org/data/plugin-versions/pie-register/3.8.4.9/classes/base.php	2026-03-30 12:55:36.000000000 +0000
+++ /home/deploy/wp-safety.org/data/plugin-versions/pie-register/3.8.4.10/classes/base.php	2026-05-14 07:33:50.000000000 +0000
@@ -1718,7 +1718,7 @@
 			$pending_payment_url = "";
 			$register_type = get_user_meta($user->ID, 'register_type', true);
 			if($register_type == "payment_verify"){
-				$hash = md5( time() );
+				$hash = wp_generate_password(32, false);
 				update_user_meta( $user->ID, 'hash', $hash );
 
 				$user_registered_form_id = get_user_meta($user->ID, "user_registered_form_id" , true);
@@ -3912,7 +3912,7 @@
 			$piereg = get_option(OPTION_PIE_REGISTER);
 			
 			$date_time 			= date_i18n("d-m-Y H:i:s");
-			$key 				= md5( time() );
+			$key 				= wp_generate_password(32, false);
 			$data 				= get_option("piereg_payment_log_option");
 			$duplicate_enty 	= false;
 			
diff -ru /home/deploy/wp-safety.org/data/plugin-versions/pie-register/3.8.4.9/classes/edit_form.php /home/deploy/wp-safety.org/data/plugin-versions/pie-register/3.8.4.10/classes/edit_form.php
--- /home/deploy/wp-safety.org/data/plugin-versions/pie-register/3.8.4.9/classes/edit_form.php	2026-03-30 12:55:36.000000000 +0000
+++ /home/deploy/wp-safety.org/data/plugin-versions/pie-register/3.8.4.10/classes/edit_form.php	2026-05-14 07:33:50.000000000 +0000
@@ -162,7 +162,7 @@
 						/*
 							* 	Generate Key
 						*/
-						$email_key = md5((uniqid("piereg_").time()));
+						$email_key = wp_generate_password(32, false);
 						/*
 							* 	Email Key add in array for email template
 						*/
@@ -559,6 +559,5 @@
 	require_once( get_stylesheet_directory().'/pie-register/pie_register_template/profile_edit/edit_form_template.php' );
 }
 else{
-	require_once(PIEREG_DIR_NAME.'/pie_register_template/profile_edit/edit_form_template.php');
-	
+	require_once(PIEREG_DIR_NAME.'/pie_register_template/profile_edit/edit_form_template.php');	
 }
diff -ru /home/deploy/wp-safety.org/data/plugin-versions/pie-register/3.8.4.9/pie-register.php /home/deploy/wp-safety.org/data/plugin-versions/pie-register/3.8.4.10/pie-register.php
--- /home/deploy/wp-safety.org/data/plugin-versions/pie-register/3.8.4.9/pie-register.php	2026-03-30 12:55:36.000000000 +0000
+++ /home/deploy/wp-safety.org/data/plugin-versions/pie-register/3.8.4.10/pie-register.php	2026-05-14 07:33:50.000000000 +0000
@@ -4,7 +4,7 @@
 Plugin Name: Pie Register - Basic
 Plugin URI: https://pieregister.com/
 Description: Create custom user registration forms, drag & drop form builder, send invitation codes, add conditional logic, 2-step authentication, assign user roles, accept payments and more!
-Version: 3.8.4.9
+Version: 3.8.4.10
 Author: Pie Register
 Author URI: https://pieregister.com/
 Text Domain: pie-register
@@ -2610,7 +2610,7 @@
 
 					if ($register_type == "admin_email_verify"  && !$this->InvCodeExpired($user_id)) {
 						update_user_meta($user_id, 'active', 0);
-						$hash = md5(time());
+						$hash = wp_generate_password(32, false);
 						update_user_meta($user_id, 'hash', $hash);
 						update_user_meta($user_id, 'register_type', "email_verify");
 
@@ -3969,7 +3969,7 @@
 				} else if ($option_user_verification == 1) //Admin Verification
 				{
 					update_user_meta($user_id, 'active', 0);
-					$admin_hash = md5(time());
+					$admin_hash = wp_generate_password(32, false);
 					update_user_meta($user_id, 'admin_hash', $admin_hash);
 					update_user_meta($user_id, 'register_type', "admin_verify");
 
@@ -4009,7 +4009,7 @@
 				} else if ($option_user_verification == 2) //E-Mail Link Verification
 				{
 					update_user_meta($user_id, 'active', 0);
-					$hash = md5(time());
+					$hash = wp_generate_password(32, false);
 					update_user_meta($user_id, 'hash', $hash);
 					update_user_meta($user_id, 'register_type', "email_verify");
 
@@ -4292,7 +4292,7 @@
 			$option 	= get_option(OPTION_PIE_REGISTER);
 
 			update_user_meta($user_id, 'active', 0);
-			$hash = md5(time());
+			$hash = wp_generate_password(32, false);
 			update_user_meta($user_id, 'hash', $hash);
 
 			$subject 		= html_entity_decode($option['user_subject_email_pending_payment'], ENT_COMPAT, "UTF-8");
@@ -4453,7 +4453,7 @@
 										$user_email 	= $user->user_email;
 
 										update_user_meta($user_id, 'active', 0);
-										$hash = md5(time());
+										$hash = wp_generate_password(32, false);
 										update_user_meta($user_id, 'hash', $hash);
 										update_user_meta($user_id, 'admin_hash', "");
 										update_user_meta($user_id, 'register_type', "email_verify");
@@ -4609,7 +4609,7 @@
 							}
 							$sent++;
 							update_user_meta($user_id, 'active', 0);
-							$hash = md5(time());
+							$hash = wp_generate_password(32, false);
 							update_user_meta($user_id, 'hash', $hash);
 
 
@@ -4671,7 +4671,7 @@
 							}
 							$sent++;
 							update_user_meta($user_id, 'active', 0);
-							$hash = md5(time());
+							$hash = wp_generate_password(32, false);
 							update_user_meta($user_id, 'hash', $hash);
 
 							$user 			= new WP_User($user_id);
@@ -7782,7 +7782,7 @@
 
 						if ($email_verify_orignal_key == $email_verify_key) {
 							$new_email_address = get_user_meta($user_data_temp->data->ID, "new_email_address", true);
-							$email_key = md5(uniqid("piereg_") . time());
+							$email_key = wp_generate_password(32, false);
 							$keys_array = array("reset_email_key" => $email_key);
 
 							/*
diff -ru /home/deploy/wp-safety.org/data/plugin-versions/pie-register/3.8.4.9/readme.txt /home/deploy/wp-safety.org/data/plugin-versions/pie-register/3.8.4.10/readme.txt
--- /home/deploy/wp-safety.org/data/plugin-versions/pie-register/3.8.4.9/readme.txt	2026-03-30 12:55:36.000000000 +0000
+++ /home/deploy/wp-safety.org/data/plugin-versions/pie-register/3.8.4.10/readme.txt	2026-05-14 07:33:50.000000000 +0000
@@ -5,7 +5,7 @@
 Requires at least: 4.0
 Tested up to: 6.9
 Requires PHP: 5.6
-Stable tag: 3.8.4.9
+Stable tag: 3.8.4.10
 License: GPL-2.0-or-later
 License URI: https://www.gnu.org/licenses/gpl-2.0.html
 
@@ -231,6 +231,12 @@
 
 == CHANGELOG ==
 
+### 3.8.4.10
+
+*Released Date 14th May 2026*
+
+* Security: Improved token generation in verification
+
 ### 3.8.4.9
 
 *Released Date 30th March 2026*

Exploit Outline

The exploit target is the unauthenticated AJAX handler for updating registration form configurations. 1. Locate a public page containing the [pie_register_form] shortcode to trigger the enqueueing of scripts and extraction of a valid security nonce (localized in the 'pie_pr_dec_vars_array' JavaScript object). 2. Construct a POST request to '/wp-admin/admin-ajax.php' using the 'pie_register_update_field_order' action. 3. Include a 'form_id' (e.g., 'default') and a 'field_order[]' array containing a new sequence of field IDs to reorder or corrupt the form structure. 4. Since the plugin fails to perform 'current_user_can('manage_options')' checks within the AJAX handler, the request will be processed, and the 'piereg_form_fields_{form_id}' option in the database will be updated without authentication.

Check if your site is affected.

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