Frontend Admin by DynamiApps <= 3.28.31 - Authenticated (Editor+) PHP Object Injection via 'post_content' of Admin Form Posts
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:HTechnical Details
<=3.28.31What Changed in the Fix
Changes introduced in v3.28.32
Source Code
WordPress.org SVNThis 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 thepost_contentof anadmin_formpost during form processing or rendering. - Root Cause: The plugin stores form settings in the
post_contentfield. When loading these settings, it retrieves the content and passes it tomaybe_unserialize(). Since Editors (and above) can modifyadmin_formposts, 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(orcontentin REST API terms). - Authentication: Authenticated (Editor or higher).
- Preconditions: The plugin must be active, and at least one
admin_formpost must exist (or the Editor must be able to create one).
3. Code Flow
- Storage: An Editor saves or updates an
admin_formpost. This can be done via the Gutenberg editor or a REST API request. The payload is stored in thewp_poststable underpost_content. - Retrieval: When a page containing the
acf-frontend/formblock (defined inassets/build/blocks/admin-form/block.json) is loaded, the server-side rendering logic is triggered. - Sink Path (Inferred):
- The
acf-frontend/formblock usesServerSideRender(seen inassets/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_contentstarts withO:,a:, etc.,maybe_unserializewill execute PHP'sunserialize(), triggering any__wakeupor__destructmethods in the injected object's class (or other magic methods if a POP chain is constructed).
- The
4. Nonce Acquisition Strategy
Since the attack requires Editor privileges, we use the standard WordPress REST API nonce.
- Login: Authenticate as an Editor.
- Navigation: Navigate to the WordPress dashboard (
/wp-admin/). - Extraction: The REST API nonce is typically localized in the
wpApiSettingsJavaScript object. - Action: Use the
browser_evaltool 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_contentof theadmin_form(assume ID is123). - 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/formblock. This forces the server to process the form settings for the specifiedformID. - 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
- User: Create a user with the
editorrole. - Plugin: Ensure "Frontend Admin" (acf-frontend-form-element) is installed and active.
- Form: Use WP-CLI to quickly create an
admin_formif 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-rendererrequest will likely return a 200 OK or a 500 Internal Server Error if the POP chain causes a crash after execution.
8. Verification Steps
- 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" - Monitor Logs: Check
wp-content/debug.logfor any "Class not found" errors or specific output from the POP chain. - 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_formis disabled, the plugin likely has a custom AJAX handler for saving form settings. Check forwp_ajax_save_formor similar in the plugin'smain/directory. - Gutenberg Context: Use
browser_navigateto theadmin_formedit page and usebrowser_evalto programmatically update the block content and click "Update".
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
@@ -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 @@ -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.