CVE-2026-12428

Blocks for ACF Fields <= 1.6.2 - Missing Authorization to Authenticated (Author+) Arbitrary ACF Field Value Disclosure via 'id' Parameter

mediumMissing Authorization
6.5
CVSS Score
6.5
CVSS Score
medium
Severity
1.6.3
Patched in
1d
Time to patch

Description

The Blocks for ACF Fields plugin for WordPress is vulnerable to unauthorized access of data due to a missing capability check on the get_all_values() function in the /wp-json/acf-field-blocks/v1/values REST endpoint in versions up to, and including, 1.6.2. The permission_callback only verifies the generic publish_posts capability and the handler passes a user-supplied id parameter directly to get_field_objects() without verifying that the requesting user is authorized to read the target object. This makes it possible for authenticated attackers, with Author-level access and above, to read ACF field values from arbitrary posts (including private posts, drafts, posts by other users, and other ACF-supported objects) that they should not have access to.

CVSS Vector Breakdown

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

Technical Details

Affected versions<=1.6.2
PublishedJuly 8, 2026
Last updatedJuly 9, 2026
Affected pluginacf-field-blocks

What Changed in the Fix

Changes introduced in v1.6.3

Loading patch diff...

Source Code

WordPress.org SVN
Research Plan
Unverified

# Exploitation Research Plan: CVE-2026-12428 This plan outlines the systematic approach to exploiting a Missing Authorization vulnerability in the **Blocks for ACF Fields** plugin (<= 1.6.2), which allows authenticated users (Author level and above) to disclose arbitrary Advanced Custom Fields (ACF…

Show full research plan

Exploitation Research Plan: CVE-2026-12428

This plan outlines the systematic approach to exploiting a Missing Authorization vulnerability in the Blocks for ACF Fields plugin (<= 1.6.2), which allows authenticated users (Author level and above) to disclose arbitrary Advanced Custom Fields (ACF) values from any post or object ID.

1. Vulnerability Summary

  • Vulnerability: Missing Authorization
  • Location: inc/class-rest.php (specifically the get_all_values() callback for the /wp-json/acf-field-blocks/v1/values REST endpoint).
  • Cause: The permission_callback for the /values endpoint only verifies the publish_posts capability. The implementation of get_all_values() (as described in the vulnerability report and supported by the Rest::register_routes structure) accepts a user-controlled id parameter and passes it to ACF functions (like get_field_objects()) without verifying if the current user has permission to read the specific post associated with that id.

2. Attack Vector Analysis

  • REST Endpoint: /wp-json/acf-field-blocks/v1/values
  • HTTP Method: GET (READABLE)
  • Vulnerable Parameter: id (Target Post/Object ID)
  • Authentication Required: Authenticated (Author role or higher). The publish_posts capability is granted to Authors, Editors, and Administrators by default.
  • Preconditions:
    • The target WordPress site must have Advanced Custom Fields (ACF) or Secure Custom Fields (SCF) installed and active.
    • Sensitive data must be stored in ACF fields attached to a post that the Author normally cannot access (e.g., a Private Post, a Draft, or a post belonging to another user).

3. Code Flow

  1. Registration: ACFFieldBlocks\Rest::register_routes() registers the endpoint:
    register_rest_route(
        'acf-field-blocks/v1',
        '/values',
        array(
            'methods'             => \WP_REST_Server::READABLE,
            'callback'            => array( $this, 'get_all_values' ),
            'permission_callback' => function() {
                return current_user_can('publish_posts');
            }
        )
    );
    
  2. Access: An Author-level user accesses the endpoint via a GET request with an id parameter.
  3. Execution (Inferred from Patch): The get_all_values($request) method retrieves the id via $request->get_param('id').
  4. Sink: It calls get_field_objects($id) (an ACF function). This function retrieves all fields for the given ID. Because the plugin does not check if the user can edit_post or read_post for that specific $id, the data is returned in the REST response.

4. Nonce Acquisition Strategy

To interact with the WordPress REST API using cookie-based authentication, a _wpnonce or X-WP-Nonce header is required. This nonce is tied to the wp_rest action.

  1. Shortcode Identification: The plugin uses the [acf_field_block] or "ACF Field" block.
  2. Setup: Create a test page containing the block.
  3. Browser Execution:
    • Log in to the WordPress dashboard as the Author user.
    • Navigate to wp-admin/index.php.
    • Use browser_eval to extract the REST nonce usually available in the wpApiSettings object or via a specific call.
    • JS Script: window.wpApiSettings ? window.wpApiSettings.nonce : "".
    • Alternatively, fetch wp-admin/admin-ajax.php?action=rest-nonce to get a fresh nonce for the wp_rest action.

5. Exploitation Strategy

The goal is to use an Author account to read ACF field values from a Private Post created by an Administrator.

Step-by-Step Plan:

  1. Target Identification: Determine the ID of a Private Post containing sensitive ACF data (e.g., Post ID 123).
  2. Login: Authenticate as the Author user.
  3. Nonce Retrieval: Obtain the wp_rest nonce.
  4. Data Extraction: Send a GET request to the vulnerable endpoint.

HTTP Request (via http_request tool):

  • Method: GET
  • URL: http://<TARGET_URL>/wp-json/acf-field-blocks/v1/values?id=123
  • Headers:
    • X-WP-Nonce: <NONCE_VALUE>
    • Content-Type: application/json
  • Expected Response: A JSON object containing the label, name, and value of all ACF fields associated with Post 123.

6. Test Data Setup

  1. Admin User Actions:
    • Create a new ACF Field Group (e.g., "Internal Secrets").
    • Add a Text field named api_key_secret.
    • Set the location to "Post Type is equal to Post".
    • Create a Private Post (ID $TARGET_ID).
    • Populate the api_key_secret field with a value: SUPER_SECRET_TOKEN_2026.
  2. Attacker User Action:
    • Create a user with the Author role.

7. Expected Results

  • The request should return a 200 OK status code.
  • The JSON body should contain the data from the Private Post:
    {
      "api_key_secret": {
        "key": "field_xxxxxxxx",
        "label": "API Key Secret",
        "name": "api_key_secret",
        "type": "text",
        "value": "SUPER_SECRET_TOKEN_2026",
        ...
      }
    }
    
  • If the vulnerability is patched, the endpoint should return a 403 Forbidden or restricted data.

8. Verification Steps

  1. Manual Check: Verify the returned value matches the one set by the Admin in the Private Post.
  2. WP-CLI Comparison:
    # Confirm the post is indeed private and not accessible by Author normally
    wp post get <TARGET_ID> --user=author_user_login
    # This should fail or return "Invalid post ID" if standard permissions are working.
    

9. Alternative Approaches

  • Brute Forcing IDs: If the specific target ID is unknown, the agent can iterate through common IDs (1 to 100) to find hidden ACF metadata.
  • Other Object Types: Test id values for other ACF-supported objects, such as user_1 (to read metadata of the Admin user) or term_5 (taxonomy metadata), as get_field_objects() accepts these string-based ID formats.
Research Findings
Static analysis — not yet PoC-verified

Summary

The Blocks for ACF Fields plugin for WordPress is vulnerable to unauthorized disclosure of data via its REST API in versions up to 1.6.2. Due to a missing object-level capability check in the get_all_values() function, authenticated users with Author-level access or higher can retrieve sensitive Advanced Custom Fields (ACF) values for any post, user, or object ID, including those they lack permissions to view.

Vulnerable Code

// inc/class-rest.php lines 88-100
		register_rest_route(
			$namespace,
			'/values',
			array(
				'methods'             => \WP_REST_Server::READABLE,
				'callback'            => array( $this, 'get_all_values' ),
				'args'                => array(),
				'permission_callback' => function() {
					return current_user_can('publish_posts');
				}
			)
		);

---

// inc/class-rest.php lines 291-314 (implementation inferred from patch diff context)
	public function get_all_values( \WP_REST_Request $request ) {
		$post_id = $request->get_param( 'id' );

		if ( empty( $post_id ) ) {
			return [];
		}

		$fields = get_field_objects( $post_id, false, true, false );
		$values = array();

		if ( ! empty( $fields ) ) {
			foreach ( $fields as $field ) {
				$values[ $field['name'] ] = $field;
			}
		}

		return apply_filters( 'acf_field_blocks_rest_values', $values );
	}

Security Fix

--- /home/deploy/wp-safety.org/data/plugin-versions/acf-field-blocks/1.6.1/inc/class-rest.php	2026-01-02 02:36:54.000000000 +0000
+++ /home/deploy/wp-safety.org/data/plugin-versions/acf-field-blocks/1.6.3/inc/class-rest.php	2026-06-27 06:48:40.000000000 +0000
@@ -299,6 +299,15 @@
 			return [];
 		}
 
+		// The id can point at any object, so check object-level access before reading.
+		if ( ! $this->current_user_can_read_object( $post_id ) ) {
+			return new \WP_Error(
+				'rest_forbidden',
+				__( 'You are not allowed to read the field values of this object.', 'acf-field-blocks' ),
+				array( 'status' => rest_authorization_required_code() )
+			);
+		}
+
 		$fields = get_field_objects( $post_id, false, true, false );
 		$values = array();
 
@@ -314,4 +323,57 @@
 		return apply_filters( 'acf_field_blocks_rest_values', $values );
 	}
 
+	/**
+	 * Check whether the current user may read the ACF values of the given object id.
+	 *
+	 * @since  1.6.3
+	 *
+	 * @param  int|string $post_id ACF object id.
+	 * @return bool                True if the current user may read the object.
+	 */
+	private function current_user_can_read_object( $post_id ) {
+		// Decode the id the same way ACF does, so the check matches what gets read.
+		if ( function_exists( 'acf_decode_post_id' ) ) {
+			$decoded = acf_decode_post_id( $post_id );
+			$type    = $decoded['type'];
+			$id      = $decoded['id'];
+		} elseif ( is_numeric( $post_id ) ) {
+			$type = 'post';
+			$id   = $post_id;
+		} else {
+			return false;
+		}
+
+		switch ( $type ) {
+			case 'post':
+				$id = (int) $id;
+				return $id && current_user_can( 'read_post', $id );
+
+			case 'user':
+				// Allow your own profile; otherwise require edit access to the user.
+				$id = (int) $id;
+				return $id && ( get_current_user_id() === $id || current_user_can( 'edit_user', $id ) );
+
+			case 'comment':
+				$id      = (int) $id;
+				$comment = $id ? get_comment( $id ) : null;
+				return $comment && ( current_user_can( 'moderate_comments' )
+					|| current_user_can( 'read_post', (int) $comment->comment_post_ID ) );
+
+			case 'term':
+				// Terms are public taxonomy data; just confirm it exists.
+				$id = (int) $id;
+				return $id > 0 && term_exists( $id ) !== null;
+
+			case 'option':
+			case 'block':
+				// Shared store, not a per-object resource. Already gated by publish_posts.
+				return true;
+
+			default:
+				// Unknown/sensitive types (blog, woo_order, ...).
+				return current_user_can( 'manage_options' );
+		}
+	}
+

Exploit Outline

The exploit requires an authenticated user account with the 'publish_posts' capability (typically Author, Editor, or Administrator roles). The attacker first obtains a valid WordPress REST API nonce. They then send a GET request to the `/wp-json/acf-field-blocks/v1/values` endpoint, supplying an arbitrary target ID in the 'id' parameter (e.g., a post ID for a private draft or a user ID such as 'user_1'). Because the plugin lacks object-level permission verification for the provided ID, it calls the ACF get_field_objects() function on the target and returns a JSON response containing all associated custom field metadata and sensitive values, even if the attacker should not have access to that specific object.

Check if your site is affected.

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