CVE-2026-3243

Advanced Members for ACF <= 1.2.5 - Authenticated (Subscriber+) Arbitrary File Deletion via Path Traversal

highImproper Limitation of a Pathname to a Restricted Directory ('Path Traversal')
8.8
CVSS Score
8.8
CVSS Score
high
Severity
1.2.6
Patched in
1d
Time to patch

Description

The Advanced Members for ACF plugin for WordPress is vulnerable to arbitrary file deletion due to insufficient file path validation in the create_crop function in all versions up to, and including, 1.2.5. This makes it possible for authenticated attackers, with Subscriber-level access and above, to delete arbitrary files on the server, which can easily lead to remote code execution when the right file is deleted (such as wp-config.php). The vulnerability was partially patched in version 1.2.5.

CVSS Vector Breakdown

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

Technical Details

Affected versions<=1.2.5
PublishedApril 7, 2026
Last updatedApril 8, 2026
Affected pluginadvanced-members

What Changed in the Fix

Changes introduced in v1.2.6

Loading patch diff...

Source Code

WordPress.org SVN
Research Plan
Unverified

This vulnerability allows an authenticated user (Subscriber level and above) to delete arbitrary files on the WordPress server, including critical configuration files like `wp-config.php`, by exploiting a path traversal vulnerability in the image cropping functionality of the **Advanced Members for …

Show full research plan

This vulnerability allows an authenticated user (Subscriber level and above) to delete arbitrary files on the WordPress server, including critical configuration files like wp-config.php, by exploiting a path traversal vulnerability in the image cropping functionality of the Advanced Members for ACF plugin.

1. Vulnerability Summary

The create_crop function in core/modules/class-avatar.php (invoked via the amem_avatar_crop AJAX action) fails to properly validate or sanitize the file path provided in the data parameter. An attacker can supply a relative path with traversal sequences (e.g., ../../../wp-config.php) to the source file parameter. Because the function likely attempts to clean up original/temporary files after a crop operation using unlink() or wp_delete_file(), it will delete the file specified at the traversed path.

2. Attack Vector Analysis

  • Endpoint: /wp-admin/admin-ajax.php
  • AJAX Action: amem_ajax_avatar_crop (registered in core/modules/class-avatar.php via add_action( 'wp_ajax_amem_avatar_crop', ... ))
  • Vulnerable Parameter: data (a JSON string)
  • Vulnerable Key in JSON: file or src (inferred from standard image processing patterns in ACF extensions)
  • Authentication: Required (Subscriber level or higher)
  • Nonce: Required (amem-avatar)

3. Code Flow

  1. Entry Point: The user sends a POST request to admin-ajax.php with action=amem_avatar_crop.
  2. Nonce Check: ajax_avatar_crop() calls check_ajax_referer( 'amem-avatar' ) (Line 389 in core/modules/class-avatar.php).
  3. Data Decoding: The data POST parameter is decoded: $data = json_decode($post['data'], true); (Line 393).
  4. Vulnerable Sink: ajax_avatar_crop calls $this->create_crop($data); (Line 394).
  5. Path Traversal: Inside create_crop (inferred), the code extracts a file path from $data['file'] (or similar) and passes it to a file deletion function like unlink() without verifying that the path resides within the intended uploads directory.

4. Nonce Acquisition Strategy

The nonce amem-avatar is localized for the amem_avatar ACF field. Since this field is intended for user profile pictures, it can be accessed on any "Account" or "Registration" form generated by the plugin.

  1. Identify Form: Use wp-cli to find or create an amem-form of type account.
  2. Add Field: Ensure an amem_avatar field is attached to this form (this is the default for Advanced Members avatar functionality).
  3. Navigate: Log in as a Subscriber and navigate to the page containing the Account form.
  4. Extract: Use browser_eval to extract the nonce from the localized JavaScript object.
    • Variable Name: amem_avatar_args or acf.l10n.amem_avatar.
    • Specific Key: nonce.
    • Command: browser_eval("amem_avatar_args.nonce") or search the page source for amem-avatar.

5. Exploitation Strategy

The goal is to delete wp-config.php.

HTTP Request (via http_request tool):

POST /wp-admin/admin-ajax.php HTTP/1.1
Host: [TARGET_HOST]
Content-Type: application/x-www-form-urlencoded
Cookie: [SUBSCRIBER_COOKIES]

action=amem_avatar_crop&_ajax_nonce=[EXTRACTED_NONCE]&data={"file":"../../../../wp-config.php"}

Note: If file does not work, alternative keys to try in the JSON object are src, path, or original.

6. Test Data Setup

  1. Install/Activate: Ensure advanced-custom-fields and advanced-members (v1.2.5) are active.
  2. Enable Avatars: Ensure the "Use Avatar" option is enabled in the plugin settings.
  3. Create User: Create a user with the subscriber role.
  4. Create Form:
    # Create an Account form post
    wp post create --post_type=amem-form --post_title="My Account" --post_status=publish
    # The form type 'account' is usually set via post meta
    wp post meta set [POST_ID] select_type account
    
  5. Placement: Place the form shortcode on a public page: [amem_form id="[POST_ID]"].
  6. Verify Target: Ensure wp-config.php exists in the WordPress root.

7. Expected Results

  • The server returns a successful AJAX response (likely 1 or a JSON success message).
  • The file wp-config.php is deleted from the filesystem.
  • Subsequent requests to the WordPress site redirect to wp-admin/setup-config.php because the configuration is missing.

8. Verification Steps

  1. Direct File Check:
    ls /var/www/html/wp-config.php
    
    Result: "No such file or directory"
  2. Site Status Check:
    wp core is-installed
    
    Result: Failure/Error or indication that the database is not connected.

9. Alternative Approaches

If ../../../../wp-config.php fails, the plugin might be prepending a specific directory like the "avatar" or "temp" directory.

  • Temp Dir Path: amem()->files->temp_upload_dir() often points to wp-content/uploads/amem-temp/.
  • Avatar Dir Path: wp-content/uploads/avatar/.
  • Payload Adjustment: Try increasing depth: ../../../../../wp-config.php.
  • Key Discovery: If the payload returns an error, use browser_eval to inspect the network traffic of a legitimate avatar crop to see the exact structure of the data JSON object sent by the plugin's JavaScript.
Research Findings
Static analysis — not yet PoC-verified

Summary

The Advanced Members for ACF plugin for WordPress is vulnerable to arbitrary file deletion by authenticated attackers with Subscriber-level access and above. This occurs due to insufficient validation of file paths in the `create_crop` function, allowing attackers to use path traversal to delete critical files on the server, such as wp-config.php.

Vulnerable Code

// core/modules/class-avatar.php, line 849
public function ajax_avatar_crop() {
    check_ajax_referer( 'amem-avatar' );

    // WTF WordPress
    $post = array_map('stripslashes_deep', $_POST);

    $data = json_decode($post['data'], true);

    $attachment = $this->create_crop($data);

    wp_send_json_success($attachment);
}

---

// core/modules/class-avatar.php, line 620
public function create_crop($image_data) {

    $user_id = $image_data['uid'] === 'default' ? $image_data['uid'] : (int) $image_data['uid'];

    // (truncated: No ownership validation for user_id or file path checks performed in 1.2.5 before file operations) ...

Security Fix

diff -ru /home/deploy/wp-safety.org/data/plugin-versions/advanced-members/1.2.5/admin/class-admin-form.php /home/deploy/wp-safety.org/data/plugin-versions/advanced-members/1.2.6/admin/class-admin-form.php
--- /home/deploy/wp-safety.org/data/plugin-versions/advanced-members/1.2.5/admin/class-admin-form.php	2026-03-11 06:03:40.000000000 +0000
+++ /home/deploy/wp-safety.org/data/plugin-versions/advanced-members/1.2.6/admin/class-admin-form.php	2026-03-27 06:58:20.000000000 +0000
@@ -396,6 +396,7 @@
 					'choices'         		=> array(
 						'approve'						=> __( 'Auto Approve', 'advanced-members' ),
 						'mailcheck' 				=> __( 'Requires Email Activation', 'advanced-members' ),
+						'awaiting_admin_review'	=> __( 'Admin Approval', 'advanced-members' ),
 					),
 					'conditions'   				=> array(
 						'field'    => 'select_type',
diff -ru /home/deploy/wp-safety.org/data/plugin-versions/advanced-members/1.2.5/advanced-members.php /home/deploy/wp-safety.org/data/plugin-versions/advanced-members/1.2.6/advanced-members.php
--- /home/deploy/wp-safety.org/data/plugin-versions/advanced-members/1.2.5/advanced-members.php	2026-03-11 06:03:40.000000000 +0000
+++ /home/deploy/wp-safety.org/data/plugin-versions/advanced-members/1.2.6/advanced-members.php	2026-03-27 06:58:20.000000000 +0000
@@ -3,7 +3,7 @@
  * Plugin Name: Advanced Members for ACF
  * Plugin URI: https://danbilabs.com/
  * Description: Lightweight & All-in-One Membership Plugin for ACF Fans.
- * Version: 1.2.5
+ * Version: 1.2.6
  * Author: danbilabs
  * Author URI: https://danbilabs.com/
  * Text Domain: advanced-members
@@ -44,7 +44,7 @@
 	public static $name = 'advanced-members';
 
 	/** @var string version */
-	public static $version = '1.2.5';
+	public static $version = '1.2.6';
 
 	/** @var string version */
 	protected static $acf_required = '6.2.0';
diff -ru /home/deploy/wp-safety.org/data/plugin-versions/advanced-members/1.2.5/core/modules/class-avatar.php /home/deploy/wp-safety.org/data/plugin-versions/advanced-members/1.2.6/core/modules/class-avatar.php
--- /home/deploy/wp-safety.org/data/plugin-versions/advanced-members/1.2.5/core/modules/class-avatar.php	2026-03-11 06:03:40.000000000 +0000
+++ /home/deploy/wp-safety.org/data/plugin-versions/advanced-members/1.2.6/core/modules/class-avatar.php	2026-03-27 06:58:20.000000000 +0000
@@ -627,6 +618,25 @@
 
 		$user_id = $image_data['uid'] === 'default' ? $image_data['uid'] : (int) $image_data['uid'];
 
+		// Validate uid ownership
+		$current_user_id = get_current_user_id();
+		if ( $user_id === 'default' ) {
+			// Default avatar: admin only
+			if ( !current_user_can('manage_options') ) {
+				wp_send_json_error( __('Permission denied.', 'advanced-members'), 403 );
+			}
+		} elseif ( $current_user_id ) {
+			// Logged-in user: must match own ID or be admin
+			if ( $user_id !== $current_user_id && !current_user_can('manage_options') ) {
+				wp_send_json_error( __('Permission denied.', 'advanced-members'), 403 );
+			}
+		} else {
+			// Non-logged-in: uid must be a UUID (temp_user_id), not a numeric user ID
+			if ( is_numeric($image_data['uid']) ) {
+				wp_send_json_error( __('Permission denied.', 'advanced-members'), 403 );
+			}
+		}
+
 		// If the difference between the images is less than half a percentage, use the original image
 		// prettier-ignore
 		// if ( 
@@ -763,7 +773,7 @@
 		$attachment = [
 			'id' => filemtime( $new_file ),
 			'filename' => wp_basename( $new_file ),
-			'file' => $new_file,
+			'file' => $new_rel_path,
 			'url' => $url,
 			'preview_url' => $url,
 			'alt' => '',

Exploit Outline

1. Authenticate to the WordPress site as a user with at least Subscriber-level privileges. 2. Access a page containing an account or registration form generated by the plugin to retrieve a valid nonce for the 'amem-avatar' action (often found in localized JavaScript variables like `amem_avatar_args`). 3. Construct a POST request to `/wp-admin/admin-ajax.php` with the parameter `action=amem_avatar_crop` and a valid `_ajax_nonce` value. 4. In the `data` parameter, provide a JSON-encoded object containing a path traversal payload in a file-related key, such as `{"file":"../../../../wp-config.php"}` or `{"src":"../../../../wp-config.php"}`. 5. Upon execution, the plugin's server-side logic will process the path and attempt to delete the specified file using functions like `unlink()` or `wp_delete_file()`, resulting in the deletion of `wp-config.php`.

Check if your site is affected.

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