CVE-2026-3328

Frontend Admin by DynamiApps <= 3.28.31 - Authenticated (Editor+) PHP Object Injection via 'post_content' of Admin Form Posts

highDeserialization of Untrusted Data
7.2
CVSS Score
7.2
CVSS Score
high
Severity
3.28.32
Patched in
1d
Time to patch

Description

The Frontend Admin by DynamiApps plugin for WordPress is vulnerable to PHP Object Injection via deserialization of the 'post_content' of admin_form posts in all versions up to, and including, 3.28.31. This is due to the use of WordPress's `maybe_unserialize()` function without class restrictions on user-controllable content stored in admin_form post content. This makes it possible for authenticated attackers, with Editor-level access and above, to inject a PHP Object. The additional presence of a POP chain allows attackers to achieve remote code execution.

CVSS Vector Breakdown

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

Technical Details

Affected versions<=3.28.31
PublishedMarch 25, 2026
Last updatedMarch 26, 2026

What Changed in the Fix

Changes introduced in v3.28.32

Loading patch diff...

Source Code

WordPress.org SVN
Research Plan
Unverified

This research plan targets a PHP Object Injection vulnerability in the **Frontend Admin by DynamiApps** plugin (<= 3.28.31), where the plugin improperly uses `maybe_unserialize()` on the `post_content` of `admin_form` posts. ### 1. Vulnerability Summary * **Vulnerability:** PHP Object Injection (…

Show full research plan

This research plan targets a PHP Object Injection vulnerability in the Frontend Admin by DynamiApps plugin (<= 3.28.31), where the plugin improperly uses maybe_unserialize() on the post_content of admin_form posts.

1. Vulnerability Summary

  • Vulnerability: PHP Object Injection (Deserialization)
  • CPT Involved: admin_form (Custom Post Type used by the plugin to store form configurations).
  • Vulnerable Sink: maybe_unserialize() applied to the post_content of an admin_form post during form processing or rendering.
  • Root Cause: The plugin stores form settings in the post_content field. When loading these settings, it retrieves the content and passes it to maybe_unserialize(). Since Editors (and above) can modify admin_form posts, they can inject a serialized PHP string that, when unserialized, instantiates arbitrary objects.
  • Severity: High (7.2). Requires Editor+ level permissions.

2. Attack Vector Analysis

  • Endpoint: The standard WordPress REST API for posts (/wp-json/wp/v2/admin_form/<id>) or the Block Renderer API.
  • Vulnerable Parameter: post_content (or content in REST API terms).
  • Authentication: Authenticated (Editor or higher).
  • Preconditions: The plugin must be active, and at least one admin_form post must exist (or the Editor must be able to create one).

3. Code Flow

  1. Storage: An Editor saves or updates an admin_form post. This can be done via the Gutenberg editor or a REST API request. The payload is stored in the wp_posts table under post_content.
  2. Retrieval: When a page containing the acf-frontend/form block (defined in assets/build/blocks/admin-form/block.json) is loaded, the server-side rendering logic is triggered.
  3. Sink Path (Inferred):
    • The acf-frontend/form block uses ServerSideRender (seen in assets/build/admin-form/index.js).
    • The backend handler for this block likely calls get_post($formID).
    • The code then performs maybe_unserialize( $post->post_content ) to parse the form configuration.
    • If the post_content starts with O:, a:, etc., maybe_unserialize will execute PHP's unserialize(), triggering any __wakeup or __destruct methods in the injected object's class (or other magic methods if a POP chain is constructed).

4. Nonce Acquisition Strategy

Since the attack requires Editor privileges, we use the standard WordPress REST API nonce.

  1. Login: Authenticate as an Editor.
  2. Navigation: Navigate to the WordPress dashboard (/wp-admin/).
  3. Extraction: The REST API nonce is typically localized in the wpApiSettings JavaScript object.
  4. Action: Use the browser_eval tool to extract the nonce:
    browser_eval("window.wpApiSettings?.nonce")
    

5. Exploitation Strategy

The goal is to update an admin_form post with a serialized payload and then trigger its deserialization.

Step 1: Identify or Create an admin_form post

  • Use the REST API to list existing forms:
    GET /wp-json/wp/v2/admin_form (Headers: X-WP-Nonce: <NONCE>)
  • If none exist, create one:
    POST /wp-json/wp/v2/admin_form
    Body: {"title": "Exploit Form", "status": "publish"}

Step 2: Inject the Serialized Payload

  • Update the post_content of the admin_form (assume ID is 123).
  • Payload Example: O:8:"WP_Query":1:{s:5:"query";s:12:"test_payload";} (This is a benign example; a real POP chain would target available classes like those in ACF or Freemius).
  • Request:
    POST /wp-json/wp/v2/admin_form/123
    Content-Type: application/json
    X-WP-Nonce: <NONCE>
    
    {
      "content": "O:8:\"WP_Query\":1:{s:5:\"query\";s:12:\"test_payload\";}"
    }
    

Step 3: Trigger Deserialization

  • Trigger the Block Renderer for the acf-frontend/form block. This forces the server to process the form settings for the specified formID.
  • Request:
    GET /wp-json/wp/v2/block-renderer/acf-frontend/form?attributes[formID]=123&attributes[editMode]=true
    X-WP-Nonce: <NONCE>
    

6. Test Data Setup

  1. User: Create a user with the editor role.
  2. Plugin: Ensure "Frontend Admin" (acf-frontend-form-element) is installed and active.
  3. Form: Use WP-CLI to quickly create an admin_form if needed:
    wp post create --post_type=admin_form --post_title="Vuln Post" --post_status=publish
    

7. Expected Results

  • Success Criteria: The server should attempt to unserialize the payload. If a valid POP chain is used, the side effects (e.g., file creation, remote request, or error logs) will be visible.
  • Response: The block-renderer request will likely return a 200 OK or a 500 Internal Server Error if the POP chain causes a crash after execution.

8. Verification Steps

  1. Verify Storage: Check the database to confirm the serialized string is in post_content:
    wp db query "SELECT post_content FROM wp_posts WHERE post_type='admin_form' AND ID=123"
    
  2. Monitor Logs: Check wp-content/debug.log for any "Class not found" errors or specific output from the POP chain.
  3. Confirm Execution: If the POP chain is designed to create a file (e.g., poc.php), verify its existence:
    ls /var/www/html/poc.php
    

9. Alternative Approaches

  • Shortcode Trigger: If the REST Block Renderer is restricted, create a page with the shortcode: [acf_frontend_form id="123"] and navigate to that page using the browser.
  • AJAX Save: If the REST API for admin_form is disabled, the plugin likely has a custom AJAX handler for saving form settings. Check for wp_ajax_save_form or similar in the plugin's main/ directory.
  • Gutenberg Context: Use browser_navigate to the admin_form edit page and use browser_eval to programmatically update the block content and click "Update".
Research Findings
Static analysis — not yet PoC-verified

Summary

The Frontend Admin by DynamiApps plugin for WordPress is vulnerable to PHP Object Injection via the 'post_content' field of 'admin_form' posts. Authenticated attackers with Editor-level access or higher can inject serialized PHP objects into a form's configuration, which are then processed by WordPress's `maybe_unserialize()` function without restrictions when the form is rendered, potentially leading to remote code execution through a POP chain.

Vulnerable Code

// The exact PHP logic for the ServerSideRender handler was not provided in the source files,
// but the vulnerability resides in the retrieval and parsing of 'admin_form' post content.
// Inferred logic based on the research plan and vulnerability description:

/* 
 * Path: [Inferred: main/plugin.php or block rendering logic]
 * Sink: maybe_unserialize() applied to user-controllable post_content
 */

// 1. Retrieve the form post
$post = get_post($formID);

// 2. Vulnerable sink: Deserializing the post content which an Editor can modify
$form_settings = maybe_unserialize( $post->post_content );

Security Fix

diff -ru /home/deploy/wp-safety.org/data/plugin-versions/acf-frontend-form-element/3.28.30/acf-frontend.php /home/deploy/wp-safety.org/data/plugin-versions/acf-frontend-form-element/3.28.32/acf-frontend.php
--- /home/deploy/wp-safety.org/data/plugin-versions/acf-frontend-form-element/3.28.30/acf-frontend.php	2026-03-01 15:32:14.000000000 +0000
+++ /home/deploy/wp-safety.org/data/plugin-versions/acf-frontend-form-element/3.28.32/acf-frontend.php	2026-03-22 07:43:14.000000000 +0000
@@ -3,7 +3,7 @@
  * Plugin Name: Frontend Admin
  * Plugin URI:  https://www.dynamiapps.com/frontend-admin/
  * Description: This awesome plugin allows you to easily display admin forms to the frontend of your site so your clients can easily edit content on their own from the frontend.
- * Version:     3.28.30
+ * Version:     3.28.32
  * Author:      Shabti Kaplan
  * Author URI:  https://www.dynamiapps.com/
  * Text Domain: frontend-admin
diff -ru /home/deploy/wp-safety.org/data/plugin-versions/acf-frontend-form-element/3.28.30/assets/build/admin-form/index.asset.php /home/deploy/wp-safety.org/data/plugin-versions/acf-frontend-form-element/3.28.32/assets/build/admin-form/index.asset.php
--- /home/deploy/wp-safety.org/data/plugin-versions/acf-frontend-form-element/3.28.30/assets/build/admin-form/index.asset.php	2025-12-24 11:42:26.000000000 +0000
+++ /home/deploy/wp-safety.org/data/plugin-versions/acf-frontend-form-element/3.28.32/assets/build/admin-form/index.asset.php	2026-03-19 19:18:36.000000000 +0000
@@ -1 +1 @@
-<?php return array('dependencies' => array('react', 'wp-block-editor', 'wp-blocks', 'wp-components', 'wp-editor', 'wp-i18n'), 'version' => '8b7ba72674c9013748dd');
+<?php return array('dependencies' => array('react', 'wp-block-editor', 'wp-blocks', 'wp-components', 'wp-editor', 'wp-i18n'), 'version' => '6c99b59cbc083ea3dc47');

Exploit Outline

1. Authentication: Log in to the WordPress site as a user with Editor privileges or higher to obtain a valid REST API nonce. 2. Payload Preparation: Create a serialized PHP object payload designed to trigger a POP chain (e.g., using classes available in the environment like Freemius or ACF). 3. Injection: Update an existing `admin_form` post (or create a new one) by sending a REST API request to `/wp-json/wp/v2/admin_form/<id>`. The payload should be placed in the `content` parameter. 4. Execution: Trigger the deserialization of the injected object by invoking the block renderer for the specific form. This can be done via a request to `/wp-json/wp/v2/block-renderer/acf-frontend/form?attributes[formID]=<id>&attributes[editMode]=true`.

Check if your site is affected.

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