Advanced Forms for ACF <= 1.9.3.7 - Missing Authorization
Description
The Advanced Forms for ACF plugin for WordPress is vulnerable to unauthorized access due to a missing capability check on a function in versions up to, and including, 1.9.3.7. 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:NTechnical Details
<=1.9.3.7What Changed in the Fix
Changes introduced in v1.9.3.8
Source Code
WordPress.org SVNI cannot fulfill your request to create a detailed exploitation plan for this vulnerability. My safety guidelines prohibit the generation of actionable instructions, payloads, or specific strategies for exploiting software vulnerabilities. Instead, I can provide a conceptual overview of the securit…
Show full research plan
I cannot fulfill your request to create a detailed exploitation plan for this vulnerability. My safety guidelines prohibit the generation of actionable instructions, payloads, or specific strategies for exploiting software vulnerabilities.
Instead, I can provide a conceptual overview of the security principles involved and how developers can prevent similar issues.
Security Principles: Authorization and Integrity in WordPress
The vulnerability described (CVE-2026-57378) centers on Missing Authorization and Missing CSRF Protection (Nonces) in a function hooked to a global initialization action.
1. The Risk of admin_init
In WordPress, the admin_init hook runs whenever an administrative page is accessed. This includes common entry points used for processing data like admin-ajax.php and admin-post.php. Crucially, admin_init executes even before WordPress performs its own internal capability checks for specific pages.
If a plugin hooks a data-processing function (like an importer) to admin_init without implementing its own security checks, any user (and in some configurations, even unauthenticated visitors) can trigger that function by sending a request to an administrative endpoint.
2. Capability Checks (Authorization)
To ensure that only authorized users can perform sensitive actions (like importing configurations that could overwrite site data), developers must use current_user_can(). For administrative tasks, a capability like manage_options or a custom capability specific to the plugin is typically used.
Secure Pattern:
function my_sensitive_action() {
if ( ! current_user_can( 'manage_options' ) ) {
wp_die( __( 'You do not have sufficient permissions to access this page.' ) );
}
// Proceed with action...
}
3. Nonce Verification (CSRF Protection)
A "Nonce" (Number used ONCE) in WordPress is a unique token used to protect against Cross-Site Request Forgery (CSRF). It ensures that the request was intentionally sent by the user from a legitimate form within the WordPress dashboard, rather than forged by a third-party site.
Secure Pattern:
// In the form:
wp_nonce_field( 'my_action_string', 'my_nonce_name' );
// In the processing function:
if ( ! isset( $_POST['my_nonce_name'] ) || ! wp_verify_nonce( $_POST['my_nonce_name'], 'my_action_string' ) ) {
wp_die( 'Security check failed' );
}
Remediation for CVE-2026-57378
The patch for this vulnerability (version 1.9.3.8) likely involves adding a capability check (e.g., checking for edit_pages or manage_options) and a nonce verification inside the import_json_file() function to ensure that only authorized administrators can upload and process JSON form files.
For further information on securing WordPress plugins, I recommend consulting the WordPress Plugin Handbook's Security section and the OWASP Top Ten project.
Summary
The Advanced Forms for ACF plugin is vulnerable to unauthorized data modification due to a missing capability check and nonce verification on its form import functionality. An unauthenticated attacker can overwrite existing forms or inject stored cross-site scripting (XSS) payloads by sending a crafted multipart POST request to administrative endpoints like admin-post.php.
Vulnerable Code
// admin/forms/forms-import.php class AF_Admin_Forms_Import { function __construct() { add_action( 'admin_menu', array( $this, 'register_admin_page' ), 5, 0 ); add_action( 'admin_init', array( $this, 'import_json_file' ), 10, 0 ); add_filter( 'admin_title', array( $this, 'fix_admin_title' ), 10, 2 ); } // ... lines 67-85 ... function import_json_file() { if ( ! isset( $_POST['af_import_json'] ) ) { return; } if ( empty( $_FILES['af_import_json_file']['size'] ) ) { return acf_add_admin_notice( __( 'No files selected', 'advanced-forms' ) ); } $file = $_FILES['af_import_json_file']; $json = file_get_contents( $file['tmp_name'] ); $json = json_decode( $json, true ); $post = af_import_form( $json ); if ( $post ) { // ...
Security Fix
@@ -36,6 +36,7 @@ <h2 class="hndle"><span>Import JSON file</span></h2> <div class="inside"> <form method="post" enctype="multipart/form-data"> + <?php wp_nonce_field( 'af_import_form', 'af_import_nonce' ); ?> <p> <?php _e( 'Select the form JSON file you would like to import. If a form with the same key already exists it will be overwritten.', 'advanced-forms' ); ?> </p> @@ -67,6 +68,26 @@ return; } + // Only users who can manage forms may import. This also guards against + // unauthenticated requests, since admin_init fires on admin-post.php. + if ( ! current_user_can( 'edit_pages' ) ) { + wp_die( + __( 'You do not have sufficient permissions to import forms.', 'advanced-forms' ), + __( 'Error: Insufficient permissions', 'advanced-forms' ), + array( 'back_link' => true ) + ); + } + + // Verify the nonce rendered on the import page. + $nonce = isset( $_POST['af_import_nonce'] ) ? sanitize_text_field( wp_unslash( $_POST['af_import_nonce'] ) ) : ''; + if ( ! wp_verify_nonce( $nonce, 'af_import_form' ) ) { + wp_die( + __( 'Form could not be imported due to a missing or invalid nonce.', 'advanced-forms' ), + __( 'Error: Invalid nonce', 'advanced-forms' ), + array( 'back_link' => true ) + ); + } + if ( empty( $_FILES['af_import_json_file']['size'] ) ) { return acf_add_admin_notice( __( 'No files selected', 'advanced-forms' ) ); } @@ -75,6 +96,11 @@ $json = file_get_contents( $file['tmp_name'] ); $json = json_decode( $json, true ); + // Bail if the uploaded file isn't a JSON object describing a form. + if ( ! is_array( $json ) || empty( $json['key'] ) ) { + return acf_add_admin_notice( __( 'The selected file is not a valid form export.', 'advanced-forms' ) ); + } + $post = af_import_form( $json ); if ( $post ) { /* translators: The ACF field key */
Exploit Outline
The exploit takes advantage of the `admin_init` hook, which runs before capability checks on administrative entry points like `/wp-admin/admin-post.php`. 1. The attacker prepares a malicious JSON file containing a valid form structure (including a 'key' property). To achieve Stored XSS, the attacker includes malicious JavaScript in the 'display' -> 'description' field of the JSON. 2. The attacker sends a multipart/form-data POST request to `http://victim-site.com/wp-admin/admin-post.php` (or any admin URL). 3. The request must include the POST parameter `af_import_json` to trigger the vulnerable function. 4. The malicious JSON file is uploaded in the `af_import_json_file` parameter. 5. Because the vulnerable version lacks `current_user_can` and `wp_verify_nonce` checks within `import_json_file`, the plugin processes the upload. 6. The `af_import_form()` function (called on the uploaded data) will overwrite any existing form matching the provided 'key', effectively allowing an unauthenticated user to modify site configurations and execute XSS payloads whenever the form is viewed in the admin dashboard or on the frontend (prior to 1.9.3.8's sanitization fix).
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.